diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9441d40269..f2e67d4e55 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -45,6 +45,7 @@ body: - OpenTelemetry.Instrumentation.Process - OpenTelemetry.Instrumentation.Quartz - OpenTelemetry.Instrumentation.Runtime + - OpenTelemetry.Instrumentation.ServiceFabricRemoting - OpenTelemetry.Instrumentation.SqlClient - OpenTelemetry.Instrumentation.StackExchangeRedis - OpenTelemetry.Instrumentation.Wcf diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 0443264723..5461bf353a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -45,6 +45,7 @@ body: - OpenTelemetry.Instrumentation.Process - OpenTelemetry.Instrumentation.Quartz - OpenTelemetry.Instrumentation.Runtime + - OpenTelemetry.Instrumentation.ServiceFabricRemoting - OpenTelemetry.Instrumentation.SqlClient - OpenTelemetry.Instrumentation.StackExchangeRedis - OpenTelemetry.Instrumentation.Wcf diff --git a/.github/ISSUE_TEMPLATE/release_request.yml b/.github/ISSUE_TEMPLATE/release_request.yml index d7bfea1b5c..b219f533ff 100644 --- a/.github/ISSUE_TEMPLATE/release_request.yml +++ b/.github/ISSUE_TEMPLATE/release_request.yml @@ -43,6 +43,7 @@ body: - OpenTelemetry.Instrumentation.Process - OpenTelemetry.Instrumentation.Quartz - OpenTelemetry.Instrumentation.Runtime + - OpenTelemetry.Instrumentation.ServiceFabricRemoting - OpenTelemetry.Instrumentation.SqlClient - OpenTelemetry.Instrumentation.StackExchangeRedis - OpenTelemetry.Instrumentation.Wcf diff --git a/.github/codecov.yml b/.github/codecov.yml index ae835620c7..b94a66eb52 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -150,6 +150,11 @@ flags: paths: - src/OpenTelemetry.Instrumentation.Runtime + unittests-Instrumentation.ServiceFabricRemoting: + carryforward: true + paths: + - src/OpenTelemetry.Instrumentation.ServiceFabricRemoting + unittests-Instrumentation.SqlClient: carryforward: true paths: diff --git a/.github/component_owners.yml b/.github/component_owners.yml index cbd6deb94c..b140db27bc 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -56,6 +56,8 @@ components: src/OpenTelemetry.Instrumentation.Runtime/: - twenzel - xiang17 + src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/: + - sablancoleis src/OpenTelemetry.Instrumentation.Wcf/: - codeblanch src/OpenTelemetry.PersistentStorage.Abstractions/: @@ -149,6 +151,8 @@ components: test/OpenTelemetry.Instrumentation.Runtime.Tests/: - twenzel - xiang17 + test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/: + - sablancoleis test/OpenTelemetry.Instrumentation.Wcf.Tests/: - codeblanch test/OpenTelemetry.PersistentStorage.FileSystem.Tests/: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0cb55915b..7f7318acb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: instrumentation-process: ['*/OpenTelemetry.Instrumentation.Process*/**', 'examples/process-instrumentation/**', '!**/*.md'] instrumentation-quartz: ['*/OpenTelemetry.Instrumentation.Quartz*/**', '!**/*.md'] instrumentation-runtime: ['*/OpenTelemetry.Instrumentation.Runtime*/**', 'examples/runtime-instrumentation/**', '!**/*.md'] + instrumentation-servicefabricremoting: ['*/OpenTelemetry.Instrumentation.ServiceFabricRemoting*/**', '!**/*.md'] instrumentation-sqlclient: ['*/OpenTelemetry.Instrumentation.SqlClient*/**', '!**/*.md'] instrumentation-stackexchangeredis: ['*/OpenTelemetry.Instrumentation.StackExchangeRedis*/**', 'examples/redis/**', '!**/*.md'] instrumentation-wcf: ['*/OpenTelemetry.Instrumentation.Wcf*/**', 'examples/wcf/**', '!**/*.md'] @@ -371,6 +372,17 @@ jobs: project-name: OpenTelemetry.Instrumentation.Runtime code-cov-name: Instrumentation.Runtime + build-test-instrumentation-servicefabricremoting: + needs: detect-changes + if: | + contains(needs.detect-changes.outputs.changes, 'instrumentation-servicefabricremoting') + || contains(needs.detect-changes.outputs.changes, 'build') + || contains(needs.detect-changes.outputs.changes, 'shared') + uses: ./.github/workflows/Component.BuildTest.yml + with: + project-name: Component[OpenTelemetry.Instrumentation.ServiceFabricRemoting] + code-cov-name: Instrumentation.ServiceFabricRemoting + build-test-instrumentation-sqlclient: needs: detect-changes if: | @@ -563,6 +575,7 @@ jobs: || contains(needs.detect-changes.outputs.changes, 'instrumentation-grpcnetclient') || contains(needs.detect-changes.outputs.changes, 'instrumentation-http') || contains(needs.detect-changes.outputs.changes, 'instrumentation-runtime') + || contains(needs.detect-changes.outputs.changes, 'instrumentation-servicefabricremoting') || contains(needs.detect-changes.outputs.changes, 'instrumentation-sqlclient') || contains(needs.detect-changes.outputs.changes, 'instrumentation-stackexchangeredis') || contains(needs.detect-changes.outputs.changes, 'resources-aws') @@ -610,6 +623,7 @@ jobs: build-test-instrumentation-process, build-test-instrumentation-quartz, build-test-instrumentation-runtime, + build-test-instrumentation-servicefabricremoting, build-test-instrumentation-sqlclient, build-test-instrumentation-stackexchangeredis, build-test-instrumentation-stackexchangeredis-integration, diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index bda2707485..dc84ca1068 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -34,6 +34,7 @@ on: - OpenTelemetry.Instrumentation.Process - OpenTelemetry.Instrumentation.Quartz - OpenTelemetry.Instrumentation.Runtime + - OpenTelemetry.Instrumentation.ServiceFabricRemoting - OpenTelemetry.Instrumentation.SqlClient - OpenTelemetry.Instrumentation.StackExchangeRedis - OpenTelemetry.Instrumentation.Wcf diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln index a67e744eb3..abcb4727f6 100644 --- a/opentelemetry-dotnet-contrib.sln +++ b/opentelemetry-dotnet-contrib.sln @@ -400,6 +400,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kafka", "kafka", "{3A464E7A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.ConfluentKafka", "examples\kafka\Examples.ConfluentKafka.csproj", "{9B994669-E839-4C42-A0F1-DF9DD058C1DC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.ServiceFabricRemoting", "src\OpenTelemetry.Instrumentation.ServiceFabricRemoting\OpenTelemetry.Instrumentation.ServiceFabricRemoting.csproj", "{91BA4FF0-50BC-49D0-976B-CBC9E5FE8337}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests", "test\OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests\OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests.csproj", "{00C9F0B8-3D51-483C-821A-5889B38A144C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -822,6 +826,14 @@ Global {9B994669-E839-4C42-A0F1-DF9DD058C1DC}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B994669-E839-4C42-A0F1-DF9DD058C1DC}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B994669-E839-4C42-A0F1-DF9DD058C1DC}.Release|Any CPU.Build.0 = Release|Any CPU + {91BA4FF0-50BC-49D0-976B-CBC9E5FE8337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91BA4FF0-50BC-49D0-976B-CBC9E5FE8337}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91BA4FF0-50BC-49D0-976B-CBC9E5FE8337}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91BA4FF0-50BC-49D0-976B-CBC9E5FE8337}.Release|Any CPU.Build.0 = Release|Any CPU + {00C9F0B8-3D51-483C-821A-5889B38A144C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00C9F0B8-3D51-483C-821A-5889B38A144C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00C9F0B8-3D51-483C-821A-5889B38A144C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00C9F0B8-3D51-483C-821A-5889B38A144C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -948,6 +960,8 @@ Global {BE40900A-2859-471D-8802-21DFD73DDAA7} = {2097345F-4DD3-477D-BC54-A922F9B2B402} {3A464E7A-42F3-44B0-B8D7-80521A7704A6} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA} {9B994669-E839-4C42-A0F1-DF9DD058C1DC} = {3A464E7A-42F3-44B0-B8D7-80521A7704A6} + {91BA4FF0-50BC-49D0-976B-CBC9E5FE8337} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63} + {00C9F0B8-3D51-483C-821A-5889B38A144C} = {2097345F-4DD3-477D-BC54-A922F9B2B402} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Shipped.txt b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Shipped.txt new file mode 100644 index 0000000000..7dc5c58110 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Unshipped.txt b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000..dae70bf6c4 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/.publicApi/PublicAPI.Unshipped.txt @@ -0,0 +1,37 @@ +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.AddExceptionAtClient.get -> bool +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.AddExceptionAtClient.set -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.AddExceptionAtServer.get -> bool +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.AddExceptionAtServer.set -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.EnrichAtClientFromRequest.get -> System.Action? +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.EnrichAtClientFromRequest.set -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.EnrichAtClientFromResponse.get -> System.Action? +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.EnrichAtClientFromResponse.set -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.Filter.get -> System.Func? +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.Filter.set -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceFabricRemotingInstrumentationOptions.ServiceFabricRemotingInstrumentationOptions() -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter.Dispose() -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter.GetRemotingMessageBodyFactory() -> Microsoft.ServiceFabric.Services.Remoting.V2.IServiceRemotingMessageBodyFactory! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter.HandleOneWayMessage(Microsoft.ServiceFabric.Services.Remoting.V2.IServiceRemotingRequestMessage! requestMessage) -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter.HandleRequestResponseAsync(Microsoft.ServiceFabric.Services.Remoting.V2.Runtime.IServiceRemotingRequestContext! requestContext, Microsoft.ServiceFabric.Services.Remoting.V2.IServiceRemotingRequestMessage! requestMessage) -> System.Threading.Tasks.Task! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.ServiceRemotingMessageDispatcherAdapter.ServiceRemotingMessageDispatcherAdapter(Microsoft.ServiceFabric.Services.Remoting.V2.Runtime.IServiceRemotingMessageHandler! dispatcher) -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedActorRemotingProviderAttribute +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedActorRemotingProviderAttribute.TraceContextEnrichedActorRemotingProviderAttribute() -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.ClientConnected -> System.EventHandler!>! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.ClientDisconnected -> System.EventHandler!>! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.GetClientAsync(System.Fabric.ResolvedServicePartition! previousRsp, Microsoft.ServiceFabric.Services.Communication.Client.TargetReplicaSelector targetReplicaSelector, string! listenerName, Microsoft.ServiceFabric.Services.Communication.Client.OperationRetrySettings! retrySettings, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.GetClientAsync(System.Uri! serviceUri, Microsoft.ServiceFabric.Services.Client.ServicePartitionKey! partitionKey, Microsoft.ServiceFabric.Services.Communication.Client.TargetReplicaSelector targetReplicaSelector, string! listenerName, Microsoft.ServiceFabric.Services.Communication.Client.OperationRetrySettings! retrySettings, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.GetRemotingMessageBodyFactory() -> Microsoft.ServiceFabric.Services.Remoting.V2.IServiceRemotingMessageBodyFactory! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.ReportOperationExceptionAsync(Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingClient! client, Microsoft.ServiceFabric.Services.Communication.Client.ExceptionInformation! exceptionInformation, Microsoft.ServiceFabric.Services.Communication.Client.OperationRetrySettings! retrySettings, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingClientFactoryAdapter.TraceContextEnrichedServiceRemotingClientFactoryAdapter(Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingClientFactory! serviceRemotingClientFactory) -> void +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingProviderAttribute +OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingProviderAttribute.TraceContextEnrichedServiceRemotingProviderAttribute() -> void +OpenTelemetry.Trace.TracerProviderBuilderExtensions +override OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedActorRemotingProviderAttribute.CreateServiceRemotingClientFactory(Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingCallbackMessageHandler? callbackMessageHandler) -> Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingClientFactory! +override OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedActorRemotingProviderAttribute.CreateServiceRemotingListeners() -> System.Collections.Generic.Dictionary!>! +override OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingProviderAttribute.CreateServiceRemotingClientFactoryV2(Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingCallbackMessageHandler? callbackMessageHandler) -> Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingClientFactory! +override OpenTelemetry.Instrumentation.ServiceFabricRemoting.TraceContextEnrichedServiceRemotingProviderAttribute.CreateServiceRemotingListeners() -> System.Collections.Generic.Dictionary!>! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddServiceFabricRemotingInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! builder) -> OpenTelemetry.Trace.TracerProviderBuilder! +static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddServiceFabricRemotingInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder! tracerProviderBuilder, System.Action? configure) -> OpenTelemetry.Trace.TracerProviderBuilder! diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ActorRemoting/TraceContextEnrichedActorRemotingProviderAttribute.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ActorRemoting/TraceContextEnrichedActorRemotingProviderAttribute.cs new file mode 100644 index 0000000000..3eed71be42 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ActorRemoting/TraceContextEnrichedActorRemotingProviderAttribute.cs @@ -0,0 +1,123 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Actors.Generator; +using Microsoft.ServiceFabric.Actors.Remoting.FabricTransport; +using Microsoft.ServiceFabric.Actors.Remoting.V2.FabricTransport.Client; +using Microsoft.ServiceFabric.Actors.Remoting.V2.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Actors.Remoting.V2.Runtime; +using Microsoft.ServiceFabric.Actors.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.Client; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// Sets fabric TCP transport as the default remoting provider for the actors. +/// +[AttributeUsage(AttributeTargets.Assembly)] +public sealed class TraceContextEnrichedActorRemotingProviderAttribute : FabricTransportActorRemotingProviderAttribute +{ + private const string DefaultV2listenerName = "V2Listener"; + + /// + /// Initializes a new instance of the class. + /// + public TraceContextEnrichedActorRemotingProviderAttribute() + { + this.RemotingClientVersion = Microsoft.ServiceFabric.Services.Remoting.RemotingClientVersion.V2; + this.RemotingListenerVersion = Microsoft.ServiceFabric.Services.Remoting.RemotingListenerVersion.V2; + } + + /// + /// Creates a service remoting listener for remoting the actor interfaces. + /// . + /// + /// A as for the specified actor service. + /// + public override Dictionary> CreateServiceRemotingListeners() + { + Dictionary> dictionary = new Dictionary>(); + + dictionary.Add(DefaultV2listenerName, (actorService) => + { + ActorServiceRemotingDispatcher actorServiceRemotingDispatcher = new ActorServiceRemotingDispatcher(actorService, serviceRemotingRequestMessageBodyFactory: null); + ServiceRemotingMessageDispatcherAdapter dispatcherAdapter = new ServiceRemotingMessageDispatcherAdapter(actorServiceRemotingDispatcher); + FabricTransportRemotingListenerSettings listenerSettings = this.InitializeListenerSettings(actorService); + + return new FabricTransportActorServiceRemotingListener(actorService, dispatcherAdapter, listenerSettings); + }); + + return dictionary; + } + + /// + /// Creates a service remoting client factory that can be used by the Microsoft.ServiceFabric.Services.Remoting.V2.Client.ServiceProxyFactory + /// to create a proxy for the remoted interface of the service. + /// + /// Client implementation where the callbacks should be dispatched. + /// An . + public override IServiceRemotingClientFactory CreateServiceRemotingClientFactory(IServiceRemotingCallbackMessageHandler? callbackMessageHandler) + { + FabricTransportRemotingSettings settings = new FabricTransportRemotingSettings(); + settings.MaxMessageSize = this.GetAndValidateMaxMessageSize(settings.MaxMessageSize); + settings.OperationTimeout = this.GetAndValidateOperationTimeout(settings.OperationTimeout); + settings.KeepAliveTimeout = this.GetAndValidateKeepAliveTimeout(settings.KeepAliveTimeout); + settings.ConnectTimeout = this.GetConnectTimeout(settings.ConnectTimeout); + + FabricTransportActorRemotingClientFactory fabricTransportActorRemotingClientFactory = new FabricTransportActorRemotingClientFactory( + settings, + callbackMessageHandler, + servicePartitionResolver: null, + exceptionHandlers: null, + traceId: null); + + return new TraceContextEnrichedServiceRemotingClientFactoryAdapter(fabricTransportActorRemotingClientFactory); + } + + private static FabricTransportRemotingListenerSettings GetActorListenerSettings(ActorService actorService) + { + string sectionName = ActorNameFormat.GetFabricServiceTransportSettingsSectionName(actorService.ActorTypeInformation.ImplementationType); + + bool isSucceded = FabricTransportRemotingListenerSettings.TryLoadFrom(sectionName, out FabricTransportRemotingListenerSettings listenerSettings); + if (!isSucceded) + { + listenerSettings = new FabricTransportRemotingListenerSettings(); + } + + return listenerSettings; + } + + private FabricTransportRemotingListenerSettings InitializeListenerSettings(ActorService actorService) + { + FabricTransportRemotingListenerSettings listenerSettings = GetActorListenerSettings(actorService); + + listenerSettings.MaxMessageSize = this.GetAndValidateMaxMessageSize(listenerSettings.MaxMessageSize); + listenerSettings.OperationTimeout = this.GetAndValidateOperationTimeout(listenerSettings.OperationTimeout); + listenerSettings.KeepAliveTimeout = this.GetAndValidateKeepAliveTimeout(listenerSettings.KeepAliveTimeout); + + return listenerSettings; + } + + private long GetAndValidateMaxMessageSize(long maxMessageSizeDefault) + { + return (this.MaxMessageSize > 0) ? this.MaxMessageSize : maxMessageSizeDefault; + } + + private TimeSpan GetAndValidateOperationTimeout(TimeSpan operationTimeoutDefault) + { + return (this.OperationTimeoutInSeconds > 0) ? TimeSpan.FromSeconds(this.OperationTimeoutInSeconds) : operationTimeoutDefault; + } + + private TimeSpan GetAndValidateKeepAliveTimeout(TimeSpan keepAliveTimeoutDefault) + { + return (this.KeepAliveTimeoutInSeconds > 0) ? TimeSpan.FromSeconds(this.KeepAliveTimeoutInSeconds) : keepAliveTimeoutDefault; + } + + private TimeSpan GetConnectTimeout(TimeSpan connectTimeoutDefault) + { + return (this.ConnectTimeoutInMilliseconds > 0) ? TimeSpan.FromMilliseconds(this.ConnectTimeoutInMilliseconds) : connectTimeoutDefault; + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/AssemblyInfo.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/AssemblyInfo.cs new file mode 100644 index 0000000000..f35369a78b --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/AssemblyInfo.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Runtime.CompilerServices; + +[assembly: CLSCompliant(false)] +#if SIGNED +[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")] +#else +[assembly: InternalsVisibleTo("OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests")] +#endif diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/CHANGELOG.md new file mode 100644 index 0000000000..5e61f8e249 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +* Initial release of `OpenTelemetry.Instrumentation.ServiceFabricRemoting` library. diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/OpenTelemetry.Instrumentation.ServiceFabricRemoting.csproj b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/OpenTelemetry.Instrumentation.ServiceFabricRemoting.csproj new file mode 100644 index 0000000000..32851ef8da --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/OpenTelemetry.Instrumentation.ServiceFabricRemoting.csproj @@ -0,0 +1,34 @@ + + + + + $(NetStandardMinimumSupportedVersion) + ServiceFabric Remoting instrumentation for OpenTelemetry .NET. + $(PackageTags);distributed-tracing + Instrumentation.ServiceFabricRemoting- + + + + + true + + + + + None + + + + + + + + + + + + + + + diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/README.md b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/README.md new file mode 100644 index 0000000000..a44deb1753 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/README.md @@ -0,0 +1,195 @@ +# Service Fabric Remoting Instrumentation for OpenTelemetry .NET + +| Status | | +| ------------- |-----------| +| Stability | [Beta](../../README.md#beta) | +| Code Owners | [@sablancoleis](https://github.com/sablancoleis) | + +[![NuGet version badge](https://img.shields.io/nuget/v/OpenTelemetry.Instrumentation.ServiceFabricRemoting)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.ServiceFabricRemoting) +[![NuGet download count badge](https://img.shields.io/nuget/dt/OpenTelemetry.Instrumentation.ServiceFabricRemoting)](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.ServiceFabricRemoting) +[![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-dotnet-contrib/branch/main/graphs/badge.svg?flag=unittests-Instrumentation.ServiceFabricRemoting)](https://app.codecov.io/gh/open-telemetry/opentelemetry-dotnet-contrib?flags[0]=unittests-Instrumentation.ServiceFabricRemoting) + +This is an [Instrumentation Library](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/glossary.md#instrumentation-library), +which instruments [Service Fabric Remoting](https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting) +and collects telemetry about incoming requests. + +## Steps to enable OpenTelemetry.Instrumentation.ServiceFabricRemoting + +### Step 1: Install Package + +Add a reference to the +[`OpenTelemetry.Instrumentation.ServiceFabricRemoting`](https://www.nuget.org/packages/OpenTelemetry.Instrumentation.ServiceFabricRemoting) +package. Also, add any other instrumentations & exporters you will need. + +```shell +dotnet add package OpenTelemetry.Instrumentation.ServiceFabricRemoting +``` + +### Step 2: Configure SF Remoting with Distributed Tracing instrumentation + +These instructions are a moified vertion of the steps mentioned here: +[`Use an assembly attribute to use the V2 stack`](https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-remoting#use-an-assembly-attribute-to-use-the-v2-stack) + +a) Change the endpoint resource from "ServiceEndpoint" to "ServiceEndpointV2" + in the service manifest. + +```xml + + + + + +``` + +b) Use the Microsoft.ServiceFabric.Services.Remoting.Runtime +.CreateServiceRemotingInstanceListeners +extension method to create remoting listeners. + +```csharp + protected override IEnumerable CreateServiceInstanceListeners() + { + return this.CreateServiceRemotingInstanceListeners(); + } +``` + +c) Mark the assembly that contains the remoting interfaces with a +**TraceContextEnrichedActorRemotingProvider** and/or a +**TraceContextEnrichedServiceRemotingProvider** attribute. +*Note that these attributes are not part of the Service Fabric SDK +and are provided by the OpenTelemetry.Instrumentation.ServiceFabricRemoting package.** + +```csharp + [assembly: TraceContextEnrichedActorRemotingProvider()] + [assembly: TraceContextEnrichedServiceRemotingProvider()] +``` + +### Step 3: Enable SF Remoting Instrumentation + +#### Configure OpenTelemetry TracerProvider in Program.cs + +Call the `AddServiceFabricRemotingInstrumentation` extension method on the +`TracerProviderBuilder` to register the OpenTelemetry instrumentation. + +```csharp + using TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation() + .AddOtlpExporter() + .Build(); +``` + +This will register the `ServiceFabric.Remoting` ActivitySource and create +spans for incoming and outgoing remoting calls. + +It will also automatically inject the trace context and Baggage into the +remoting headers, and extract them on the receiving side. + +#### Configuration options + +The `AddServiceFabricRemotingInstrumentation` extension method takes an optional +`ServiceFabricRemotingInstrumentationOptions` parameter that offers the following +configurable settings: + +- **Filter** - A filter function that can be used to exclude certain remoting +calls from being instrumented. +The function takes a `ServiceRemotingRequest` and returns a boolean value. +If the function returns `true`, the remoting call will be instrumented. +If the function returns `false`, the remoting call will not be instrumented. +By default, all remoting calls are instrumented. + +```csharp + TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation(options => + { + options.Filter = requestMessage => + { + // Exclude remoting calls to a specific method + IServiceRemotingRequestMessageHeader requestMessageHeader = requestMessage?.GetHeader(); + if (requestMessageHeader?.MethodName == "SomeMethodToIgnore") + { + return false; + } + return true; + }; + }) + .AddOtlpExporter() + .Build(); +``` + +- **EnrichAtClientFromRequest** - A function that can be used to enrich the span +created for the client side of the remoting call. +The function takes a `Activity` and a `ServiceRemotingRequest` and returns the `Activity`. +By default, the client span is enriched with the service interface name +and method name. + +```csharp + TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation(options => + { + options.EnrichAtClientFromRequest = (activity, requestMessage) => + { + // Add custom attributes to the client span + activity.SetTag("CustomAttribute", "CustomValue"); + return activity; + }; + }) + .AddOtlpExporter() + .Build(); +``` + +- **EnrichAtServerFromRequest** - A function that can be used to enrich the span +created for the server side of the remoting call. +The function takes a `Activity` and a `ServiceRemotingRequest` and returns the `Activity`. +By default, the server span is enriched with the service interface +name and method name. + +```csharp + TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation(options => + { + options.EnrichAtServerFromRequest = (activity, requestMessage) => + { + // Add custom attributes to the server span + activity.SetTag("CustomAttribute", "CustomValue"); + return activity; + }; + }) + .AddOtlpExporter() + .Build(); +``` + +- **AddExceptionAtClient** - Gets or sets a value indicating whether +the exception will be recorded at the client as an `ActivityEvent` or not. + +```csharp + TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation(options => + { + options.AddExceptionAtClient = true; + }) + .AddOtlpExporter() + .Build(); +``` + +- **AddExceptionAtServer** - Gets or sets a value indicating whether +the exception will be recorded at the server as an `ActivityEvent` or not + +```csharp + TracerProvider tracerProvider = Sdk.CreateTracerProviderBuilder() + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("ServiceFabricRemoting-Example")) + .AddServiceFabricRemotingInstrumentation(options => + { + options.AddExceptionAtServer = true; + }) + .AddOtlpExporter() + .Build(); +``` + +## References + +- [Azure Service Fabric documentation](https://learn.microsoft.com/en-us/azure/service-fabric/) +- [OpenTelemetry Project](https://opentelemetry.io/) diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingActivitySource.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingActivitySource.cs new file mode 100644 index 0000000000..eb69c138d5 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingActivitySource.cs @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using System.Reflection; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +internal class ServiceFabricRemotingActivitySource +{ + internal static readonly Assembly Assembly = typeof(ServiceFabricRemotingActivitySource).Assembly; + internal static readonly AssemblyName AssemblyName = Assembly.GetName(); + internal static readonly string ActivitySourceName = AssemblyName.Name; + + internal static readonly string IncomingRequestActivityName = ActivitySourceName + ".IncomingRequest"; + internal static readonly string OutgoingRequestActivityName = ActivitySourceName + ".OutgoingRequest"; + + public static ActivitySource ActivitySource { get; } = new ActivitySource(ActivitySourceName, Assembly.GetPackageVersion()); + + public static ServiceFabricRemotingInstrumentationOptions? Options { get; set; } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingInstrumentationOptions.cs new file mode 100644 index 0000000000..dd549be835 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingInstrumentationOptions.cs @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using Microsoft.ServiceFabric.Services.Remoting.V2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// Options for ServiceFabric Remoting instrumentation. +/// +public class ServiceFabricRemotingInstrumentationOptions +{ + /// + /// Initializes a new instance of the class. + /// + public ServiceFabricRemotingInstrumentationOptions() + { + } + + /// + /// Gets or sets a Filter function that determines whether or not to collect telemetry about requests on a per request basis. + /// The Filter gets the , and should return a boolean. + /// If Filter returns true, the request is collected. + /// If Filter returns false or throw exception, the request is filtered out. + /// + public Func? Filter { get; set; } + + /// + /// Gets or sets an action to enrich the created by the client instrumentation, from the request. + /// + public Action? EnrichAtClientFromRequest { get; set; } + + /// + /// Gets or sets an action to enrich the created by the client instrumentation, from the response. + /// + public Action? EnrichAtClientFromResponse { get; set; } + + /// + /// Gets or sets a value indicating whether the exception will be recorded at the client as or not. + /// + /// + /// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/exceptions.md. + /// + public bool AddExceptionAtClient { get; set; } + + /// + /// Gets or sets a value indicating whether the exception will be recorded at the server as or not. + /// + /// + /// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/exceptions.md. + /// + public bool AddExceptionAtServer { get; set; } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingUtils.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingUtils.cs new file mode 100644 index 0000000000..9e90cab099 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceFabricRemotingUtils.cs @@ -0,0 +1,32 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Text; +using Microsoft.ServiceFabric.Services.Remoting.V2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +internal static class ServiceFabricRemotingUtils +{ + internal static void InjectTraceContextIntoServiceRemotingRequestMessageHeader(IServiceRemotingRequestMessageHeader requestMessageHeader, string key, string value) + { + if (!requestMessageHeader.TryGetHeaderValue(key, out byte[] _)) + { + byte[] valueAsBytes = Encoding.UTF8.GetBytes(value); + + requestMessageHeader.AddHeader(key, valueAsBytes); + } + } + + internal static IEnumerable ExtractTraceContextFromRequestMessageHeader(IServiceRemotingRequestMessageHeader requestMessageHeader, string headerKey) + { + if (requestMessageHeader.TryGetHeaderValue(headerKey, out byte[] headerValueAsBytes)) + { + string headerValue = Encoding.UTF8.GetString(headerValueAsBytes); + + return [headerValue]; + } + + return Enumerable.Empty(); + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemoting/TraceContextEnrichedServiceRemotingProviderAttribute.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemoting/TraceContextEnrichedServiceRemotingProviderAttribute.cs new file mode 100644 index 0000000000..4fb29fe8d3 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemoting/TraceContextEnrichedServiceRemotingProviderAttribute.cs @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using Microsoft.ServiceFabric.Services.Remoting; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.Client; +using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client; +using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// This attributes allows to set Fabric TCP transport as the default service remoting transport provider in the assembly and customization of it. +/// +[AttributeUsage(AttributeTargets.Assembly)] +public sealed class TraceContextEnrichedServiceRemotingProviderAttribute : FabricTransportServiceRemotingProviderAttribute +{ + private const string DefaultV2listenerName = "V2Listener"; + + /// + /// Initializes a new instance of the class. + /// + public TraceContextEnrichedServiceRemotingProviderAttribute() + { + this.RemotingClientVersion = RemotingClientVersion.V2; + this.RemotingListenerVersion = RemotingListenerVersion.V2; + } + + /// + /// Creates a V2 service remoting listener for remoting the service interface. + /// + /// + /// A Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportServiceRemotingListener + /// as Microsoft.ServiceFabric.Services.Remoting.Runtime.IServiceRemotingListener for the specified service implementation. + /// + public override Dictionary> CreateServiceRemotingListeners() + { + Dictionary> dictionary = new Dictionary>(); + + dictionary.Add(DefaultV2listenerName, (ServiceContext serviceContext, IService serviceImplementation) => + { + FabricTransportRemotingListenerSettings listenerSettings = this.GetListenerSettings(serviceContext); + ServiceRemotingMessageDispatcher serviceRemotingMessageDispatcher = new ServiceRemotingMessageDispatcher(serviceContext, serviceImplementation); + ServiceRemotingMessageDispatcherAdapter dispatcherAdapter = new ServiceRemotingMessageDispatcherAdapter(serviceRemotingMessageDispatcher); + + return new FabricTransportServiceRemotingListener(serviceContext, dispatcherAdapter, listenerSettings); + }); + + return dictionary; + } + + /// + /// Creates a V2 service remoting client factory for connecting to the service over remoted service interfaces. + /// + /// The client implementation where the callbacks should be dispatched. + /// + /// A Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client.FabricTransportServiceRemotingClientFactory + /// as Microsoft.ServiceFabric.Services.Remoting.V2.Client.IServiceRemotingClientFactory + /// that can be used with Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxyFactory + /// to generate service proxy to talk to a stateless or stateful service over remoted actor interface. + /// + public override IServiceRemotingClientFactory CreateServiceRemotingClientFactoryV2(IServiceRemotingCallbackMessageHandler? callbackMessageHandler) + { + FabricTransportRemotingSettings fabricTransportRemotingSettings = new FabricTransportRemotingSettings(); + fabricTransportRemotingSettings.MaxMessageSize = this.GetAndValidateMaxMessageSize(fabricTransportRemotingSettings.MaxMessageSize); + fabricTransportRemotingSettings.OperationTimeout = this.GetAndValidateOperationTimeout(fabricTransportRemotingSettings.OperationTimeout); + fabricTransportRemotingSettings.KeepAliveTimeout = this.GetAndValidateKeepAliveTimeout(fabricTransportRemotingSettings.KeepAliveTimeout); + fabricTransportRemotingSettings.ConnectTimeout = this.GetConnectTimeout(fabricTransportRemotingSettings.ConnectTimeout); + + FabricTransportServiceRemotingClientFactory fabricTransportServiceRemotingClientFactory = new FabricTransportServiceRemotingClientFactory( + fabricTransportRemotingSettings, + callbackMessageHandler, + servicePartitionResolver: null, + exceptionHandlers: null, + traceId: null); + + return new TraceContextEnrichedServiceRemotingClientFactoryAdapter(fabricTransportServiceRemotingClientFactory); + } + + private FabricTransportRemotingListenerSettings GetListenerSettings(ServiceContext serviceContext) + { + FabricTransportRemotingListenerSettings listenerSettings = new FabricTransportRemotingListenerSettings(); + + listenerSettings.MaxMessageSize = this.GetAndValidateMaxMessageSize(listenerSettings.MaxMessageSize); + listenerSettings.OperationTimeout = this.GetAndValidateOperationTimeout(listenerSettings.OperationTimeout); + listenerSettings.KeepAliveTimeout = this.GetAndValidateKeepAliveTimeout(listenerSettings.KeepAliveTimeout); + + return listenerSettings; + } + + private long GetAndValidateMaxMessageSize(long maxMessageSizeDefault) + { + return (this.MaxMessageSize > 0) ? this.MaxMessageSize : maxMessageSizeDefault; + } + + private TimeSpan GetAndValidateOperationTimeout(TimeSpan operationTimeoutDefault) + { + return (this.OperationTimeoutInSeconds > 0) ? TimeSpan.FromSeconds(this.OperationTimeoutInSeconds) : operationTimeoutDefault; + } + + private TimeSpan GetAndValidateKeepAliveTimeout(TimeSpan keepAliveTimeoutDefault) + { + return (this.KeepAliveTimeoutInSeconds > 0) ? TimeSpan.FromSeconds(this.KeepAliveTimeoutInSeconds) : keepAliveTimeoutDefault; + } + + private TimeSpan GetConnectTimeout(TimeSpan connectTimeoutDefault) + { + return (this.ConnectTimeoutInMilliseconds > 0) ? TimeSpan.FromMilliseconds(this.ConnectTimeoutInMilliseconds) : connectTimeoutDefault; + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemotingMessageDispatcherAdapter.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemotingMessageDispatcherAdapter.cs new file mode 100644 index 0000000000..3c3f489504 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/ServiceRemotingMessageDispatcherAdapter.cs @@ -0,0 +1,115 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using Microsoft.ServiceFabric.Services.Remoting.V2; +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Internal; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// Provides an implementation of that can dispatch +/// messages to an actor service and to the actors hosted in the service. +/// +public sealed class ServiceRemotingMessageDispatcherAdapter : IServiceRemotingMessageHandler, IDisposable +{ + private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; + + private readonly IServiceRemotingMessageHandler innerDispatcher; + + /// + /// Initializes a new instance of the class. + /// + /// The IServiceRemotingMessageHandler to wrap. + public ServiceRemotingMessageDispatcherAdapter(IServiceRemotingMessageHandler dispatcher) + { + Guard.ThrowIfNull(dispatcher); + + this.innerDispatcher = dispatcher; + } + + /// + /// Gets a factory for creating the remoting message bodies. + /// + /// A factory for creating the remoting message bodies. + public IServiceRemotingMessageBodyFactory GetRemotingMessageBodyFactory() + { + return this.innerDispatcher.GetRemotingMessageBodyFactory(); + } + + /// + /// Handles a one way message from the client. + /// + /// The request message. + public void HandleOneWayMessage(IServiceRemotingRequestMessage requestMessage) + { + this.innerDispatcher.HandleOneWayMessage(requestMessage); + } + + /// + /// Dispatches the messages received from the client to the actor service methods or the actor methods. + /// This can be used by user where they know interfaceId and MethodId for the method to dispatch to. + /// + /// Request context that allows getting the callback channel if required. + /// Remoting message. + /// The response for the received request. + public async Task HandleRequestResponseAsync(IServiceRemotingRequestContext requestContext, IServiceRemotingRequestMessage requestMessage) + { + Guard.ThrowIfNull(requestMessage); + + if (ServiceFabricRemotingActivitySource.Options?.Filter?.Invoke(requestMessage) == false) + { + // If we filter out the request we don't need to process anything related to the activity + return await this.innerDispatcher.HandleRequestResponseAsync(requestContext, requestMessage).ConfigureAwait(false); + } + else + { + IServiceRemotingRequestMessageHeader requestMessageHeader = requestMessage.GetHeader(); + Guard.ThrowIfNull(requestMessageHeader, "requestMessage.GetHeader()"); + + // Extract the PropagationContext of the upstream parent from the message headers. + PropagationContext parentContext = Propagator.Extract(default, requestMessageHeader, ServiceFabricRemotingUtils.ExtractTraceContextFromRequestMessageHeader); + Baggage.Current = parentContext.Baggage; + + string activityName = requestMessageHeader?.MethodName ?? ServiceFabricRemotingActivitySource.IncomingRequestActivityName; + + using (Activity? activity = ServiceFabricRemotingActivitySource.ActivitySource.StartActivity(activityName, ActivityKind.Server, parentContext.ActivityContext)) + { + try + { + IServiceRemotingResponseMessage responseMessage = await this.innerDispatcher.HandleRequestResponseAsync(requestContext, requestMessage).ConfigureAwait(false); + + return responseMessage; + } + catch (Exception ex) + { + if (activity != null) + { + activity.SetStatus(ActivityStatusCode.Error); + + if (ServiceFabricRemotingActivitySource.Options?.AddExceptionAtServer == true) + { + activity.RecordException(ex); + } + } + + throw; + } + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (this.innerDispatcher is IDisposable disposable) + { + disposable.Dispose(); + } + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientAdapter.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientAdapter.cs new file mode 100644 index 0000000000..7c130d3373 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientAdapter.cs @@ -0,0 +1,142 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using System.Fabric; +using Microsoft.ServiceFabric.Services.Remoting.V2; +using Microsoft.ServiceFabric.Services.Remoting.V2.Client; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Internal; +using OpenTelemetry.Trace; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// An IServiceRemotingClient that enriches the outgoing request with the current Activity (if any). +/// +internal class TraceContextEnrichedServiceRemotingClientAdapter : IServiceRemotingClient +{ + private static readonly TextMapPropagator Propagator = Propagators.DefaultTextMapPropagator; + + private readonly IServiceRemotingClient innerClient; + + public TraceContextEnrichedServiceRemotingClientAdapter(IServiceRemotingClient remotingClient) + { + this.innerClient = remotingClient; + } + + public IServiceRemotingClient InnerClient + { + get { return this.innerClient; } + } + + public ResolvedServicePartition ResolvedServicePartition + { + get { return this.InnerClient.ResolvedServicePartition; } + set { this.InnerClient.ResolvedServicePartition = value; } + } + + public string ListenerName + { + get { return this.InnerClient.ListenerName; } + set { this.InnerClient.ListenerName = value; } + } + + public ResolvedServiceEndpoint Endpoint + { + get { return this.InnerClient.Endpoint; } + set { this.InnerClient.Endpoint = value; } + } + + public async Task RequestResponseAsync(IServiceRemotingRequestMessage requestMessage) + { + Guard.ThrowIfNull(requestMessage); + + if (ServiceFabricRemotingActivitySource.Options?.Filter?.Invoke(requestMessage) == false) + { + // If we filter out the request we don't need to process anything related to the activity + return await this.innerClient.RequestResponseAsync(requestMessage).ConfigureAwait(false); + } + else + { + IServiceRemotingRequestMessageHeader requestMessageHeader = requestMessage.GetHeader(); + Guard.ThrowIfNull(requestMessageHeader, "requestMessage.GetHeader()"); + + string activityName = requestMessageHeader.MethodName ?? ServiceFabricRemotingActivitySource.OutgoingRequestActivityName; + + using (Activity? activity = ServiceFabricRemotingActivitySource.ActivitySource.StartActivity(activityName, ActivityKind.Client)) + { + // Depending on Sampling (and whether a listener is registered or not), the activity above may not be created. + // If it is created, then propagate its context. + if (activity != null) + { + try + { + ServiceFabricRemotingActivitySource.Options?.EnrichAtClientFromRequest?.Invoke(activity, requestMessage); + } + catch (Exception) + { + // TODO: Log error + } + + try + { + // Inject the ActivityContext into the message headers to propagate trace context and Baggage to the receiving service. + Propagator.Inject(new PropagationContext(activity.Context, Baggage.Current), requestMessageHeader, ServiceFabricRemotingUtils.InjectTraceContextIntoServiceRemotingRequestMessageHeader); + } + catch (Exception ex) + { + activity.SetStatus(ActivityStatusCode.Error, $"Error trying to inject the context in the remoting request: '{ex.Message}'"); + } + } + + try + { + IServiceRemotingResponseMessage responseMessage = await this.innerClient.RequestResponseAsync(requestMessage).ConfigureAwait(false); + + if (activity != null) + { + try + { + ServiceFabricRemotingActivitySource.Options?.EnrichAtClientFromResponse?.Invoke(activity, responseMessage, /* exception */ null); + } + catch (Exception) + { + // TODO: Log error + } + } + + return responseMessage; + } + catch (Exception ex) + { + if (activity != null) + { + activity.SetStatus(ActivityStatusCode.Error); + + if (ServiceFabricRemotingActivitySource.Options?.AddExceptionAtClient == true) + { + activity.RecordException(ex); + } + + try + { + ServiceFabricRemotingActivitySource.Options?.EnrichAtClientFromResponse?.Invoke(activity, /* serviceRemotingResponseMessage */ null, ex); + } + catch (Exception) + { + // TODO: Log error + } + } + + throw; + } + } + } + } + + public void SendOneWay(IServiceRemotingRequestMessage requestMessage) + { + this.InnerClient.SendOneWay(requestMessage); + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientFactoryAdapter.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientFactoryAdapter.cs new file mode 100644 index 0000000000..fbe02e4411 --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TraceContextEnrichedServiceRemotingClientFactoryAdapter.cs @@ -0,0 +1,98 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using Microsoft.ServiceFabric.Services.Client; +using Microsoft.ServiceFabric.Services.Communication.Client; +using Microsoft.ServiceFabric.Services.Remoting.V2; +using Microsoft.ServiceFabric.Services.Remoting.V2.Client; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting; + +/// +/// An IServiceRemotingClientFactory that uses Fabric TCP transport to create IServiceRemotingClient +/// that communicate with stateless and stateful services over interfaces that are +/// remoted via Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime.FabricTransportServiceRemotingListener. +/// +/// Both the client and the listener are instrumented to propagate both the traceContext and Baggage objects from OpenTelemetry. +/// +public class TraceContextEnrichedServiceRemotingClientFactoryAdapter : IServiceRemotingClientFactory +{ + private readonly IServiceRemotingClientFactory innerFactory; + + /// + /// Initializes a new instance of the class. + /// + /// The communication client factory to wrap. + public TraceContextEnrichedServiceRemotingClientFactoryAdapter(IServiceRemotingClientFactory serviceRemotingClientFactory) + { + this.innerFactory = serviceRemotingClientFactory; + } + + /// + /// Event handler that is fired when a client is connected to the service endpoint. + /// + public event EventHandler> ClientConnected + { + add { this.innerFactory.ClientConnected += value; } + remove { this.innerFactory.ClientConnected -= value; } + } + + /// + /// Event handler that is fired when a client is disconnected from the service endpoint. + /// + public event EventHandler> ClientDisconnected + { + add { this.innerFactory.ClientDisconnected += value; } + remove { this.innerFactory.ClientDisconnected -= value; } + } + + /// + /// Gets a factory for creating the remoting message bodies. + /// + /// A factory for creating the remoting message bodies. + public IServiceRemotingMessageBodyFactory GetRemotingMessageBodyFactory() + { + return this.innerFactory.GetRemotingMessageBodyFactory(); + } + + /// + public async Task GetClientAsync(Uri serviceUri, ServicePartitionKey partitionKey, TargetReplicaSelector targetReplicaSelector, string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken) + { + IServiceRemotingClient serviceRemotingClient = await this.innerFactory.GetClientAsync( + serviceUri, + partitionKey, + targetReplicaSelector, + listenerName, + retrySettings, + cancellationToken).ConfigureAwait(false); + + return new TraceContextEnrichedServiceRemotingClientAdapter(serviceRemotingClient); + } + + /// + public async Task GetClientAsync(ResolvedServicePartition previousRsp, TargetReplicaSelector targetReplicaSelector, string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken) + { + IServiceRemotingClient serviceRemotingClient = await this.innerFactory.GetClientAsync( + previousRsp, + targetReplicaSelector, + listenerName, + retrySettings, + cancellationToken).ConfigureAwait(false); + + return new TraceContextEnrichedServiceRemotingClientAdapter(serviceRemotingClient); + } + + /// + public Task ReportOperationExceptionAsync(IServiceRemotingClient client, ExceptionInformation exceptionInformation, OperationRetrySettings retrySettings, CancellationToken cancellationToken) + { + IServiceRemotingClient innerClient = client; + TraceContextEnrichedServiceRemotingClientAdapter? clientAdapter = client as TraceContextEnrichedServiceRemotingClientAdapter; + if (clientAdapter != null) + { + innerClient = clientAdapter.InnerClient; + } + + return this.innerFactory.ReportOperationExceptionAsync(innerClient, exceptionInformation, retrySettings, cancellationToken); + } +} diff --git a/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TracerProviderBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TracerProviderBuilderExtensions.cs new file mode 100644 index 0000000000..f8d275576a --- /dev/null +++ b/src/OpenTelemetry.Instrumentation.ServiceFabricRemoting/TracerProviderBuilderExtensions.cs @@ -0,0 +1,51 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OpenTelemetry.Instrumentation.ServiceFabricRemoting; +using OpenTelemetry.Internal; + +namespace OpenTelemetry.Trace; + +/// +/// Extension methods to simplify registering of ServiceFabric Remoting instrumentation. +/// +public static class TracerProviderBuilderExtensions +{ + /// + /// Enables the incoming requests automatic data collection for ServiceFabric Remoting. + /// + /// being configured. + /// The instance of to chain the calls. + public static TracerProviderBuilder AddServiceFabricRemotingInstrumentation(this TracerProviderBuilder builder) + { + return AddServiceFabricRemotingInstrumentation(builder, configure: null); + } + + /// + /// Enables the incoming requests automatic data collection for ServiceFabric Remoting. + /// + /// being configured. + /// ServiceFabric Remoting configuration options. + /// The instance of to chain the calls. + public static TracerProviderBuilder AddServiceFabricRemotingInstrumentation(this TracerProviderBuilder tracerProviderBuilder, Action? configure) + { + Guard.ThrowIfNull(tracerProviderBuilder); + + return tracerProviderBuilder.ConfigureServices(services => + { + if (configure != null) + { + services.Configure(configure); + } + + services.ConfigureOpenTelemetryTracerProvider((serviceProvider, builder) => + { + ServiceFabricRemotingActivitySource.Options = serviceProvider.GetRequiredService>().Get(name: null); + + builder.AddSource(ServiceFabricRemotingActivitySource.ActivitySourceName); + }); + }); + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActor.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActor.cs new file mode 100644 index 0000000000..a3c506201b --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActor.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Actors; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public interface IMyTestActor : IActor +{ + Task TestContextPropagation(string valueToReturn); +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActorService.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActorService.cs new file mode 100644 index 0000000000..0a661ff269 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/IMyTestActorService.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Actors; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public interface IMyTestActorService : IActorService +{ + Task TestContextPropagation(string valueToReturn); +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ITestMyStatefulService.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ITestMyStatefulService.cs new file mode 100644 index 0000000000..95f83922eb --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ITestMyStatefulService.cs @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Services.Remoting; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public interface ITestMyStatefulService : IService +{ + Task TestContextPropagation(string valueToReturn); +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/FabricTransportServiceRemotingRequestContextMock.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/FabricTransportServiceRemotingRequestContextMock.cs new file mode 100644 index 0000000000..d7066092e2 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/FabricTransportServiceRemotingRequestContextMock.cs @@ -0,0 +1,18 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal class FabricTransportServiceRemotingRequestContextMock : IServiceRemotingRequestContext +{ + public FabricTransportServiceRemotingRequestContextMock() + { + } + + public IServiceRemotingCallbackClient? GetCallBackClient() + { + return null; + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingClientMock.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingClientMock.cs new file mode 100644 index 0000000000..57ab9f5872 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingClientMock.cs @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using System.Text; +using Microsoft.ServiceFabric.Services.Remoting.V2; +using Microsoft.ServiceFabric.Services.Remoting.V2.Client; +using OpenTelemetry.Context.Propagation; +using ServiceFabric.Mocks.RemotingV2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal class ServiceRemotingClientMock : IServiceRemotingClient +{ + public ServiceRemotingClientMock() + { + } + + public ResolvedServicePartition ResolvedServicePartition { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public string ListenerName { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public ResolvedServiceEndpoint Endpoint { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + /// + /// The RequestResponseAsync method reads the headers from the request and injects them into the response, using OpneTelemetry's TextMapPropagator. + /// + public Task RequestResponseAsync(IServiceRemotingRequestMessage requestMessage) + { + IServiceRemotingRequestMessageHeader requestMessageHeader = requestMessage.GetHeader(); + PropagationContext propagationContext = Propagators.DefaultTextMapPropagator.Extract(default, requestMessageHeader, ServiceFabricRemotingUtils.ExtractTraceContextFromRequestMessageHeader); + + MockServiceRemotingResponseMessage responseMessage = new MockServiceRemotingResponseMessage() + { + Header = new ServiceRemotingResponseMessageHeaderMock(), + }; + + Propagators.DefaultTextMapPropagator.Inject(new PropagationContext(propagationContext.ActivityContext, propagationContext.Baggage), responseMessage.Header, this.InjectTraceContextIntoServiceRemotingRequestMessageHeader); + + return Task.FromResult(responseMessage); + } + + public void SendOneWay(IServiceRemotingRequestMessage requestMessage) => throw new NotImplementedException(); + + private void InjectTraceContextIntoServiceRemotingRequestMessageHeader(IServiceRemotingResponseMessageHeader responseMessageHeaders, string key, string value) + { + if (!responseMessageHeaders.TryGetHeaderValue(key, out byte[] _)) + { + byte[] valueAsBytes = Encoding.UTF8.GetBytes(value); + + responseMessageHeaders.AddHeader(key, valueAsBytes); + } + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageHeaderMock.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageHeaderMock.cs new file mode 100644 index 0000000000..4aacb37b4e --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageHeaderMock.cs @@ -0,0 +1,71 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using System.Globalization; +using System.Runtime.Serialization; +using Microsoft.ServiceFabric.Services.Remoting.V2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal class ServiceRemotingRequestMessageHeaderMock : IServiceRemotingRequestMessageHeader +{ + [DataMember(Name = "Headers", IsRequired = true, Order = 2)] + private readonly Dictionary headers = new Dictionary(); + + public ServiceRemotingRequestMessageHeaderMock() + { + this.InvocationId = null; + } + + /// + /// Gets or sets the methodId of the remote method. + /// + [DataMember(Name = "MethodId", IsRequired = true, Order = 0)] + public int MethodId { get; set; } + + /// + /// Gets or sets the interface id of the remote interface. + /// + [DataMember(Name = "InterfaceId", IsRequired = true, Order = 1)] + public int InterfaceId { get; set; } + + /// + /// Gets or sets identifier for the remote method invocation. + /// + [DataMember(Name = "InvocationId", IsRequired = false, Order = 3, EmitDefaultValue = false)] + public string? InvocationId { get; set; } + + /// + /// Gets or sets the method name of the remote method. + /// + [DataMember(Name = "MethodName", IsRequired = false, Order = 4)] + public string? MethodName { get; set; } + + /// + /// Gets or sets the request id. + /// + [DataMember(Name = "RequestId", IsRequired = false, Order = 5)] + public Guid RequestId { get; set; } + + public void AddHeader(string headerName, byte[] headerValue) + { + if (this.headers.ContainsKey(headerName)) + { + throw new FabricElementAlreadyExistsException(string.Format((IFormatProvider)(object)CultureInfo.CurrentCulture, "ErrorHeaderAlreadyExists")); + } + + this.headers[headerName] = headerValue; + } + + public bool TryGetHeaderValue(string headerName, out byte[]? headerValue) + { + headerValue = null; + if (this.headers == null) + { + return false; + } + + return this.headers.TryGetValue(headerName, out headerValue); + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageMock.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageMock.cs new file mode 100644 index 0000000000..65a81fde09 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingRequestMessageMock.cs @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using Microsoft.ServiceFabric.Services.Remoting.V2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal class ServiceRemotingRequestMessageMock : IServiceRemotingRequestMessage +{ + private readonly IServiceRemotingRequestMessageHeader header; + + private readonly IServiceRemotingRequestMessageBody msgBody; + + public ServiceRemotingRequestMessageMock(IServiceRemotingRequestMessageHeader header, IServiceRemotingRequestMessageBody msgBody) + { + this.header = header; + this.msgBody = msgBody; + } + + public IServiceRemotingRequestMessageHeader GetHeader() + { + return this.header; + } + + public IServiceRemotingRequestMessageBody GetBody() + { + return this.msgBody; + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingResponseMessageHeaderMock.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingResponseMessageHeaderMock.cs new file mode 100644 index 0000000000..ba8eef9619 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/Mocks/ServiceRemotingResponseMessageHeaderMock.cs @@ -0,0 +1,49 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using System.Globalization; +using Microsoft.ServiceFabric.Services.Remoting.V2; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal class ServiceRemotingResponseMessageHeaderMock : IServiceRemotingResponseMessageHeader +{ + private Dictionary headers; + + public ServiceRemotingResponseMessageHeaderMock() + { + this.headers = new Dictionary(); + } + + public void AddHeader(string headerName, byte[] headerValue) + { + if (this.headers.ContainsKey(headerName)) + { + throw new FabricElementAlreadyExistsException(string.Format((IFormatProvider)(object)CultureInfo.CurrentCulture, "ErrorHeaderAlreadyExists")); + } + + this.headers[headerName] = headerValue; + } + + public bool CheckIfItsEmpty() + { + if (this.headers == null || this.headers.Count == 0) + { + return true; + } + + return false; + } + + public bool TryGetHeaderValue(string headerName, out byte[]? headerValue) + { + headerValue = null; + if (this.headers == null) + { + return false; + } + + return this.headers.TryGetValue(headerName, out headerValue); + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActor.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActor.cs new file mode 100644 index 0000000000..168cc0c08e --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActor.cs @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using Microsoft.ServiceFabric.Actors; +using Microsoft.ServiceFabric.Actors.Runtime; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public class MyTestActor : Actor, IMyTestActor +{ + public MyTestActor(ActorService actorService, ActorId actorId) + : base(actorService, actorId) + { + } + + public Task TestContextPropagation(string valueToReturn) + { + ActivityContext activityContext = Activity.Current!.Context; + Baggage baggage = Baggage.Current; + + ServiceResponse serviceResponse = new ServiceResponse + { + ParameterValue = valueToReturn, + ActivityContext = activityContext, + Baggage = baggage, + }; + + return Task.FromResult(serviceResponse); + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActorService.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActorService.cs new file mode 100644 index 0000000000..330bd42159 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestActorService.cs @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Fabric; +using Microsoft.ServiceFabric.Actors; +using Microsoft.ServiceFabric.Actors.Remoting.V2.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Actors.Remoting.V2.Runtime; +using Microsoft.ServiceFabric.Actors.Runtime; +using Microsoft.ServiceFabric.Services.Communication.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public class MyTestActorService : ActorService, IMyTestActorService +{ + private static readonly Guid ActorId = Guid.Parse("{1F263E8C-78D4-4D91-AAE6-C4B9CE03D6EB}"); + + private readonly ServiceRemotingMessageDispatcherAdapter dispatcher; + private MyTestActor actor; + + public MyTestActorService(StatefulServiceContext context, ActorTypeInformation actorTypeInfo, Func? actorFactory = null, Func? stateManagerFactory = null, IActorStateProvider? stateProvider = null, ActorServiceSettings? settings = null) + : base(context, actorTypeInfo, actorFactory, stateManagerFactory, stateProvider, settings) + { + ActorServiceRemotingDispatcher actorServiceRemotingDispatcher = new ActorServiceRemotingDispatcher(this, serviceRemotingRequestMessageBodyFactory: null); + + this.dispatcher = new ServiceRemotingMessageDispatcherAdapter(actorServiceRemotingDispatcher); + + ActorId id = new ActorId(ActorId); + this.actor = new MyTestActor(this, id); + } + + public IServiceRemotingMessageHandler Dispatcher + { + get { return this.dispatcher; } + } + + public MyTestActor Actor + { + get { return this.actor; } + } + + public Task TestContextPropagation(string valueToReturn) + { + return this.actor.TestContextPropagation(valueToReturn); + } + + protected override IEnumerable CreateServiceReplicaListeners() + { + Func getListenerFunc = () => + { + FabricTransportRemotingListenerSettings listenerSettings = new FabricTransportRemotingListenerSettings(); + + return new FabricTransportActorServiceRemotingListener(this, this.dispatcher, listenerSettings); + }; + + ServiceReplicaListener serviceReplicaListener = new ServiceReplicaListener((StatefulServiceContext t) => getListenerFunc(), "V2Listener"); + + return [serviceReplicaListener]; + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestStatefulService.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestStatefulService.cs new file mode 100644 index 0000000000..f3fbbd315f --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/MyTestStatefulService.cs @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using System.Fabric; +using Microsoft.ServiceFabric.Data; +using Microsoft.ServiceFabric.Services.Communication.Runtime; +using Microsoft.ServiceFabric.Services.Remoting; +using Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; +using Microsoft.ServiceFabric.Services.Runtime; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public class MyTestStatefulService : StatefulService, ITestMyStatefulService +{ + private IServiceRemotingMessageHandler? dispatcher; + + public MyTestStatefulService(StatefulServiceContext serviceContext, IReliableStateManagerReplica reliableStateManagerReplica) + : base(serviceContext, reliableStateManagerReplica) + { + } + + public Task TestContextPropagation(string valueToReturn) + { + ActivityContext activityContext = Activity.Current!.Context; + Baggage baggage = Baggage.Current; + + ServiceResponse serviceResponse = new ServiceResponse + { + ParameterValue = valueToReturn, + ActivityContext = activityContext, + Baggage = baggage, + }; + + return Task.FromResult(serviceResponse); + } + + internal void SetDispatcher(IServiceRemotingMessageHandler dispatcher) + { + this.dispatcher = dispatcher; + } + + protected override IEnumerable CreateServiceReplicaListeners() + { + Func getListenerFunc = (ServiceContext serviceContext, IService serviceImplementation) => + { + FabricTransportRemotingListenerSettings listenerSettings = new FabricTransportRemotingListenerSettings(); + + return new FabricTransportServiceRemotingListener(serviceContext, this.dispatcher, listenerSettings); + }; + + ServiceReplicaListener serviceReplicaListener = new ServiceReplicaListener((StatefulServiceContext t) => getListenerFunc(this.ServiceContext, this), "V2Listener"); + + return [serviceReplicaListener]; + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests.csproj b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests.csproj new file mode 100644 index 0000000000..79b9bbf971 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests.csproj @@ -0,0 +1,30 @@ + + + + + $(SupportedNetTargets) + $(TargetFrameworks);$(NetFrameworkMinimumSupportedVersion) + + + + + None + + + + + + + + + + + + + + + + + + + diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricRemotingTests.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricRemotingTests.cs new file mode 100644 index 0000000000..e93e46ff40 --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricRemotingTests.cs @@ -0,0 +1,174 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; +using System.Fabric; +using System.Reflection; +using System.Text; +using Microsoft.ServiceFabric.Actors; +using Microsoft.ServiceFabric.Actors.Runtime; +using Microsoft.ServiceFabric.Services.Remoting.V2; +using Microsoft.ServiceFabric.Services.Remoting.V2.Runtime; +using OpenTelemetry.Context.Propagation; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; +using ServiceFabric.Mocks; +using ServiceFabric.Mocks.RemotingV2; +using Xunit; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public class ServiceFabricRemotingTests +{ + private const string ValueToSend = "SomeValue"; + private const string BaggageKey = "SomeBaggageKey"; + private const string BaggageValue = "SomeBaggageValue"; + private static readonly ActivitySource ActivitySource = new ActivitySource("ServiceFabricRemotingTests"); + + [Fact] + public async Task TestStatefulServiceContextPropagation_ShouldExtractActivityContextAndBaggage() + { + // Arrange + using TracerProvider provider = Sdk.CreateTracerProviderBuilder() + .AddServiceFabricRemotingInstrumentation() + .Build(); + + TextMapPropagator propagator = Propagators.DefaultTextMapPropagator; + + StatefulServiceContext serviceContext = MockStatefulServiceContextFactory.Default; + MockReliableStateManager reliableStateManager = new MockReliableStateManager(); + + // We need to create the service, then the dispatcher, and then set the dispatcher on the service, because the dispatcher needs the service as an argument, and the service needs the dispatcher. + MyTestStatefulService myStatefulService = new MyTestStatefulService(serviceContext, reliableStateManager); + ServiceRemotingMessageDispatcher serviceRemotingMessageDispatcher = new ServiceRemotingMessageDispatcher(serviceContext, myStatefulService); + ServiceRemotingMessageDispatcherAdapter dispatcherAdapter = new ServiceRemotingMessageDispatcherAdapter(serviceRemotingMessageDispatcher); + myStatefulService.SetDispatcher(dispatcherAdapter); + + // We create an ActivityContext and Baggage to inject into the request message, instead of starting a new Activity, because the dispatcher is in the same process as the test, and we don't want to set Activity.Current. + ActivityContext activityContext = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded); + Baggage baggage = Baggage.Create(new Dictionary { { BaggageKey, BaggageValue } }); + + IServiceRemotingRequestMessageHeader remotingRequestMessageHeader = this.CreateServiceRemotingRequestMessageHeader(typeof(ITestMyStatefulService), nameof(ITestMyStatefulService.TestContextPropagation)); + + propagator.Inject(new PropagationContext(activityContext, baggage), remotingRequestMessageHeader, ServiceFabricRemotingUtils.InjectTraceContextIntoServiceRemotingRequestMessageHeader); + + MockServiceRemotingRequestMessageBody messageBody = new MockServiceRemotingRequestMessageBody(); + messageBody.SetParameter(0, "valueToReturn", ValueToSend); + + ServiceRemotingRequestMessageMock requestMessage = new(remotingRequestMessageHeader, messageBody); + FabricTransportServiceRemotingRequestContextMock remotingRequestContext = new FabricTransportServiceRemotingRequestContextMock(); + + // Act + IServiceRemotingResponseMessage response = await dispatcherAdapter.HandleRequestResponseAsync(remotingRequestContext, requestMessage); + + // Assert + ServiceResponse serviceResponse = (ServiceResponse)response.GetBody().Get(typeof(ServiceResponse)); + + Assert.Equal(ValueToSend, serviceResponse.ParameterValue); + Assert.Equal(activityContext.TraceId, serviceResponse.ActivityContext.TraceId); + Assert.Equal(BaggageValue, serviceResponse.Baggage.GetBaggage(BaggageKey)); + } + + [Fact] + public async Task TestActorContextPropagation_ShouldExtractActivityContextAndBaggage() + { + // Arrange + using TracerProvider provider = Sdk.CreateTracerProviderBuilder() + .AddServiceFabricRemotingInstrumentation() + .Build(); + + TextMapPropagator propagator = Propagators.DefaultTextMapPropagator; + + // We have to include the method 'TestContextPropagation' in the interface IMyTestActorService a redirected it to the actor because the normal flow in the base classes is not unit-testable. + // This still allows us to test what we want to test here, which is the method 'HandleRequestResponseAsync' in TraceContextEnrichedActorServiceV2RemotingDispatcher. + Func actorFactory = (service, actorId) => ((MyTestActorService)service).Actor; + MyTestActorService actorService = MockActorServiceFactory.CreateCustomActorServiceForActor(actorFactory); + + // We create an ActivityContext and Baggage to inject into the request message, instead of starting a new Activity, because the dispatcher is in the same process as the test, and we don't want to set Activity.Current. + ActivityContext activityContext = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded); + Baggage baggage = Baggage.Create(new Dictionary { { BaggageKey, BaggageValue } }); + + IServiceRemotingRequestMessageHeader actorRemotingMessageHeaders = this.CreateServiceRemotingRequestMessageHeader(typeof(IMyTestActorService), nameof(IMyTestActorService.TestContextPropagation)); + + propagator.Inject(new PropagationContext(activityContext, baggage), actorRemotingMessageHeaders, ServiceFabricRemotingUtils.InjectTraceContextIntoServiceRemotingRequestMessageHeader); + + MockServiceRemotingRequestMessageBody messageBody = new MockServiceRemotingRequestMessageBody(); + messageBody.SetParameter(0, "valueToReturn", ValueToSend); + + ServiceRemotingRequestMessageMock requestMessage = new(actorRemotingMessageHeaders, messageBody); + FabricTransportServiceRemotingRequestContextMock remotingRequestContext = new FabricTransportServiceRemotingRequestContextMock(); + + // Act + IServiceRemotingResponseMessage response = await actorService.Dispatcher.HandleRequestResponseAsync(remotingRequestContext, requestMessage); + + // Assert + ServiceResponse serviceResponse = (ServiceResponse)response.GetBody().Get(typeof(ServiceResponse)); + + Assert.Equal(ValueToSend, serviceResponse.ParameterValue); + Assert.Equal(activityContext.TraceId, serviceResponse.ActivityContext.TraceId); + Assert.Equal(BaggageValue, serviceResponse.Baggage.GetBaggage(BaggageKey)); + } + + [Fact] + public async Task TestServiceRemotingClientContextPropagation_ShouldInjectActivityContextAndBaggage() + { + // Arrange + using TracerProvider provider = Sdk.CreateTracerProviderBuilder() + .AddServiceFabricRemotingInstrumentation() + .AddSource(ActivitySource.Name) + .Build(); + + // The Baggage set here will be used automatically by TraceContextEnrichedServiceRemotingClientAdapter to inject the baggage into the request message. + Baggage.SetBaggage(BaggageKey, BaggageValue); + + // The activity is created here will be used automatically by TraceContextEnrichedServiceRemotingClientAdapter to inject the context into the request message. + using (Activity activity = ActivitySource.StartActivity("TestActivity")!) + { + ServiceRemotingRequestMessageHeaderMock header = new ServiceRemotingRequestMessageHeaderMock(); + MockServiceRemotingRequestMessageBody messageBody = new MockServiceRemotingRequestMessageBody(); + ServiceRemotingRequestMessageMock requestMessage = new(header, messageBody); + + // The ServiceRemotingClientMock reads the headers from the request and injects them into the response, using OpneTelemetry's TextMapPropagator. + ServiceRemotingClientMock innerClient = new ServiceRemotingClientMock(); + TraceContextEnrichedServiceRemotingClientAdapter serviceRemotingClientAdapter = new TraceContextEnrichedServiceRemotingClientAdapter(innerClient); + + // Act + IServiceRemotingResponseMessage response = await serviceRemotingClientAdapter.RequestResponseAsync(requestMessage); + + // Assert + IServiceRemotingResponseMessageHeader responseMessageHeaders = response.GetHeader(); + PropagationContext propagationContext = Propagators.DefaultTextMapPropagator.Extract(default, responseMessageHeaders, this.ExtractTraceContextFromRequestMessageHeader); + + Assert.Equal(activity.TraceId, propagationContext.ActivityContext.TraceId); + Assert.Equal(BaggageValue, propagationContext.Baggage.GetBaggage(BaggageKey)); + } + } + + private ServiceRemotingRequestMessageHeaderMock CreateServiceRemotingRequestMessageHeader(Type interfaceType, string methodName) + { + int interfaceId = ServiceFabricUtils.GetInterfaceId(interfaceType); + + MethodInfo methodInfo = interfaceType.GetMethod(methodName)!; + int methodId = ServiceFabricUtils.GetMethodId(methodInfo); + + ServiceRemotingRequestMessageHeaderMock serviceRemotingRequestMessageHeader = new ServiceRemotingRequestMessageHeaderMock + { + InterfaceId = interfaceId, + MethodId = methodId, + }; + + return serviceRemotingRequestMessageHeader; + } + + private IEnumerable ExtractTraceContextFromRequestMessageHeader(IServiceRemotingResponseMessageHeader responseMessageHeaders, string headerKey) + { + if (responseMessageHeaders.TryGetHeaderValue(headerKey, out byte[] headerValueAsBytes)) + { + string headerValue = Encoding.UTF8.GetString(headerValueAsBytes); + + return [headerValue]; + } + + return Enumerable.Empty(); + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricUtils.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricUtils.cs new file mode 100644 index 0000000000..19a780ef1d --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceFabricUtils.cs @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Reflection; +using System.Text; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +internal static class ServiceFabricUtils +{ + private static readonly ulong[] Crc64Table = new ulong[256] + { + 0uL, 4823603603198064275uL, 9647207206396128550uL, 14344283933443513269uL, 5274672035359026399uL, 847670339082705484uL, 14759040976900489721uL, 10241823793177474922uL, 10549344070718052798uL, 15030250704074698541uL, + 1695340678165410968uL, 6158653484774949387uL, 15804726273676621153uL, 11071337880091427826uL, 6824194888265062471uL, 2036903512645398228uL, 7367177604490692079uL, 2651944067726553980uL, 16419204125234161865uL, 11613757334439845466uL, + 3390681356330821936uL, 7926053118503640995uL, 12317306969549898774uL, 16726154088988619397uL, 17607865585094646865uL, 13162708473643690690uL, 8194994013375312247uL, 3695931686473304036uL, 13648389776530124942uL, 18417527692557321757uL, + 4073807025290796456uL, 8825348881154370363uL, 14734355208981384158uL, 10271039580541631821uL, 5303888135453107960uL, 822984195088142443uL, 9604374506261047041uL, 14391664176758772114uL, 47380625301539367uL, 4780770595170139316uL, + 6781362712661643872uL, 2084283301222999283uL, 15852106237007281990uL, 11028505464239851989uL, 1670654249350217407uL, 6187869865390245932uL, 10578560694269006745uL, 15005564104267687178uL, 12269926345859042865uL, 16768987096479742114uL, + 3433514057002836759uL, 7878672873577829764uL, 16389988026750624494uL, 11638443477897467005uL, 7391863372946608072uL, 2622728278751721819uL, 4044590402276644751uL, 8850035479350698268uL, 13673076206955870889uL, 18388311311405091898uL, + 8147614050581592912uL, 3738764100714335683uL, 17650697762308740726uL, 13115328684529279205uL, 15709965168302367023uL, 11021966344253216700uL, 6909860770376862729uL, 2095335087373712026uL, 10607776270906215920uL, 15115916238825782115uL, + 1645968390176284886uL, 6063892853452478021uL, 5216239979862816913uL, 762004938812542466uL, 14808413130408695223uL, 10336584279807992612uL, 94761250603078734uL, 4872975272980325085uL, 9561541190340278632uL, 14285852213486374907uL, + 13562725425323287744uL, 18359094313119879763uL, 4168566602445998566uL, 8874722219015798645uL, 17657238303940757535uL, 13257468400305012364uL, 8136561383943382329uL, 3610266854770152362uL, 3341308498700434814uL, 7831293060043656173uL, + 12375739730780491864uL, 16811819059476047563uL, 7452841817450123681uL, 2710377314828461874uL, 16324444680414493831uL, 11564384134825822740uL, 1621282580641819377uL, 6093108618008534114uL, 10636992411005044695uL, 15091230119249932612uL, + 6867028114005673518uL, 2142715359940571325uL, 15757345747155659528uL, 10979133309658045851uL, 9518708972583580495uL, 14333231979791697372uL, 142141253402664553uL, 4830142882085382394uL, 14783726745893216144uL, 10365800689136969987uL, + 5245456557503443638uL, 737318311902463013uL, 8089180804553289502uL, 3653099890976004493uL, 17700070958701396536uL, 13210088128275084459uL, 4139350461810230209uL, 8899408340202190162uL, 13587411233247080167uL, 18329878549100632180uL, + 16295228101163185824uL, 11589070762272702515uL, 7477528201428671366uL, 2681160907110034709uL, 12328359726370364031uL, 16854651450907929836uL, 3384140715920324441uL, 7783913295349006794uL, 17796789492404876493uL, 12973186262895182430uL, + 8294265019745835499uL, 3597188614796881784uL, 13819721540753725458uL, 18246723593521770113uL, 4190670174747424052uL, 8707887697765516199uL, 7249714899603402099uL, 2768808468102880224uL, 16248400991498780757uL, 11785088403942012614uL, + 3291936780352569772uL, 8025325358597240639uL, 12127785706904956042uL, 16915077318774037017uL, 10432479959725633826uL, 15147713122803500977uL, 1524009877625084932uL, 6329456346323069591uL, 15705454305770282493uL, 11170082187107838830uL, + 6635271944638132443uL, 2226424485906433608uL, 189522501206157468uL, 4634679410803088911uL, 9745950545960650170uL, 14245012653811987241uL, 5445476407655580739uL, 676338306971005648uL, 14876502445374089573uL, 10124960353263198198uL, + 4215391513593610003uL, 8678706776937023872uL, 13790540925671641653uL, 18271444552530207910uL, 8337133204891997132uL, 3549843186494580063uL, 17749444438031597290uL, 13016054137459951737uL, 12170653315997410989uL, 16867732534171963454uL, + 3244592164593781643uL, 8068192726900473112uL, 16273122767886764658uL, 11755906975779290337uL, 7220533709540304724uL, 2793530071884239303uL, 6682616997400869628uL, 2183556611878603887uL, 15662586120087312346uL, 11217427617020813641uL, + 1553190491096487459uL, 6304735387851432112uL, 10407758620342516485uL, 15176894045242543510uL, 14905683634900247362uL, 10100238751092381137uL, 5420754629656923748uL, 705519735670536439uL, 9793295161182637981uL, 14202145287119436046uL, + 146654890503152315uL, 4682024195942093864uL, 3242565161283638754uL, 7930564333232481137uL, 12186217236017068228uL, 17000743249723264599uL, 7335380351123765565uL, 2827240748300537774uL, 16153640314560107547uL, 11735716164790313608uL, + 13734056228011347036uL, 18188291445129067215uL, 4285430719881142650uL, 8757259798139230185uL, 17846161249714921603uL, 13067947420601767440uL, 8235833358291897765uL, 3511522545606540086uL, 5387043107155988493uL, 590673871457609374uL, + 14925875833148783915uL, 10219719885873843128uL, 284282506805329106uL, 4684052045342640705uL, 9660285764170764788uL, 14186579979835500391uL, 15610694155489642931uL, 11120709418076880672uL, 6720936860919424149uL, 2284857304564388358uL, + 10490913115006887276uL, 15233377424362361855uL, 1474636623804926026uL, 6234696958930763481uL, 16178361609106579004uL, 11706535215248140463uL, 7306199781952008986uL, 2851961734412043657uL, 12229085463316509411uL, 16953397843693241456uL, + 3195220067441434565uL, 7973432182840617302uL, 8278700923620460418uL, 3464177731752866065uL, 17798816680404380324uL, 13110814815472245815uL, 4310152537884486493uL, 8728078392784608718uL, 13704874997943502459uL, 18213013024491712744uL, + 9707630858549910483uL, 14143712128616820032uL, 241414281116563189uL, 4731397450835853414uL, 14955056402857342732uL, 10194998898151653791uL, 5362321814220069418uL, 619854820462849209uL, 1503817855483314797uL, 6209975379031176446uL, + 10466191297540353867uL, 15262558828106308056uL, 6768281431840648882uL, 2241989909157107745uL, 15567826590698013588uL, 11168054230320002311uL, + }; + + internal static int GetInterfaceId(Type type) + { + string text = ((MemberInfo)type).Name; + if (type.Namespace != null) + { + text = string.Concat(type.Namespace, text); + } + + int interfaceId = (int)ToCRC64(Encoding.UTF8.GetBytes(text)); + return interfaceId; + } + + internal static int GetMethodId(MethodInfo methodInfo) + { + string text = methodInfo.Name; + if (methodInfo.DeclaringType != null) + { + if (methodInfo.DeclaringType.Namespace != null) + { + text = methodInfo.DeclaringType.Namespace + text; + } + + text = methodInfo.DeclaringType.Name + text; + } + + int methodId = (int)ToCRC64(Encoding.UTF8.GetBytes(text)); + return methodId; + } + + private static ulong ToCRC64(byte[] value) + { + ulong num = 18446744073709551615uL; + for (int i = 0; i < value.Length; i++) + { + uint num2 = (uint)((int)(num >> 56) ^ value[i]) & 0xFFu; + num = Crc64Table[num2] ^ (num << 8); + } + + return num ^ 0xFFFFFFFFFFFFFFFFuL; + } +} diff --git a/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceResponse.cs b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceResponse.cs new file mode 100644 index 0000000000..c2f8a37efd --- /dev/null +++ b/test/OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests/ServiceResponse.cs @@ -0,0 +1,15 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Diagnostics; + +namespace OpenTelemetry.Instrumentation.ServiceFabricRemoting.Tests; + +public class ServiceResponse +{ + public string? ParameterValue { get; set; } + + public ActivityContext ActivityContext { get; set; } + + public Baggage Baggage { get; set; } +}