From f808734af7b6ac3bfb4243cbe39ce9988aa65c21 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 1 May 2026 12:33:59 +0100 Subject: [PATCH 1/9] [GrpcNetClient] Update Semantic Conventions - Update to v1.14.0 of the gRPC Semantic Conventions. - Improve gRPC test server fixture. - Remove obsolete code. - Unskip tests that pass. Resolves #4335. Contributes to #4064. --- .../CHANGELOG.md | 6 ++ .../GrpcClientDiagnosticListener.cs | 99 ++++++++++--------- ...metry.Instrumentation.GrpcNetClient.csproj | 1 + .../README.md | 6 +- .../TracerProviderBuilderExtensions.cs | 2 +- src/Shared/SemanticConventions.cs | 1 + .../GrpcServer.cs | 58 +++++++---- .../GrpcTests.client.cs | 79 +++++++-------- .../GrpcTests.server.cs | 37 ++++--- ...Instrumentation.GrpcNetClient.Tests.csproj | 1 + test/Shared/TcpPortProvider.cs | 38 +++++++ 11 files changed, 198 insertions(+), 130 deletions(-) create mode 100644 test/Shared/TcpPortProvider.cs diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md index 954c3bb916..90dafbdf57 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* **BREAKING**: Update to version 1.41.0 of the Semantic Conventions. + ([#TODO](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/TODO)) + +* Add instrumentation scope version and schema URL to traces. + ([#TODO](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/TODO)) + ## 1.15.1-beta.1 Released 2026-Apr-21 diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs index 389b5c7ecc..06506717a0 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs @@ -3,22 +3,15 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using OpenTelemetry.Context.Propagation; -using OpenTelemetry.Internal; using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.GrpcNetClient.Implementation; internal sealed class GrpcClientDiagnosticListener : ListenerHandler { - internal static readonly Assembly Assembly = typeof(GrpcClientDiagnosticListener).Assembly; - internal static readonly AssemblyName AssemblyName = Assembly.GetName(); -#pragma warning disable IDE0370 // Suppression is unnecessary - internal static readonly string ActivitySourceName = AssemblyName.Name!; -#pragma warning restore IDE0370 // Suppression is unnecessary - internal static readonly string Version = Assembly.GetPackageVersion(); - internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Version); + internal static readonly Version SemanticConventionsVersion = new(1, 41, 0); + internal static readonly ActivitySource ActivitySource = ActivitySourceFactory.Create(SemanticConventionsVersion); private const string OnStartEvent = "Grpc.Net.Client.GrpcOut.Start"; private const string OnStopEvent = "Grpc.Net.Client.GrpcOut.Stop"; @@ -36,21 +29,21 @@ public GrpcClientDiagnosticListener(GrpcClientTraceInstrumentationOptions option public override void OnEventWritten(string name, object? payload) { - var activity = Activity.Current!; + if (Activity.Current is not { } activity) + { + return; + } + switch (name) { case OnStartEvent: - { - this.OnStartActivity(activity, payload); - } - + this.OnStartActivity(activity, payload); break; - case OnStopEvent: - { - this.OnStopActivity(activity, payload); - } + case OnStopEvent: + this.OnStopActivity(activity, payload); break; + default: break; } @@ -130,33 +123,34 @@ public void OnStartActivity(Activity activity, object? payload) } } - activity.SetTag(SemanticConventions.AttributeRpcSystem, GrpcTagHelper.RpcSystemGrpc); + activity.SetTag(SemanticConventions.AttributeRpcSystemName, GrpcTagHelper.RpcSystemGrpc); var requestUri = request.RequestUri; if (requestUri != null) { + activity.SetTag(SemanticConventions.AttributeServerAddress, requestUri.Host); + activity.SetTag(SemanticConventions.AttributeServerPort, requestUri.Port); + var uriHostNameType = Uri.CheckHostName(requestUri.Host); if (uriHostNameType is UriHostNameType.IPv4 or UriHostNameType.IPv6) { - activity.SetTag(SemanticConventions.AttributeServerSocketAddress, requestUri.Host); + activity.SetTag(SemanticConventions.AttributeNetworkPeerAddress, requestUri.Host); + activity.SetTag(SemanticConventions.AttributeNetworkPeerPort, requestUri.Port); } - else - { - activity.SetTag(SemanticConventions.AttributeServerAddress, requestUri.Host); - } - - activity.SetTag(SemanticConventions.AttributeServerPort, requestUri.Port); } - try - { - this.options.EnrichWithHttpRequestMessage?.Invoke(activity, request); - } - catch (Exception ex) + if (this.options.EnrichWithHttpRequestMessage is { } enrich) { - GrpcInstrumentationEventSource.Log.EnrichmentException(ex); + try + { + enrich(activity, request); + } + catch (Exception ex) + { + GrpcInstrumentationEventSource.Log.EnrichmentException(ex); + } } } @@ -173,28 +167,43 @@ static bool TryFetchRequest(object? payload, [NotNullWhen(true)] out HttpRequest public void OnStopActivity(Activity activity, object? payload) { - if (activity.IsAllDataRequested) + if (!activity.IsAllDataRequested) + { + return; + } + + var validConversion = GrpcTagHelper.TryGetGrpcStatusCodeFromActivity(activity, out var status); + if (validConversion) + { + if (activity.Status == ActivityStatusCode.Unset) + { + activity.SetStatus(GrpcTagHelper.ResolveSpanStatusForGrpcStatusCodeOnClient(status)); + } + + activity.SetTag(SemanticConventions.AttributeRpcResponseStatusCode, status); + } + + // Remove the grpc.status_code tag added by the gRPC .NET library + activity.SetTag(GrpcTagHelper.GrpcStatusCodeTagName, null); + + if (TryFetchResponse(payload, out var response)) { - var validConversion = GrpcTagHelper.TryGetGrpcStatusCodeFromActivity(activity, out var status); - if (validConversion) + if (response.RequestMessage?.RequestUri is { } requestUri) { - if (activity.Status == ActivityStatusCode.Unset) + var uriHostNameType = Uri.CheckHostName(requestUri.Host); + + if (uriHostNameType is UriHostNameType.IPv4 or UriHostNameType.IPv6) { - activity.SetStatus(GrpcTagHelper.ResolveSpanStatusForGrpcStatusCodeOnClient(status)); + activity.SetTag(SemanticConventions.AttributeNetworkPeerAddress, requestUri.Host); + activity.SetTag(SemanticConventions.AttributeNetworkPeerPort, requestUri.Port); } - - // setting rpc.grpc.status_code - activity.SetTag(SemanticConventions.AttributeRpcGrpcStatusCode, status); } - // Remove the grpc.status_code tag added by the gRPC .NET library - activity.SetTag(GrpcTagHelper.GrpcStatusCodeTagName, null); - - if (TryFetchResponse(payload, out var response)) + if (this.options.EnrichWithHttpResponseMessage is { } enrich) { try { - this.options.EnrichWithHttpResponseMessage?.Invoke(activity, response); + enrich(activity, response); } catch (Exception ex) { diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/OpenTelemetry.Instrumentation.GrpcNetClient.csproj b/src/OpenTelemetry.Instrumentation.GrpcNetClient/OpenTelemetry.Instrumentation.GrpcNetClient.csproj index ab28b9e070..e915c8cb97 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/OpenTelemetry.Instrumentation.GrpcNetClient.csproj +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/OpenTelemetry.Instrumentation.GrpcNetClient.csproj @@ -20,6 +20,7 @@ + diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md index 1df09ffffb..00412e7c50 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/README.md @@ -25,9 +25,9 @@ is released, there can be breaking changes. ## Supported .NET Versions -This package targets -[`NETSTANDARD2.1`](https://docs.microsoft.com/dotnet/standard/net-standard#net-implementation-support) -and hence can be used in any .NET versions implementing `NETSTANDARD2.1`. +This package targets all supported versions of .NET and +[`netstandard2.1`](https://docs.microsoft.com/dotnet/standard/net-standard#net-implementation-support) +and hence can be used in any .NET versions implementing `netstandard2.1`. ## Steps to enable OpenTelemetry.Instrumentation.GrpcNetClient diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.GrpcNetClient/TracerProviderBuilderExtensions.cs index cfd48d50a6..ffa83b58cd 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/TracerProviderBuilderExtensions.cs @@ -55,7 +55,7 @@ public static TracerProviderBuilder AddGrpcClientInstrumentation( builder.ConfigureServices(services => services.Configure(name, configure)); } - builder.AddSource(GrpcClientDiagnosticListener.ActivitySourceName); + builder.AddSource(GrpcClientDiagnosticListener.ActivitySource.Name); builder.AddLegacySource("Grpc.Net.Client.GrpcOut"); return builder.AddInstrumentation(sp => diff --git a/src/Shared/SemanticConventions.cs b/src/Shared/SemanticConventions.cs index 784b20524d..5689c80bc7 100644 --- a/src/Shared/SemanticConventions.cs +++ b/src/Shared/SemanticConventions.cs @@ -60,6 +60,7 @@ internal static class SemanticConventions public const string AttributeRpcService = "rpc.service"; public const string AttributeRpcMethod = "rpc.method"; public const string AttributeRpcGrpcStatusCode = "rpc.grpc.status_code"; + public const string AttributeRpcResponseStatusCode = "rpc.response.status_code"; public const string AttributeMessageType = "message.type"; public const string AttributeMessageId = "message.id"; diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs index 879007b0bf..9fd8c58dae 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using OpenTelemetry.Tests; namespace OpenTelemetry.Instrumentation.Grpc.Tests; @@ -15,7 +16,11 @@ internal sealed class GrpcServer : IAsyncDisposable { private IHost? host; - public int Port { get; private set; } + public Uri Address + { + get => field ?? throw new InvalidOperationException("Server has not been started."); + set; + } public async Task StartAsync() { @@ -24,29 +29,45 @@ public async Task StartAsync() throw new InvalidOperationException("Server is already started."); } - // Allows gRPC client to call insecure gRPC services - // https://docs.microsoft.com/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.1#call-insecure-grpc-services-with-net-core-client - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - - this.Port = 0; - var retryCount = 5; - while (retryCount > 0) + var attemptsRemaining = retryCount; + + while (attemptsRemaining > 0) { try { - this.Port = Random.Shared.Next(2000, 5000); - this.host = this.CreateServer(); - - await this.host.StartAsync(); - break; + var uri = new UriBuilder() + { + Host = "localhost", + Port = TcpPortProvider.GetOpenPort(), + Scheme = Uri.UriSchemeHttp, + }.Uri; + + var host = this.CreateServer(uri.Port); + + try + { + await host.StartAsync(); + + this.host = host; + this.Address = uri; + + return; + } + catch (Exception) + { + host.Dispose(); + throw; + } } catch (IOException) { - retryCount--; + attemptsRemaining--; this.host?.Dispose(); } } + + throw new InvalidOperationException($"Failed to start server within {retryCount} attempts."); } public async ValueTask DisposeAsync() @@ -60,17 +81,14 @@ public async ValueTask DisposeAsync() GC.SuppressFinalize(this); } - private IHost CreateServer() + private IHost CreateServer(int port) { var hostBuilder = Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webBuilder => { + // Setup a HTTP/2 endpoint without TLS webBuilder - .ConfigureKestrel(options => - { - // Setup a HTTP/2 endpoint without TLS. - options.ListenLocalhost(this.Port, o => o.Protocols = HttpProtocols.Http2); - }) + .ConfigureKestrel(options => options.ListenLocalhost(port, o => o.Protocols = HttpProtocols.Http2)) .UseStartup(); }); diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs index bdfd206c50..db9ee383f0 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs @@ -15,7 +15,6 @@ #endif using OpenTelemetry.Instrumentation.Grpc.Tests.GrpcTestHelpers; using OpenTelemetry.Instrumentation.GrpcNetClient; -using OpenTelemetry.Instrumentation.GrpcNetClient.Implementation; using OpenTelemetry.Trace; using Xunit; @@ -35,8 +34,7 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho var enrichWithHttpRequestMessageCalled = false; var enrichWithHttpResponseMessageCalled = false; - var uri = new Uri($"{baseAddress}:1234"); - var uriHostNameType = Uri.CheckHostName(uri.Host); + var uri = new UriBuilder(baseAddress) { Port = 1234 }.Uri; using var httpClient = ClientTestHelpers.CreateTestClient(async request => { @@ -70,7 +68,7 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho HttpClient = httpClient, }); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest()); + _ = client.SayHello(new HelloRequest()); } Assert.Single(exportedItems); @@ -83,20 +81,12 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho Assert.NotEqual(default, activity.Context.SpanId); Assert.Equal($"greet.Greeter/SayHello", activity.DisplayName); - Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystem)); + Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystemName)); Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); Assert.Equal("SayHello", activity.GetTagValue(SemanticConventions.AttributeRpcMethod)); - if (uriHostNameType is UriHostNameType.IPv4 or UriHostNameType.IPv6) - { - Assert.Equal(uri.Host, activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); - Assert.Null(activity.GetTagValue(SemanticConventions.AttributeServerAddress)); - } - else - { - Assert.Null(activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); - Assert.Equal(uri.Host, activity.GetTagValue(SemanticConventions.AttributeServerAddress)); - } + Assert.Null(activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); + Assert.Equal(uri.Host, activity.GetTagValue(SemanticConventions.AttributeServerAddress)); Assert.Equal(uri.Port, activity.GetTagValue(SemanticConventions.AttributeServerPort)); Assert.Equal(ActivityStatusCode.Unset, activity.Status); @@ -104,7 +94,7 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho // Tags added by the library then removed from the instrumentation Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcMethodTagName)); Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcStatusCodeTagName)); - Assert.Equal(0, activity.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, activity.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); if (shouldEnrich) { @@ -119,7 +109,6 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho [InlineData(false)] public void GrpcAndHttpClientInstrumentationIsInvoked(bool shouldEnrich) { - var uri = new Uri($"http://localhost:{this.server.Port}"); var exportedItems = new List(); using var parent = new Activity("parent") @@ -146,15 +135,13 @@ public void GrpcAndHttpClientInstrumentationIsInvoked(bool shouldEnrich) .AddInMemoryExporter(exportedItems) .Build()) { - // With net5, based on the grpc changes, the quantity of default activities changed. - // TODO: This is a workaround. https://github.com/open-telemetry/opentelemetry-dotnet/issues/1490 - using var channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions() + using var channel = GrpcChannel.ForAddress(this.server.Address, new GrpcChannelOptions() { HttpClient = new HttpClient(), }); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest()); + _ = client.SayHello(new HelloRequest()); } Assert.Equal(2, exportedItems.Count); @@ -163,7 +150,7 @@ public void GrpcAndHttpClientInstrumentationIsInvoked(bool shouldEnrich) ValidateGrpcActivity(grpcSpan); Assert.Equal($"greet.Greeter/SayHello", grpcSpan.DisplayName); - Assert.Equal(0, grpcSpan.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, grpcSpan.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); Assert.Equal("POST", httpSpan.DisplayName); Assert.Equal(grpcSpan.SpanId, httpSpan.ParentSpanId); @@ -179,10 +166,9 @@ public void GrpcAndHttpClientInstrumentationIsInvoked(bool shouldEnrich) } } - [Fact(Skip = "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1727")] + [Fact] public void GrpcAndHttpClientInstrumentationWithSuppressInstrumentation() { - var uri = new Uri($"http://localhost:{this.server.Port}"); var exportedItems = new List(); using var parent = new Activity("parent") @@ -203,9 +189,9 @@ public void GrpcAndHttpClientInstrumentationWithSuppressInstrumentation() }, (value) => { - var channel = GrpcChannel.ForAddress(uri); + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest()); + _ = client.SayHello(new HelloRequest()); }); } @@ -217,27 +203,30 @@ public void GrpcAndHttpClientInstrumentationWithSuppressInstrumentation() ValidateGrpcActivity(grpcSpan1); Assert.Equal($"greet.Greeter/SayHello", grpcSpan1.DisplayName); - Assert.Equal(0, grpcSpan1.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, grpcSpan1.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); ValidateGrpcActivity(grpcSpan2); Assert.Equal($"greet.Greeter/SayHello", grpcSpan2.DisplayName); - Assert.Equal(0, grpcSpan2.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, grpcSpan2.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); ValidateGrpcActivity(grpcSpan3); Assert.Equal($"greet.Greeter/SayHello", grpcSpan3.DisplayName); - Assert.Equal(0, grpcSpan3.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, grpcSpan3.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); ValidateGrpcActivity(grpcSpan4); Assert.Equal($"greet.Greeter/SayHello", grpcSpan4.DisplayName); - Assert.Equal(0, grpcSpan4.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, grpcSpan4.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); } +#if NET [Fact(Skip = "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1727")] +#else + [Fact] +#endif public void GrpcPropagatesContextWithSuppressInstrumentationOptionSetToTrue() { try { - var uri = new Uri($"http://localhost:{this.server.Port}"); var exportedItems = new List(); using var source = new ActivitySource("test-source"); @@ -269,9 +258,10 @@ public void GrpcPropagatesContextWithSuppressInstrumentationOptionSetToTrue() { using var activity = source.StartActivity("parent"); Assert.NotNull(activity); - var channel = GrpcChannel.ForAddress(uri); + + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest()); + _ = client.SayHello(new HelloRequest()); } var serverActivity = exportedItems.Single(activity => activity.OperationName == OperationNameHttpRequestIn); @@ -298,7 +288,6 @@ public void GrpcDoesNotPropagateContextWithSuppressInstrumentationOptionSetToFal { try { - var uri = new Uri($"http://localhost:{this.server.Port}"); var exportedItems = new List(); using var source = new ActivitySource("test-source"); @@ -322,9 +311,9 @@ public void GrpcDoesNotPropagateContextWithSuppressInstrumentationOptionSetToFal .Build()) { using var activity = source.StartActivity("parent"); - var channel = GrpcChannel.ForAddress(uri); + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest(), headers); + _ = client.SayHello(new HelloRequest(), headers); } Assert.Equal(2, exportedItems.Count); @@ -344,12 +333,11 @@ public void GrpcDoesNotPropagateContextWithSuppressInstrumentationOptionSetToFal } } - [Fact(Skip = "https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1727")] + [Fact] public void GrpcClientInstrumentationRespectsSdkSuppressInstrumentation() { try { - var uri = new Uri($"http://localhost:{this.server.Port}"); var exportedItems = new List(); using var source = new ActivitySource("test-source"); @@ -364,19 +352,16 @@ public void GrpcClientInstrumentationRespectsSdkSuppressInstrumentation() using (Sdk.CreateTracerProviderBuilder() .AddSource("test-source") - .AddGrpcClientInstrumentation(o => - { - o.SuppressDownstreamInstrumentation = true; - }) + .AddGrpcClientInstrumentation(o => o.SuppressDownstreamInstrumentation = true) .AddInMemoryExporter(exportedItems) .Build()) { using var activity = source.StartActivity("parent"); using (SuppressInstrumentationScope.Begin()) { - var channel = GrpcChannel.ForAddress(uri); + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); - var rs = client.SayHello(new HelloRequest()); + _ = client.SayHello(new HelloRequest()); } } @@ -425,8 +410,10 @@ public void Grpc_BadArgs() private static void ValidateGrpcActivity(Activity activityToValidate) { - Assert.Equal(GrpcClientDiagnosticListener.ActivitySourceName, activityToValidate.Source.Name); - Assert.Equal(GrpcClientDiagnosticListener.Version, activityToValidate.Source.Version); + Assert.Equal("OpenTelemetry.Instrumentation.GrpcNetClient", activityToValidate.Source.Name); + Assert.NotNull(activityToValidate.Source.Version); + Assert.NotEmpty(activityToValidate.Source.Version); Assert.Equal(ActivityKind.Client, activityToValidate.Kind); + Assert.StartsWith("https://opentelemetry.io/schemas/", activityToValidate.Source.TelemetrySchemaUrl); } } diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.server.cs b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.server.cs index 6eba77db9a..ff3df90d3e 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.server.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.server.cs @@ -56,10 +56,13 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(string? enableGrp .AddInMemoryExporter(exportedItems) .Build(); - var clientLoopbackAddresses = new[] { IPAddress.Loopback.ToString(), IPAddress.IPv6Loopback.ToString() }; - var uri = new Uri($"http://localhost:{this.server.Port}"); + var clientLoopbackAddresses = new[] + { + IPAddress.Loopback.ToString(), + IPAddress.IPv6Loopback.ToString(), + }; - using var channel = GrpcChannel.ForAddress(uri); + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); var returnMsg = client.SayHello(new HelloRequest()).Message; @@ -93,7 +96,7 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributes(string? enableGrp // The following are http.* attributes that are also included on the span for the gRPC invocation. Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeServerAddress)); - Assert.Equal(this.server.Port, activity.GetTagValue(SemanticConventions.AttributeServerPort)); + Assert.Equal(this.server.Address.Port, activity.GetTagValue(SemanticConventions.AttributeServerPort)); Assert.Equal("POST", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeUrlScheme)); Assert.Equal("/greet.Greeter/SayHello", activity.GetTagValue(SemanticConventions.AttributeUrlPath)); @@ -127,10 +130,13 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewAc .AddInMemoryExporter(exportedItems) .Build(); - var clientLoopbackAddresses = new[] { IPAddress.Loopback.ToString(), IPAddress.IPv6Loopback.ToString() }; - var uri = new Uri($"http://localhost:{this.server.Port}"); + var clientLoopbackAddresses = new[] + { + IPAddress.Loopback.ToString(), + IPAddress.IPv6Loopback.ToString(), + }; - using var channel = GrpcChannel.ForAddress(uri); + using var channel = GrpcChannel.ForAddress(this.server.Address); var client = new Greeter.GreeterClient(channel); var headers = new Metadata { @@ -149,14 +155,14 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewAc if (enableGrpcAspNetCoreSupport != null && enableGrpcAspNetCoreSupport.Equals("true", StringComparison.OrdinalIgnoreCase)) { - Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystem)); + Assert.Equal("grpc", activity.GetTagValue(SemanticConventions.AttributeRpcSystemName)); Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); Assert.Equal("SayHello", activity.GetTagValue(SemanticConventions.AttributeRpcMethod)); Assert.Contains(activity.GetTagValue(SemanticConventions.AttributeNetPeerIp), clientLoopbackAddresses); Assert.NotEqual(0, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcMethodTagName)); Assert.Null(activity.GetTagValue(GrpcTagHelper.GrpcStatusCodeTagName)); - Assert.Equal(0, activity.GetTagValue(SemanticConventions.AttributeRpcGrpcStatusCode)); + Assert.Equal(0, activity.GetTagValue(SemanticConventions.AttributeRpcResponseStatusCode)); } else { @@ -167,12 +173,13 @@ public void GrpcAspNetCoreInstrumentationAddsCorrectAttributesWhenItCreatesNewAc Assert.Equal(ActivityStatusCode.Unset, activity.Status); // The following are http.* attributes that are also included on the span for the gRPC invocation. - Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeNetHostName)); - Assert.Equal(this.server.Port, activity.GetTagValue(SemanticConventions.AttributeNetHostPort)); - Assert.Equal("POST", activity.GetTagValue(SemanticConventions.AttributeHttpMethod)); - Assert.Equal("/greet.Greeter/SayHello", activity.GetTagValue(SemanticConventions.AttributeHttpTarget)); - Assert.Equal($"http://localhost:{this.server.Port}/greet.Greeter/SayHello", activity.GetTagValue(SemanticConventions.AttributeHttpUrl)); - Assert.StartsWith("grpc-dotnet", activity.GetTagValue(SemanticConventions.AttributeHttpUserAgent) as string); + Assert.Equal("localhost", activity.GetTagValue(SemanticConventions.AttributeServerAddress)); + Assert.Equal(this.server.Address.Port, activity.GetTagValue(SemanticConventions.AttributeServerPort)); + Assert.Equal("POST", activity.GetTagValue(SemanticConventions.AttributeHttpRequestMethod)); + Assert.Equal("http", activity.GetTagValue(SemanticConventions.AttributeUrlScheme)); + Assert.Equal("/greet.Greeter/SayHello", activity.GetTagValue(SemanticConventions.AttributeUrlPath)); + Assert.Equal("2", activity.GetTagValue(SemanticConventions.AttributeNetworkProtocolVersion)); + Assert.StartsWith("grpc-dotnet", activity.GetTagValue(SemanticConventions.AttributeUserAgentOriginal) as string); } finally { diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj index b11b937fea..4bbc41b716 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj @@ -33,6 +33,7 @@ + diff --git a/test/Shared/TcpPortProvider.cs b/test/Shared/TcpPortProvider.cs new file mode 100644 index 0000000000..e1de08305b --- /dev/null +++ b/test/Shared/TcpPortProvider.cs @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Net; +using System.Net.Sockets; + +namespace OpenTelemetry.Tests; + +/// +/// Helper class that tries to provide unique ports numbers across processes and threads in the same machine. +/// This class cannot guarantee a port is actually available, but should help avoid most conflicts. +/// +internal static class TcpPortProvider +{ + public static int GetOpenPort() + { + TcpListener? tcpListener = null; + + try + { + tcpListener = new TcpListener(IPAddress.Loopback, 0); + tcpListener.Start(); + + var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port; + + return port; + } + finally + { +#if NET + tcpListener?.Dispose(); +#else + tcpListener?.Stop(); +#endif + + } + } +} From cd05e59b39472e8dc5426afa1ce0b6d184ea3df4 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 1 May 2026 13:45:21 +0100 Subject: [PATCH 2/9] [GrpcCore] Update Semantic Conventions - Update to v1.14.0 of the gRPC Semantic Conventions. - Add `net10.0` TFM. - Improve gRPC tests. - Apply Visual Studio refactor suggestoins. Contributes to open-telemetry#4064. --- .../CHANGELOG.md | 9 ++ .../ClientTracingInterceptor.cs | 17 +--- .../Extensions.cs | 21 ++--- .../GrpcCoreInstrumentation.cs | 21 +---- ...nTelemetry.Instrumentation.GrpcCore.csproj | 3 +- .../RpcScope.cs | 44 ++++++--- .../ServerTracingInterceptor.cs | 2 +- .../TracerProviderBuilderExtensions.cs | 2 +- .../CHANGELOG.md | 4 +- .../GrpcClientDiagnosticListener.cs | 4 +- .../FoobarService.cs | 6 +- .../GrpcCoreClientInterceptorTests.cs | 94 ++++++++++++++----- .../GrpcCoreServerInterceptorTests.cs | 57 ++++++++--- .../InterceptorActivityListener.cs | 18 +--- .../GrpcTests.client.cs | 2 - 15 files changed, 185 insertions(+), 119 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md index 24e594860d..cf24afb872 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +* **BREAKING**: Update to version 1.41.0 of the Semantic Conventions. + ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) + +* Add instrumentation scope version and schema URL to traces. + ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) + +* Add `net10.0` target framework. + ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) + ## 1.0.0-beta.11 Released 2026-Apr-21 diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs index ce87f3a3d8..0c9b0205aa 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs @@ -291,11 +291,6 @@ private sealed class ClientRpcScope : RpcScope private static readonly Action MetadataSetter = (metadata, key, value) => { metadata?.Add(new Metadata.Entry(key, value)); }; - /// - /// The context. - /// - private readonly ClientInterceptorContext context; - /// /// The parent activity. /// @@ -307,9 +302,9 @@ private sealed class ClientRpcScope : RpcScopeThe context. /// The options. public ClientRpcScope(ClientInterceptorContext context, ClientTracingInterceptorOptions options) - : base(context.Method?.FullName, options.RecordMessageEvents, options.RecordException) + : base(context.Host, context.Method?.FullName, options.RecordMessageEvents, options.RecordException) { - this.context = context; + this.Context = context; // Capture the current activity. this.parentActivity = Activity.Current; @@ -355,20 +350,18 @@ public ClientRpcScope(ClientInterceptorContext context, Cli this.SetActivity(rpcActivity); options.Propagator.Inject(new PropagationContext(rpcActivity.Context, Baggage.Current), callOptions.Headers, MetadataSetter); - this.context = new ClientInterceptorContext(context.Method!, context.Host, callOptions); + this.Context = new ClientInterceptorContext(context.Method!, context.Host, callOptions); } /// /// Gets the context. /// - public ClientInterceptorContext Context => this.context; + public ClientInterceptorContext Context { get; } /// /// Restores the parent activity. /// public void RestoreParentActivity() - { - Activity.Current = this.parentActivity; - } + => Activity.Current = this.parentActivity; } } diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/Extensions.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/Extensions.cs index 0170a05048..6eaf8f8f12 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/Extensions.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/Extensions.cs @@ -14,18 +14,15 @@ internal static class Extensions /// The first. /// The second. /// An Action. - internal static Action WithBestEffortDispose(this IDisposable first, IDisposable second) + internal static Action WithBestEffortDispose(this IDisposable first, IDisposable second) => () => { - return () => + try { - try - { - first.Dispose(); - } - finally - { - second.Dispose(); - } - }; - } + first.Dispose(); + } + finally + { + second.Dispose(); + } + }; } diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/GrpcCoreInstrumentation.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/GrpcCoreInstrumentation.cs index a3a627133a..0a33e9368f 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/GrpcCoreInstrumentation.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/GrpcCoreInstrumentation.cs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; -using System.Reflection; -using OpenTelemetry.Internal; +using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.GrpcCore; @@ -13,22 +12,12 @@ namespace OpenTelemetry.Instrumentation.GrpcCore; internal static class GrpcCoreInstrumentation { /// - /// The assembly. + /// Gets the version of the RPC Semantic Conventions used by the instrumentation. /// - internal static readonly Assembly Assembly = typeof(GrpcCoreInstrumentation).Assembly; + internal static readonly Version SemanticConventionsVersion = new(1, 41, 0); /// - /// The assembly name. + /// Gets the activity source for the instrumentation. /// - internal static readonly AssemblyName AssemblyName = Assembly.GetName(); - - /// - /// The activity source name. - /// - internal static readonly string ActivitySourceName = AssemblyName.Name; - - /// - /// The activity source. - /// - internal static readonly ActivitySource ActivitySource = new(ActivitySourceName, Assembly.GetPackageVersion()); + internal static readonly ActivitySource ActivitySource = ActivitySourceFactory.Create(typeof(GrpcCoreInstrumentation), SemanticConventionsVersion); } diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj index 55b9831f35..5b0f1c946f 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj @@ -1,7 +1,7 @@ - $(NetStandardMinimumSupportedVersion) + net10.0;$(NetStandardMinimumSupportedVersion) .NET gRPC Core based client and server interceptors for OpenTelemetry. $(PackageTags);gRPC Core;interceptors Instrumentation.GrpcCore- @@ -24,6 +24,7 @@ + diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs index d87311a92a..29115d560c 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs @@ -19,6 +19,11 @@ internal abstract class RpcScope : IDisposable where TRequest : class where TResponse : class { + /// + /// The host. + /// + private readonly string? host; + /// /// The record message events flag. /// @@ -52,11 +57,17 @@ internal abstract class RpcScope : IDisposable /// /// Initializes a new instance of the class. /// + /// The host that the currect invocation will be dispatched to. /// Full name of the service. /// if set to true [record message events]. /// If set to true [record exception]. - protected RpcScope(string? fullServiceName, bool recordMessageEvents, bool recordException) + protected RpcScope( + string? host, + string? fullServiceName, + bool recordMessageEvents, + bool recordException) { + this.host = host; this.FullServiceName = fullServiceName?.TrimStart('/') ?? "unknownservice/unknownmethod"; this.recordMessageEvents = recordMessageEvents; this.recordException = recordException; @@ -164,11 +175,11 @@ protected void SetActivity(Activity? activity) return; } - // assign some reasonable defaults + // Assign some reasonable defaults var rpcService = this.FullServiceName; var rpcMethod = this.FullServiceName; - // split the full service name by the slash + // Split the full service name by the slash var parts = this.FullServiceName.Split('/'); if (parts.Length == 2) { @@ -176,9 +187,16 @@ protected void SetActivity(Activity? activity) rpcMethod = parts[1]; } - this.activity.SetTag(SemanticConventions.AttributeRpcSystem, "grpc"); + this.activity.SetTag(SemanticConventions.AttributeRpcSystemName, "grpc"); this.activity.SetTag(SemanticConventions.AttributeRpcService, rpcService); this.activity.SetTag(SemanticConventions.AttributeRpcMethod, rpcMethod); + + if (this.host is { Length: > 0 } host) + { + this.activity.SetTag(SemanticConventions.AttributeServerAddress, host); + } + + this.activity.DisplayName = rpcMethod.Trim('/'); } /// @@ -188,12 +206,12 @@ protected void SetActivity(Activity? activity) /// If set to true [mark as completed]. private void StopActivity(int statusCode, bool markAsCompleted = true) { - if (markAsCompleted && !this.TryMarkAsCompleted()) + if ((markAsCompleted && !this.TryMarkAsCompleted()) || this.activity is null) { return; } - this.activity!.SetTag(SemanticConventions.AttributeRpcGrpcStatusCode, statusCode); + this.activity.SetTag(SemanticConventions.AttributeRpcResponseStatusCode, statusCode); this.activity.Stop(); } @@ -203,7 +221,7 @@ private void StopActivity(int statusCode, bool markAsCompleted = true) /// The exception. private void StopActivity(Exception exception) { - if (!this.TryMarkAsCompleted()) + if (!this.TryMarkAsCompleted() || this.activity is null) { return; } @@ -219,10 +237,10 @@ private void StopActivity(Exception exception) if (!string.IsNullOrEmpty(description)) { - this.activity!.SetStatus(ActivityStatusCode.Error, description); + this.activity.SetStatus(ActivityStatusCode.Error, description); } - if (this.activity!.IsAllDataRequested && this.recordException) + if (this.activity.IsAllDataRequested && this.recordException) { this.activity.AddException(exception); } @@ -235,9 +253,7 @@ private void StopActivity(Exception exception) /// /// Returns true if marked as completed successfully. private bool TryMarkAsCompleted() - { - return Interlocked.CompareExchange(ref this.complete, 1, 0) == 0; - } + => Interlocked.CompareExchange(ref this.complete, 1, 0) == 0; /// /// Adds a message event. @@ -247,7 +263,7 @@ private bool TryMarkAsCompleted() /// if true this is a request message. private void AddMessageEvent(string eventName, IMessage? message, bool request) { - if (message == null) + if (message == null || this.activity is null) { return; } @@ -265,6 +281,6 @@ private void AddMessageEvent(string eventName, IMessage? message, bool request) new(SemanticConventions.AttributeMessageUncompressedSize, messageSize), ]); - this.activity!.AddEvent(new ActivityEvent(eventName, default, attributes)); + this.activity.AddEvent(new ActivityEvent(eventName, default, attributes)); } } diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/ServerTracingInterceptor.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/ServerTracingInterceptor.cs index 07600cbc5c..4921bcc476 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/ServerTracingInterceptor.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/ServerTracingInterceptor.cs @@ -176,7 +176,7 @@ private class ServerRpcScope : RpcScopeThe context. /// The options. public ServerRpcScope(ServerCallContext context, ServerTracingInterceptorOptions options) - : base(context.Method, options.RecordMessageEvents, options.RecordException) + : base(context.Host, context.Method, options.RecordMessageEvents, options.RecordException) { if (!GrpcCoreInstrumentation.ActivitySource.HasListeners()) { diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/TracerProviderBuilderExtensions.cs index 67ab4e9701..9a75fd1c02 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/TracerProviderBuilderExtensions.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/TracerProviderBuilderExtensions.cs @@ -21,6 +21,6 @@ public static TracerProviderBuilder AddGrpcCoreInstrumentation( { Guard.ThrowIfNull(builder); - return builder.AddSource(GrpcCoreInstrumentation.ActivitySourceName); + return builder.AddSource(GrpcCoreInstrumentation.ActivitySource.Name); } } diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md index 90dafbdf57..69c71eaa5c 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/CHANGELOG.md @@ -3,10 +3,10 @@ ## Unreleased * **BREAKING**: Update to version 1.41.0 of the Semantic Conventions. - ([#TODO](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/TODO)) + ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) * Add instrumentation scope version and schema URL to traces. - ([#TODO](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/TODO)) + ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) ## 1.15.1-beta.1 diff --git a/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs index 06506717a0..593a84e65a 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcNetClient/Implementation/GrpcClientDiagnosticListener.cs @@ -109,10 +109,10 @@ public void OnStartActivity(Activity activity, object? payload) var grpcMethod = GrpcTagHelper.GetGrpcMethodFromActivity(activity); + activity.DisplayName = grpcMethod?.Trim('/') ?? GrpcTagHelper.RpcSystemGrpc; + if (grpcMethod != null) { - activity.DisplayName = grpcMethod.Trim('/'); - if (GrpcTagHelper.TryParseRpcServiceAndRpcMethod(grpcMethod, out var rpcService, out var rpcMethod)) { activity.SetTag(SemanticConventions.AttributeRpcService, rpcService); diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs index 3d2903872c..9194f13f27 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs @@ -176,7 +176,7 @@ public static async Task MakeDuplexStreamingRequest(Foobar.FoobarClient client, } /// - public async override Task Unary(FoobarRequest request, ServerCallContext context) + public override async Task Unary(FoobarRequest request, ServerCallContext context) { this.CheckForFailure(context); @@ -267,8 +267,6 @@ public DisposableServer(Server server, string uriString) /// public void Dispose() - { - this.server.ShutdownAsync().Wait(); - } + => this.server.ShutdownAsync().Wait(); } } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs index 0fbe5d8247..a2fdf7865c 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs @@ -33,7 +33,10 @@ public class GrpcCoreClientInterceptorTests /// A task. [Fact] public async Task AsyncUnarySuccess() => - await TestHandlerSuccess(FoobarService.MakeUnaryAsyncRequest, DefaultMetadataFunc()); + await TestHandlerSuccess( + FoobarService.MakeUnaryAsyncRequest, + "Unary", + DefaultMetadataFunc()); /// /// Validates a failed AsyncUnary call because the endpoint isn't there. @@ -43,6 +46,7 @@ public async Task AsyncUnarySuccess() => public async Task AsyncUnaryUnavailable() => await TestHandlerFailure( FoobarService.MakeUnaryAsyncRequest, + "Unary", StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -53,7 +57,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task AsyncUnaryFail() => - await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest); + await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, "Unary"); /// /// Validates a failed AsyncUnary call because the client is disposed before completing the RPC. @@ -66,7 +70,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.UnaryAsync(FoobarService.DefaultRequestMessage); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "Unary"); } /// @@ -75,7 +79,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task ClientStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, "ClientStreaming", DefaultMetadataFunc()); /// /// Validates a failed ClientStreaming call when the service is unavailable. @@ -85,6 +89,7 @@ public async Task ClientStreamingSuccess() => public async Task ClientStreamingUnavailable() => await TestHandlerFailure( FoobarService.MakeClientStreamingRequest, + "ClientStreaming", StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -95,7 +100,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task ClientStreamingFail() => - await TestHandlerFailure(FoobarService.MakeClientStreamingRequest); + await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); /// /// Validates a failed ClientStreaming call because the client is disposed before completing the RPC. @@ -108,7 +113,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.ClientStreaming(); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "ClientStreaming"); } /// @@ -117,7 +122,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task ServerStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, "ServerStreaming", DefaultMetadataFunc()); /// /// Validates a failed ServerStreaming call. @@ -125,7 +130,7 @@ public async Task ServerStreamingSuccess() => /// A task. [Fact] public async Task ServerStreamingFail() => - await TestHandlerFailure(FoobarService.MakeServerStreamingRequest); + await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); /// /// Validates a failed ServerStreaming call because the client is disposed before completing the RPC. @@ -138,7 +143,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.ServerStreaming(FoobarService.DefaultRequestMessage); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "ServerStreaming"); } /// @@ -147,7 +152,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task DuplexStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming", DefaultMetadataFunc()); /// /// Validates a failed DuplexStreaming call when the service is unavailable. @@ -157,6 +162,7 @@ public async Task DuplexStreamingSuccess() => public async Task DuplexStreamingUnavailable() => await TestHandlerFailure( FoobarService.MakeDuplexStreamingRequest, + "DuplexStreaming", StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -167,7 +173,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task DuplexStreamingFail() => - await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest); + await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); /// /// Validates a failed DuplexStreaming call because the client is disposed before completing the RPC. @@ -180,7 +186,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.DuplexStreaming(); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "DuplexStreaming"); } /// @@ -310,7 +316,7 @@ public void BlockingUnaryCallWithNonProtobufPayloadDoesNotThrowWhenRecordingMess Assert.NotNull(response); var activity = activityListener.Activity; - ValidateCommonActivityTags(activity); + ValidateCommonActivityTags(activity, "Unary"); Assert.Empty(activity!.Events); } @@ -318,11 +324,13 @@ public void BlockingUnaryCallWithNonProtobufPayloadDoesNotThrowWhenRecordingMess /// Validates the common activity tags. /// /// The activity. + /// The expected gRPC method name. /// The expected status code. /// if set to true [recorded messages]. /// if set to true [recorded exceptions]. internal static void ValidateCommonActivityTags( Activity? activity, + string expectedMethodName, StatusCode expectedStatusCode = StatusCode.OK, bool recordedMessages = false, bool recordedExceptions = false) @@ -332,12 +340,14 @@ internal static void ValidateCommonActivityTags( Assert.True(activity.IsStopped, "The activity has not been stopped."); + Assert.Equal(expectedMethodName, activity.DisplayName); + // TagObjects contain non string values // Tags contains only string values - Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcSystem && (string?)t.Value == "grpc"); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcSystemName && (string?)t.Value == "grpc"); Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcService && (string?)t.Value == "OpenTelemetry.Instrumentation.GrpcCore.Tests.Foobar"); - Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcMethod); - Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcGrpcStatusCode && (int?)t.Value == (int)expectedStatusCode); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcMethod && (string?)t.Value == expectedMethodName); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeRpcResponseStatusCode && (int?)t.Value == (int)expectedStatusCode); // Cancelled is not an error. if (expectedStatusCode is not StatusCode.OK and not StatusCode.Cancelled) @@ -388,9 +398,13 @@ static void ValidateCommonEventAttributes(ActivityEvent activityEvent) /// Tests basic handler success. /// /// The client request function. + /// The expected gRPC method name. /// The additional metadata, if any. /// A Task. - private static async Task TestHandlerSuccess(Func clientRequestFunc, Metadata additionalMetadata) + private static async Task TestHandlerSuccess( + Func clientRequestFunc, + string expectedMethodName, + Metadata additionalMetadata) { var propagator = new TestTextMapPropagator(); PropagationContext capturedPropagationContext = default; @@ -454,7 +468,11 @@ private static async Task TestHandlerSuccess(Func /// The client request function. + /// The expected gRPC method name. /// The status code to use for the failure. Defaults to ResourceExhausted. /// if set to true [validate error description]. /// An alternate server URI string. @@ -501,12 +524,18 @@ private static async Task TestHandlerSuccess(Func private static async Task TestHandlerFailure( Func clientRequestFunc, + string expectedMethodName, StatusCode statusCode = StatusCode.ResourceExhausted, bool validateErrorDescription = true, string? serverUriString = null) { var testTags = new TestActivityTags(); - var interceptorOptions = new ClientTracingInterceptorOptions { Propagator = new TraceContextPropagator(), AdditionalTags = testTags.Tags, RecordException = true }; + var interceptorOptions = new ClientTracingInterceptorOptions + { + Propagator = new TraceContextPropagator(), + AdditionalTags = testTags.Tags, + RecordException = true, + }; using var activityListener = new InterceptorActivityListener(testTags); @@ -525,7 +554,12 @@ private static async Task TestHandlerFailure( var activity = activityListener.Activity; - ValidateCommonActivityTags(activity, statusCode, interceptorOptions.RecordMessageEvents, interceptorOptions.RecordException); + ValidateCommonActivityTags( + activity, + expectedMethodName, + statusCode, + interceptorOptions.RecordMessageEvents, + interceptorOptions.RecordException); if (validateErrorDescription) { @@ -538,18 +572,30 @@ private static async Task TestHandlerFailure( /// Tests for Activity cancellation when the handler is disposed before completing the RPC. /// /// The client request action. - private void TestActivityIsCancelledWhenHandlerDisposed(Action clientRequestAction) + /// The expected gRPC method name. + private void TestActivityIsCancelledWhenHandlerDisposed( + Action clientRequestAction, + string expectedMethodName) { var testTags = new TestActivityTags(); using var activityListener = new InterceptorActivityListener(testTags); using (var server = FoobarService.Start()) { - var clientInterceptorOptions = new ClientTracingInterceptorOptions { Propagator = new TraceContextPropagator(), AdditionalTags = testTags.Tags }; + var clientInterceptorOptions = new ClientTracingInterceptorOptions + { + Propagator = new TraceContextPropagator(), + AdditionalTags = testTags.Tags, + }; + var client = FoobarService.ConstructRpcClient(server.UriString, new ClientTracingInterceptor(clientInterceptorOptions)); clientRequestAction(client); } - ValidateCommonActivityTags(activityListener.Activity, StatusCode.Cancelled, false); + ValidateCommonActivityTags( + activityListener.Activity, + expectedMethodName, + StatusCode.Cancelled, + false); } } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs index 3dbafaa387..26865f45da 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs @@ -19,7 +19,7 @@ public class GrpcCoreServerInterceptorTests /// A task. [Fact] public async Task UnaryServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeUnaryAsyncRequest); + await TestHandlerSuccess(FoobarService.MakeUnaryAsyncRequest, "Unary"); /// /// Validates a failed UnaryServerHandler call. @@ -27,7 +27,7 @@ public async Task UnaryServerHandlerSuccess() => /// A task. [Fact] public async Task UnaryServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest); + await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, "Unary"); /// /// Validates a successful ClientStreamingServerHandler call. @@ -35,7 +35,7 @@ public async Task UnaryServerHandlerFail() => /// A task. [Fact] public async Task ClientStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest); + await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); /// /// Validates a failed ClientStreamingServerHandler call. @@ -43,7 +43,7 @@ public async Task ClientStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task ClientStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeClientStreamingRequest); + await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); /// /// Validates a successful ServerStreamingServerHandler call. @@ -51,7 +51,7 @@ public async Task ClientStreamingServerHandlerFail() => /// A task. [Fact] public async Task ServerStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest); + await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); /// /// Validates a failed ServerStreamingServerHandler call. @@ -59,7 +59,7 @@ public async Task ServerStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task ServerStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeServerStreamingRequest); + await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); /// /// Validates a successful DuplexStreamingServerHandler call. @@ -67,7 +67,7 @@ public async Task ServerStreamingServerHandlerFail() => /// A task. [Fact] public async Task DuplexStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest); + await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); /// /// Validates a failed DuplexStreamingServerHandler call. @@ -75,7 +75,7 @@ public async Task DuplexStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task DuplexStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest); + await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); /// /// Validates that non protobuf payloads do not abort server RPCs when @@ -129,7 +129,9 @@ static Task HandleUnaryCall(NonProtobufPayload request, Serv Assert.NotNull(response); var activity = activityListener.Activity; - GrpcCoreClientInterceptorTests.ValidateCommonActivityTags(activity); + + GrpcCoreClientInterceptorTests.ValidateCommonActivityTags(activity, "Unary"); + Assert.Empty(activity!.Events); } finally @@ -147,9 +149,13 @@ static Task HandleUnaryCall(NonProtobufPayload request, Serv /// A common method to test server interceptor handler success. /// /// The specific client request function. + /// The expected gRPC method name. /// The additional metadata, if any. /// A Task. - private static async Task TestHandlerSuccess(Func clientRequestFunc, Metadata? additionalMetadata = null) + private static async Task TestHandlerSuccess( + Func clientRequestFunc, + string expectedMethodName, + Metadata? additionalMetadata = null) { // starts the server with the server interceptor var testTags = new TestActivityTags(); @@ -163,7 +169,13 @@ private static async Task TestHandlerSuccess(Func /// The specific client request function. + /// The expected gRPC method name. /// The additional metadata, if any. /// A Task. - private static async Task TestHandlerFailure(Func clientRequestFunc, Metadata? additionalMetadata = null) + private static async Task TestHandlerFailure( + Func clientRequestFunc, + string expectedMethodName, + Metadata? additionalMetadata = null) { // starts the server with the server interceptor var testTags = new TestActivityTags(); @@ -210,7 +232,14 @@ private static async Task TestHandlerFailure(Func(async () => await clientRequestFunc(client, additionalMetadata).ConfigureAwait(false)); var activity = activityListener.Activity; - GrpcCoreClientInterceptorTests.ValidateCommonActivityTags(activity, StatusCode.ResourceExhausted, interceptorOptions.RecordMessageEvents, interceptorOptions.RecordException); + + GrpcCoreClientInterceptorTests.ValidateCommonActivityTags( + activity, + expectedMethodName, + StatusCode.ResourceExhausted, + interceptorOptions.RecordMessageEvents, + interceptorOptions.RecordException); + Assert.NotNull(activity); Assert.Equal(FoobarService.DefaultParentFromTraceparentHeader.SpanId, activity.ParentSpanId); } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/InterceptorActivityListener.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/InterceptorActivityListener.cs index 78e749fcb9..a1414d27cc 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/InterceptorActivityListener.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/InterceptorActivityListener.cs @@ -23,19 +23,18 @@ public InterceptorActivityListener(TestActivityTags testTags) { this.activityListener = new ActivityListener { - ShouldListenTo = source => source.Name == GrpcCoreInstrumentation.ActivitySourceName, - ActivityStarted = activity => + Sample = static (ref _) => ActivitySamplingResult.AllDataAndRecorded, + ShouldListenTo = static (source) => source.Name is "OpenTelemetry.Instrumentation.GrpcCore", + ActivityStarted = (activity) => { if (testTags.HasTestTags(activity)) { this.Activity = activity; } }, - Sample = this.Sample, }; ActivitySource.AddActivityListener(this.activityListener); - Debug.Assert(GrpcCoreInstrumentation.ActivitySource.HasListeners(), "activity source has no listeners"); } /// @@ -45,14 +44,5 @@ public InterceptorActivityListener(TestActivityTags testTags) /// public void Dispose() - { - this.activityListener.Dispose(); - } - - /// - /// Always sample. - /// - /// The options. - /// a result. - private ActivitySamplingResult Sample(ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded; + => this.activityListener.Dispose(); } diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs index db9ee383f0..9b95386564 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcTests.client.cs @@ -85,9 +85,7 @@ public void GrpcClientCallsAreCollectedSuccessfully(string baseAddress, bool sho Assert.Equal("greet.Greeter", activity.GetTagValue(SemanticConventions.AttributeRpcService)); Assert.Equal("SayHello", activity.GetTagValue(SemanticConventions.AttributeRpcMethod)); - Assert.Null(activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); Assert.Equal(uri.Host, activity.GetTagValue(SemanticConventions.AttributeServerAddress)); - Assert.Equal(uri.Port, activity.GetTagValue(SemanticConventions.AttributeServerPort)); Assert.Equal(ActivityStatusCode.Unset, activity.Status); From 76a8bfd63b120fff88753a0e7eb822b7a376839f Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 1 May 2026 13:49:15 +0100 Subject: [PATCH 3/9] [GrpcCore] Fix typo Fix copy-paste typo from Grpc.Core.Api. --- src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs index 29115d560c..fb4a5aae26 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs @@ -57,7 +57,7 @@ internal abstract class RpcScope : IDisposable /// /// Initializes a new instance of the class. /// - /// The host that the currect invocation will be dispatched to. + /// The host that the current invocation will be dispatched to. /// Full name of the service. /// if set to true [record message events]. /// If set to true [record exception]. From 52eecd2a3011d18749382e179c4c5a71b0a0ec6f Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 1 May 2026 14:04:24 +0100 Subject: [PATCH 4/9] [GrpcCore] Add net8.0 TFM Add `net8.0` too. --- src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md | 2 +- .../OpenTelemetry.Instrumentation.GrpcCore.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md index cf24afb872..97122a90df 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md @@ -8,7 +8,7 @@ * Add instrumentation scope version and schema URL to traces. ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) -* Add `net10.0` target framework. +* Add `net8.0` and `net10.0` target frameworks. ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) ## 1.0.0-beta.11 diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj index 5b0f1c946f..053a9d1bec 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj @@ -1,7 +1,7 @@ - net10.0;$(NetStandardMinimumSupportedVersion) + $(TargetFrameworksForLibraries);$(NetStandardMinimumSupportedVersion) .NET gRPC Core based client and server interceptors for OpenTelemetry. $(PackageTags);gRPC Core;interceptors Instrumentation.GrpcCore- From 803e6892bc3cd7fc55d8dd27640bb2515f218c78 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 1 May 2026 17:01:02 +0100 Subject: [PATCH 5/9] [GrpcCore] Address feedback - Fix `server.address` handling. - Remove redundant TFM. - Fix typo. - Make property setter private. --- ...nTelemetry.Instrumentation.GrpcCore.csproj | 2 +- .../RpcScope.cs | 14 +++++++++++- .../FoobarService.cs | 22 +++++++++++-------- .../GrpcCoreClientInterceptorTests.cs | 22 ++++++++++++++----- .../GrpcCoreServerInterceptorTests.cs | 16 +++++++++----- .../GrpcServer.cs | 2 +- ...Instrumentation.GrpcNetClient.Tests.csproj | 2 +- test/Shared/TcpPortProvider.cs | 2 +- 8 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj index 053a9d1bec..48393e198f 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj @@ -1,7 +1,7 @@ - $(TargetFrameworksForLibraries);$(NetStandardMinimumSupportedVersion) + $(TargetFrameworksForLibraries) .NET gRPC Core based client and server interceptors for OpenTelemetry. $(PackageTags);gRPC Core;interceptors Instrumentation.GrpcCore- diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs index fb4a5aae26..d2c26c3051 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; +using System.Globalization; using Google.Protobuf; using Grpc.Core; using OpenTelemetry.Internal; @@ -193,7 +194,18 @@ protected void SetActivity(Activity? activity) if (this.host is { Length: > 0 } host) { - this.activity.SetTag(SemanticConventions.AttributeServerAddress, host); + parts = host.Split(':'); + + if (parts.Length > 0) + { + this.activity.SetTag(SemanticConventions.AttributeServerAddress, parts[0]); + + if (parts.Length > 1 && + int.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out var port)) + { + this.activity.SetTag(SemanticConventions.AttributeServerPort, port); + } + } } this.activity.DisplayName = rpcMethod.Trim('/'); diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs index 9194f13f27..e028301d2d 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs @@ -73,9 +73,8 @@ public static DisposableServer Start(Interceptor? serverInterceptor = null) }; server.Start(); - var serverUriString = new Uri("dns:localhost:" + server.Ports.Single().BoundPort).ToString(); - return new DisposableServer(server, serverUriString); + return new DisposableServer(server); } /// @@ -253,17 +252,22 @@ internal sealed class DisposableServer : IDisposable /// Initializes a new instance of the class. /// /// The server. - /// The URI string. - public DisposableServer(Server server, string uriString) + public DisposableServer(Server server) { + var serverPort = server.Ports.Single(); + this.server = server; - this.UriString = uriString; + this.HostName = serverPort.Host; + this.Port = serverPort.BoundPort; } - /// - /// Gets the URI string. - /// - public string UriString { get; } + public string Host => $"{this.HostName}:{this.Port}"; + + public string HostName { get; } + + public int Port { get; } + + public string Target => $"dns:{this.Host}"; /// public void Dispose() diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs index a2fdf7865c..e5a6083914 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs @@ -198,7 +198,7 @@ static void MakeRequest(Foobar.FoobarClient client) public async Task DownstreamInterceptorActivityAccess() { using var server = FoobarService.Start(); - var channel = new Channel(server.UriString, ChannelCredentials.Insecure); + var channel = new Channel(server.Target, ChannelCredentials.Insecure); var callInvoker = channel.CreateCallInvoker(); // Activity has a parent @@ -441,7 +441,10 @@ private static async Task TestHandlerSuccess( // No Activity parent using (var activityListener = new InterceptorActivityListener(testTags)) { - var client = FoobarService.ConstructRpcClient(server.UriString, new ClientTracingInterceptor(interceptorOptions)); + var client = FoobarService + .ConstructRpcClient(server.Target, new ClientTracingInterceptor(interceptorOptions)) + .WithHost(server.Host); + await clientRequestFunc(client, additionalMetadata).ConfigureAwait(false); Assert.Equal(default, Activity.Current); @@ -475,6 +478,9 @@ private static async Task TestHandlerSuccess( interceptorOptions.RecordMessageEvents); Assert.Equal(default, activity.ParentSpanId); + + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeServerAddress && (string?)t.Value == server.HostName); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeServerPort && (int?)t.Value == server.Port); } propagatorCalled = 0; @@ -486,7 +492,11 @@ private static async Task TestHandlerSuccess( using var parentActivity = new Activity("foo"); parentActivity.SetIdFormat(ActivityIdFormat.W3C); parentActivity.Start(); - var client = FoobarService.ConstructRpcClient(server.UriString, new ClientTracingInterceptor(interceptorOptions)); + + var client = FoobarService + .ConstructRpcClient(server.Target, new ClientTracingInterceptor(interceptorOptions)) + .WithHost(server.Host); + await clientRequestFunc(client, additionalMetadata).ConfigureAwait(false); Assert.Equal(parentActivity, Activity.Current); @@ -508,6 +518,8 @@ private static async Task TestHandlerSuccess( Assert.NotNull(activity); Assert.Equal(parentActivity.Id, activity.ParentId); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeServerAddress && (string?)t.Value == server.HostName); + Assert.Contains(activity.TagObjects, t => t.Key == SemanticConventions.AttributeServerPort && (int?)t.Value == server.Port); } } @@ -542,7 +554,7 @@ private static async Task TestHandlerFailure( using (var server = FoobarService.Start()) { var client = FoobarService.ConstructRpcClient( - serverUriString ?? server.UriString, + serverUriString ?? server.Target, new ClientTracingInterceptor(interceptorOptions), [ new(FoobarService.RequestHeaderFailWithStatusCode, statusCode.ToString()), @@ -588,7 +600,7 @@ private void TestActivityIsCancelledWhenHandlerDisposed( AdditionalTags = testTags.Tags, }; - var client = FoobarService.ConstructRpcClient(server.UriString, new ClientTracingInterceptor(clientInterceptorOptions)); + var client = FoobarService.ConstructRpcClient(server.Target, new ClientTracingInterceptor(clientInterceptorOptions)); clientRequestAction(client); } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs index 26865f45da..9153f1a405 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs @@ -111,7 +111,7 @@ static Task HandleUnaryCall(NonProtobufPayload request, Serv }; server.Start(); - var serverUriString = new Uri("dns:localhost:" + server.Ports.Single().BoundPort).ToString(); + var serverUriString = $"dns:localhost:{server.Ports.Single().BoundPort}"; try { @@ -159,13 +159,19 @@ private static async Task TestHandlerSuccess( { // starts the server with the server interceptor var testTags = new TestActivityTags(); - var interceptorOptions = new ServerTracingInterceptorOptions { Propagator = new TraceContextPropagator(), RecordMessageEvents = true, AdditionalTags = testTags.Tags }; + var interceptorOptions = new ServerTracingInterceptorOptions + { + Propagator = new TraceContextPropagator(), + RecordMessageEvents = true, + AdditionalTags = testTags.Tags, + }; + using var server = FoobarService.Start(new ServerTracingInterceptor(interceptorOptions)); // No parent Activity, no context from header using (var activityListener = new InterceptorActivityListener(testTags)) { - var client = FoobarService.ConstructRpcClient(server.UriString); + var client = FoobarService.ConstructRpcClient(server.Target); await clientRequestFunc(client, additionalMetadata); var activity = activityListener.Activity; @@ -184,7 +190,7 @@ private static async Task TestHandlerSuccess( using (var activityListener = new InterceptorActivityListener(testTags)) { var client = FoobarService.ConstructRpcClient( - server.UriString, + server.Target, additionalMetadata: [new Metadata.Entry("traceparent", FoobarService.DefaultTraceparentWithSampling)]); await clientRequestFunc(client, additionalMetadata).ConfigureAwait(false); @@ -221,7 +227,7 @@ private static async Task TestHandlerFailure( using var activityListener = new InterceptorActivityListener(testTags); var client = FoobarService.ConstructRpcClient( - server.UriString, + server.Target, additionalMetadata: [ new Metadata.Entry("traceparent", FoobarService.DefaultTraceparentWithSampling), diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs index 9fd8c58dae..da36ca4b39 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/GrpcServer.cs @@ -19,7 +19,7 @@ internal sealed class GrpcServer : IAsyncDisposable public Uri Address { get => field ?? throw new InvalidOperationException("Server has not been started."); - set; + private set; } public async Task StartAsync() diff --git a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj index 4bbc41b716..15738810a4 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.GrpcNetClient.Tests/OpenTelemetry.Instrumentation.GrpcNetClient.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/test/Shared/TcpPortProvider.cs b/test/Shared/TcpPortProvider.cs index e1de08305b..2cea207834 100644 --- a/test/Shared/TcpPortProvider.cs +++ b/test/Shared/TcpPortProvider.cs @@ -7,7 +7,7 @@ namespace OpenTelemetry.Tests; /// -/// Helper class that tries to provide unique ports numbers across processes and threads in the same machine. +/// Helper class that tries to provide unique port numbers across processes and threads in the same machine. /// This class cannot guarantee a port is actually available, but should help avoid most conflicts. /// internal static class TcpPortProvider From cc1e3e1355178f7edbcbebdbed3ab1bbdf627231 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 5 May 2026 10:21:26 +0100 Subject: [PATCH 6/9] [Instrumentation.GrpcCore] Remove netX.0 support Address review feedback. --- src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md | 3 --- .../OpenTelemetry.Instrumentation.GrpcCore.csproj | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md index 97122a90df..8f25e04a9d 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/CHANGELOG.md @@ -8,9 +8,6 @@ * Add instrumentation scope version and schema URL to traces. ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) -* Add `net8.0` and `net10.0` target frameworks. - ([#4338](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4338)) - ## 1.0.0-beta.11 Released 2026-Apr-21 diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj index 48393e198f..41ce979cfb 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/OpenTelemetry.Instrumentation.GrpcCore.csproj @@ -1,7 +1,7 @@ - $(TargetFrameworksForLibraries) + $(NetStandardMinimumSupportedVersion) .NET gRPC Core based client and server interceptors for OpenTelemetry. $(PackageTags);gRPC Core;interceptors Instrumentation.GrpcCore- From 0ff784a00a8b6d2a2a3300a8dc10f35aa36bbb51 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 5 May 2026 10:51:07 +0100 Subject: [PATCH 7/9] [Instrumentation.GrpcCore] Test on net4xx - Test the library actually works on .NET Framework. - Fix test failures. --- .../ClientTracingInterceptor.cs | 12 ++++++++++-- ...enTelemetry.Instrumentation.GrpcCore.Tests.csproj | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs index 0c9b0205aa..0922f9e4df 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/ClientTracingInterceptor.cs @@ -321,10 +321,11 @@ public ClientRpcScope(ClientInterceptorContext context, Cli // the callers current Activity which isn't what we want. We need to restore the original immediately after doing this. // If this call happened after some kind of async context await then a restore wouldn't be necessary. // gRPC Core just doesn't have the hooks to do this as far as I can tell. - var rpcActivity = GrpcCoreInstrumentation.ActivitySource.StartActivity( + var parentContext = this.parentActivity?.Context ?? default; + var rpcActivity = GrpcCoreInstrumentation.ActivitySource.CreateActivity( this.FullServiceName, ActivityKind.Client, - this.parentActivity == default ? default : this.parentActivity.Context, + parentContext, tags: options.AdditionalTags); if (rpcActivity == null) @@ -332,6 +333,13 @@ public ClientRpcScope(ClientInterceptorContext context, Cli return; } + if (!parentContext.IsValid()) + { + rpcActivity.SetIdFormat(ActivityIdFormat.W3C); + } + + rpcActivity.Start(); + var callOptions = context.Options; // Do NOT mutate incoming call headers, make a new copy. diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/OpenTelemetry.Instrumentation.GrpcCore.Tests.csproj b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/OpenTelemetry.Instrumentation.GrpcCore.Tests.csproj index 6739b13339..8428d96538 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/OpenTelemetry.Instrumentation.GrpcCore.Tests.csproj +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/OpenTelemetry.Instrumentation.GrpcCore.Tests.csproj @@ -2,6 +2,7 @@ $(SupportedNetTargets) + $(TargetFrameworks);$(NetFrameworkMinimumSupportedVersion) From 1d0992025e686552a5e02eacf8150fe21b1591f7 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 5 May 2026 10:59:59 +0100 Subject: [PATCH 8/9] [OpAMP] Fix flaky test Handle `ApplicationException` for an invalid handle on .NET Framework. --- test/Shared/TestWebSocketServer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Shared/TestWebSocketServer.cs b/test/Shared/TestWebSocketServer.cs index ee96dcf2d6..5c175386dd 100644 --- a/test/Shared/TestWebSocketServer.cs +++ b/test/Shared/TestWebSocketServer.cs @@ -100,10 +100,10 @@ private bool IsListenerShutdownException(Exception ex) // genuine failure that happens to surface as code 1 return ex is ObjectDisposedException || (ex is HttpListenerException httpEx - && (httpEx.ErrorCode == 995 - || httpEx.ErrorCode == 6 - || (httpEx.ErrorCode == 1 && !this.listener.IsListening))) - || (ex is InvalidOperationException && !this.listener.IsListening); + && (httpEx.ErrorCode is 6 or 995 || + (httpEx.ErrorCode == 1 && !this.listener.IsListening))) + || (ex is InvalidOperationException && !this.listener.IsListening) + || (ex is ApplicationException appEx && (uint)appEx.HResult == 0x80070006); // The handle is invalid. } private async Task ListenAsync(Func handler) From 6fca15ac51f3dff51e0d282bccd270f3993b27c6 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 5 May 2026 12:13:56 +0100 Subject: [PATCH 9/9] [Instrumentation.GrpcCore] Address feedback - Use `Uri.TryParse()` to parse host and port. - Move parsing to its own method. - Add constants for client method names. --- .../RpcScope.cs | 30 +++++++++-------- .../FoobarService.cs | 8 +++++ .../GrpcCoreClientInterceptorTests.cs | 32 +++++++++---------- .../GrpcCoreServerInterceptorTests.cs | 18 +++++------ .../NonProtobufGrpcTestHelpers.cs | 2 +- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs index d2c26c3051..bf62eb5422 100644 --- a/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs +++ b/src/OpenTelemetry.Instrumentation.GrpcCore/RpcScope.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 using System.Diagnostics; -using System.Globalization; using Google.Protobuf; using Grpc.Core; using OpenTelemetry.Internal; @@ -194,23 +193,28 @@ protected void SetActivity(Activity? activity) if (this.host is { Length: > 0 } host) { - parts = host.Split(':'); - - if (parts.Length > 0) - { - this.activity.SetTag(SemanticConventions.AttributeServerAddress, parts[0]); - - if (parts.Length > 1 && - int.TryParse(parts[1], NumberStyles.None, CultureInfo.InvariantCulture, out var port)) - { - this.activity.SetTag(SemanticConventions.AttributeServerPort, port); - } - } + TrySetServerAttributes(this.activity, host); } this.activity.DisplayName = rpcMethod.Trim('/'); } + private static void TrySetServerAttributes(Activity activity, string host) + { + // Add a placeholder for the scheme so the parse succeeds. The value is not important. + if (!Uri.TryCreate($"http://{host}", UriKind.Absolute, out var uri)) + { + return; + } + + activity.SetTag(SemanticConventions.AttributeServerAddress, uri.Host); + + if (!uri.IsDefaultPort) + { + activity.SetTag(SemanticConventions.AttributeServerPort, uri.Port); + } + } + /// /// Stops the activity. /// diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs index e028301d2d..11fb4bb858 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/FoobarService.cs @@ -12,6 +12,14 @@ namespace OpenTelemetry.Instrumentation.GrpcCore.Tests; /// internal class FoobarService : Foobar.FoobarBase { + public const string UnaryMethod = nameof(Foobar.FoobarClient.Unary); + + public const string ClientStreamingMethod = nameof(Foobar.FoobarClient.ClientStreaming); + + public const string ServerStreamingMethod = nameof(Foobar.FoobarClient.ServerStreaming); + + public const string DuplexStreamingMethod = nameof(Foobar.FoobarClient.DuplexStreaming); + /// /// Default traceparent header value with the sampling bit on. /// diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs index e5a6083914..79556c2b55 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreClientInterceptorTests.cs @@ -35,7 +35,7 @@ public class GrpcCoreClientInterceptorTests public async Task AsyncUnarySuccess() => await TestHandlerSuccess( FoobarService.MakeUnaryAsyncRequest, - "Unary", + FoobarService.UnaryMethod, DefaultMetadataFunc()); /// @@ -46,7 +46,7 @@ await TestHandlerSuccess( public async Task AsyncUnaryUnavailable() => await TestHandlerFailure( FoobarService.MakeUnaryAsyncRequest, - "Unary", + FoobarService.UnaryMethod, StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -57,7 +57,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task AsyncUnaryFail() => - await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, "Unary"); + await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, FoobarService.UnaryMethod); /// /// Validates a failed AsyncUnary call because the client is disposed before completing the RPC. @@ -70,7 +70,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.UnaryAsync(FoobarService.DefaultRequestMessage); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "Unary"); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, FoobarService.UnaryMethod); } /// @@ -79,7 +79,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task ClientStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, "ClientStreaming", DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, FoobarService.ClientStreamingMethod, DefaultMetadataFunc()); /// /// Validates a failed ClientStreaming call when the service is unavailable. @@ -89,7 +89,7 @@ public async Task ClientStreamingSuccess() => public async Task ClientStreamingUnavailable() => await TestHandlerFailure( FoobarService.MakeClientStreamingRequest, - "ClientStreaming", + FoobarService.ClientStreamingMethod, StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -100,7 +100,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task ClientStreamingFail() => - await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); + await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, FoobarService.ClientStreamingMethod); /// /// Validates a failed ClientStreaming call because the client is disposed before completing the RPC. @@ -113,7 +113,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.ClientStreaming(); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "ClientStreaming"); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, FoobarService.ClientStreamingMethod); } /// @@ -122,7 +122,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task ServerStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, "ServerStreaming", DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, FoobarService.ServerStreamingMethod, DefaultMetadataFunc()); /// /// Validates a failed ServerStreaming call. @@ -130,7 +130,7 @@ public async Task ServerStreamingSuccess() => /// A task. [Fact] public async Task ServerStreamingFail() => - await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); + await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, FoobarService.ServerStreamingMethod); /// /// Validates a failed ServerStreaming call because the client is disposed before completing the RPC. @@ -143,7 +143,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.ServerStreaming(FoobarService.DefaultRequestMessage); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "ServerStreaming"); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, FoobarService.ServerStreamingMethod); } /// @@ -152,7 +152,7 @@ static void MakeRequest(Foobar.FoobarClient client) /// A task. [Fact] public async Task DuplexStreamingSuccess() => - await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming", DefaultMetadataFunc()); + await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, FoobarService.DuplexStreamingMethod, DefaultMetadataFunc()); /// /// Validates a failed DuplexStreaming call when the service is unavailable. @@ -162,7 +162,7 @@ public async Task DuplexStreamingSuccess() => public async Task DuplexStreamingUnavailable() => await TestHandlerFailure( FoobarService.MakeDuplexStreamingRequest, - "DuplexStreaming", + FoobarService.DuplexStreamingMethod, StatusCode.Unavailable, validateErrorDescription: false, BogusServerUri); @@ -173,7 +173,7 @@ await TestHandlerFailure( /// A task. [Fact] public async Task DuplexStreamingFail() => - await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); + await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, FoobarService.DuplexStreamingMethod); /// /// Validates a failed DuplexStreaming call because the client is disposed before completing the RPC. @@ -186,7 +186,7 @@ static void MakeRequest(Foobar.FoobarClient client) using var call = client.DuplexStreaming(); } - this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, "DuplexStreaming"); + this.TestActivityIsCancelledWhenHandlerDisposed(MakeRequest, FoobarService.DuplexStreamingMethod); } /// @@ -316,7 +316,7 @@ public void BlockingUnaryCallWithNonProtobufPayloadDoesNotThrowWhenRecordingMess Assert.NotNull(response); var activity = activityListener.Activity; - ValidateCommonActivityTags(activity, "Unary"); + ValidateCommonActivityTags(activity, FoobarService.UnaryMethod); Assert.Empty(activity!.Events); } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs index 9153f1a405..5961f6636e 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/GrpcCoreServerInterceptorTests.cs @@ -19,7 +19,7 @@ public class GrpcCoreServerInterceptorTests /// A task. [Fact] public async Task UnaryServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeUnaryAsyncRequest, "Unary"); + await TestHandlerSuccess(FoobarService.MakeUnaryAsyncRequest, FoobarService.UnaryMethod); /// /// Validates a failed UnaryServerHandler call. @@ -27,7 +27,7 @@ public async Task UnaryServerHandlerSuccess() => /// A task. [Fact] public async Task UnaryServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, "Unary"); + await TestHandlerFailure(FoobarService.MakeUnaryAsyncRequest, FoobarService.UnaryMethod); /// /// Validates a successful ClientStreamingServerHandler call. @@ -35,7 +35,7 @@ public async Task UnaryServerHandlerFail() => /// A task. [Fact] public async Task ClientStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); + await TestHandlerSuccess(FoobarService.MakeClientStreamingRequest, FoobarService.ClientStreamingMethod); /// /// Validates a failed ClientStreamingServerHandler call. @@ -43,7 +43,7 @@ public async Task ClientStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task ClientStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, "ClientStreaming"); + await TestHandlerFailure(FoobarService.MakeClientStreamingRequest, FoobarService.ClientStreamingMethod); /// /// Validates a successful ServerStreamingServerHandler call. @@ -51,7 +51,7 @@ public async Task ClientStreamingServerHandlerFail() => /// A task. [Fact] public async Task ServerStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); + await TestHandlerSuccess(FoobarService.MakeServerStreamingRequest, FoobarService.ServerStreamingMethod); /// /// Validates a failed ServerStreamingServerHandler call. @@ -59,7 +59,7 @@ public async Task ServerStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task ServerStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, "ServerStreaming"); + await TestHandlerFailure(FoobarService.MakeServerStreamingRequest, FoobarService.ServerStreamingMethod); /// /// Validates a successful DuplexStreamingServerHandler call. @@ -67,7 +67,7 @@ public async Task ServerStreamingServerHandlerFail() => /// A task. [Fact] public async Task DuplexStreamingServerHandlerSuccess() => - await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); + await TestHandlerSuccess(FoobarService.MakeDuplexStreamingRequest, FoobarService.DuplexStreamingMethod); /// /// Validates a failed DuplexStreamingServerHandler call. @@ -75,7 +75,7 @@ public async Task DuplexStreamingServerHandlerSuccess() => /// A task. [Fact] public async Task DuplexStreamingServerHandlerFail() => - await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, "DuplexStreaming"); + await TestHandlerFailure(FoobarService.MakeDuplexStreamingRequest, FoobarService.DuplexStreamingMethod); /// /// Validates that non protobuf payloads do not abort server RPCs when @@ -130,7 +130,7 @@ static Task HandleUnaryCall(NonProtobufPayload request, Serv var activity = activityListener.Activity; - GrpcCoreClientInterceptorTests.ValidateCommonActivityTags(activity, "Unary"); + GrpcCoreClientInterceptorTests.ValidateCommonActivityTags(activity, FoobarService.UnaryMethod); Assert.Empty(activity!.Events); } diff --git a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/NonProtobufGrpcTestHelpers.cs b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/NonProtobufGrpcTestHelpers.cs index 026343f342..57001ec8f4 100644 --- a/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/NonProtobufGrpcTestHelpers.cs +++ b/test/OpenTelemetry.Instrumentation.GrpcCore.Tests/NonProtobufGrpcTestHelpers.cs @@ -14,7 +14,7 @@ internal static class NonProtobufGrpcTestHelpers new( MethodType.Unary, "OpenTelemetry.Instrumentation.GrpcCore.Tests.Foobar", - "Unary", + FoobarService.UnaryMethod, PayloadMarshaller, PayloadMarshaller); }