Skip to content
This repository has been archived by the owner on Jul 9, 2024. It is now read-only.

Adds registry for activity sources #165

Merged
merged 5 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.1] - 2023-11-10

### Added

- Fixes multiple initialization of `ActivitySource` instances on each request send(https://github.com/microsoft/kiota-http-dotnet/issues/161).

## [1.3.0] - 2023-11-02

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware;
using Xunit;

namespace Microsoft.Kiota.Http.HttpClientLibrary.Tests.Middleware.Registries
{
public class ActivitySourceRegistryTests
{
[Fact]
public void Defensive()
{
Assert.Throws<ArgumentNullException>(() => ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(""));
Assert.Throws<ArgumentNullException>(() => ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(null));
}

[Fact]
public void CreatesNewInstanceOnFirstCallAndReturnsSameInstance()
{
// Act
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource("sample source");
Assert.NotNull(activitySource);

var activitySource2 = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource("sample source");
Assert.NotNull(activitySource);

// They are the same instance
Assert.Equal(activitySource, activitySource2);
Assert.Equal("sample source", activitySource.Name);
Assert.Equal("sample source", activitySource2.Name);
}

[Fact]
public void CreatesDifferentInstances()
{
// Act
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource("sample source");
Assert.NotNull(activitySource);
Assert.Equal("sample source", activitySource.Name);

var activitySource2 = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource("sample source 2");
Assert.NotNull(activitySource);
Assert.Equal("sample source 2", activitySource2.Name);

// They are not the same instance
Assert.NotEqual(activitySource, activitySource2);
}
}
}
2 changes: 1 addition & 1 deletion src/KiotaClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public static IList<DelegatingHandler> CreateDefaultHandlers()
}
if(finalHandler != null)
handlers[handlers.Length-1].InnerHandler = finalHandler;
return handlers.First();
return handlers[0];//first
}
/// <summary>
/// Creates a <see cref="DelegatingHandler"/> to use for the <see cref="HttpClient" /> from the provided <see cref="DelegatingHandler"/> instances. Order matters.
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.Kiota.Http.HttpClientLibrary.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageProjectUrl>https://aka.ms/kiota/docs</PackageProjectUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.3.0</VersionPrefix>
<VersionPrefix>1.3.1</VersionPrefix>
<VersionSuffix></VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- Enable this line once we go live to prevent breaking changes -->
Expand Down
37 changes: 37 additions & 0 deletions src/Middleware/ActivitySourceRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.Diagnostics;

namespace Microsoft.Kiota.Http.HttpClientLibrary.Middleware;

/// <summary>
/// Internal static registry for activity sources during tracing.
/// </summary>
internal class ActivitySourceRegistry
{
private readonly ConcurrentDictionary<string, ActivitySource> _activitySources = new (StringComparer.OrdinalIgnoreCase);

/// <summary>
/// The default instance of the registry
/// </summary>
public static readonly ActivitySourceRegistry DefaultInstance = new();

/// <summary>
/// Get an a <see cref="ActivitySource"/> instance with the given name or create one.
/// </summary>
/// <param name="sourceName">The name of the <see cref="ActivitySource"/></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">When the parameter is null or empty</exception>
public ActivitySource GetOrCreateActivitySource(string sourceName)
{
if(string.IsNullOrEmpty(sourceName))
throw new ArgumentNullException(nameof(sourceName));

return _activitySources.GetOrAdd(sourceName, static source => new ActivitySource(source));

}
}
7 changes: 2 additions & 5 deletions src/Middleware/ChaosHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/// <summary>
/// A <see cref="DelegatingHandler"/> implementation that is used for simulating server failures.
/// </summary>
public class ChaosHandler : DelegatingHandler, IDisposable

Check warning on line 25 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Fix this implementation of 'IDisposable' to conform to the dispose pattern. (https://rules.sonarsource.com/csharp/RSPEC-3881)

Check warning on line 25 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Fix this implementation of 'IDisposable' to conform to the dispose pattern. (https://rules.sonarsource.com/csharp/RSPEC-3881)

Check warning on line 25 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Fix this implementation of 'IDisposable' to conform to the dispose pattern. (https://rules.sonarsource.com/csharp/RSPEC-3881)
{
private readonly Random _random;
private readonly ChaosHandlerOption _chaosHandlerOptions;
Expand All @@ -36,7 +36,7 @@
public ChaosHandler(ChaosHandlerOption? chaosHandlerOptions = null)
{
_chaosHandlerOptions = chaosHandlerOptions ?? new ChaosHandlerOption();
_random = new Random(DateTime.Now.Millisecond);

Check warning on line 39 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make sure that using this pseudorandom number generator is safe here. (https://rules.sonarsource.com/csharp/RSPEC-2245)

Check warning on line 39 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make sure that using this pseudorandom number generator is safe here. (https://rules.sonarsource.com/csharp/RSPEC-2245)

Check warning on line 39 in src/Middleware/ChaosHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Make sure that using this pseudorandom number generator is safe here. (https://rules.sonarsource.com/csharp/RSPEC-2245)
LoadKnownFailures(_chaosHandlerOptions.KnownChaos);
}
/// <summary>
Expand All @@ -56,18 +56,16 @@

// Select global or per request options
var chaosHandlerOptions = request.GetRequestOption<ChaosHandlerOption>() ?? _chaosHandlerOptions;
ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(ChaosHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.chaos.enable", true);
}
else
{
activity = null;
activitySource = null;
}
try
{
Expand All @@ -94,7 +92,6 @@
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/Middleware/CompressionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,16 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
if(request == null)
throw new ArgumentNullException(nameof(request));

ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(CompressionHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.compression.enable", true);
}
else
{
activity = null;
activitySource = null;
}

try
Expand Down Expand Up @@ -79,7 +77,6 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/Middleware/HeadersInspectionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,22 @@
_defaultOptions = defaultOptions ?? new HeadersInspectionHandlerOption();
}
/// <inheritdoc/>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Check warning on line 31 in src/Middleware/HeadersInspectionHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 31 in src/Middleware/HeadersInspectionHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
if(request == null) throw new ArgumentNullException(nameof(request));

var options = request.GetRequestOption<HeadersInspectionHandlerOption>() ?? _defaultOptions;

ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(RedirectHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.headersInspection.enable", true);
}
else
{
activity = null;
activitySource = null;
}
try
{
Expand Down Expand Up @@ -79,7 +77,6 @@
finally
{
activity?.Dispose();
activitySource?.Dispose();
}

}
Expand Down
8 changes: 2 additions & 6 deletions src/Middleware/ParametersNameDecodingHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// ------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
Expand Down Expand Up @@ -35,15 +34,13 @@ public ParametersNameDecodingHandler(ParametersNameDecodingOption? options = def
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var options = request.GetRequestOption<ParametersNameDecodingOption>() ?? EncodingOptions;
ActivitySource? activitySource;
Activity? activity;
if (request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions) {
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
if (request.GetRequestOption<ObservabilityOptions>() is { } obsOptions) {
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource.StartActivity($"{nameof(ParametersNameDecodingHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.parameters_name_decoding.enable", true);
} else {
activity = null;
activitySource = null;
}
try {
if(!request.RequestUri!.Query.Contains('%') ||
Expand All @@ -60,7 +57,6 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
return base.SendAsync(request, cancellationToken);
} finally {
activity?.Dispose();
activitySource?.Dispose();
}
}
internal static string? DecodeUriEncodedString(string? original, char[] charactersToDecode) {
Expand Down
5 changes: 2 additions & 3 deletions src/Middleware/RedirectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/// <param name="request">The <see cref="HttpRequestMessage"/> to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/>for the request.</param>
/// <returns>The <see cref="HttpResponseMessage"/>.</returns>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Check warning on line 44 in src/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 33 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 44 in src/Middleware/RedirectHandler.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 33 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
if(request == null) throw new ArgumentNullException(nameof(request));

Expand All @@ -49,9 +49,9 @@

ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(RedirectHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.redirect.enable", true);
}
Expand Down Expand Up @@ -157,7 +157,6 @@
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/Middleware/RetryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
var retryOption = request.GetRequestOption<RetryHandlerOption>() ?? RetryOption;
ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(RetryHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.retry.enable", true);
}
Expand All @@ -83,7 +83,6 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}

Expand Down
8 changes: 2 additions & 6 deletions src/Middleware/UriReplacementHandler.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Http.HttpClientLibrary.Extensions;
using Microsoft.Kiota.Http.HttpClientLibrary.Middleware.Options;

Expand All @@ -28,18 +27,16 @@ public UriReplacementHandler(TUriReplacementHandlerOption uriReplacement)
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
ActivitySource? activitySource;
Activity? activity;
if(request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions)
if(request.GetRequestOption<ObservabilityOptions>() is { } obsOptions)
{
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource.StartActivity($"{nameof(UriReplacementHandler<TUriReplacementHandlerOption>)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.uri_replacement.enable", uriReplacement.IsEnabled());
}
else
{
activity = null;
activitySource = null;
}

try
Expand All @@ -50,7 +47,6 @@ protected override async Task<HttpResponseMessage> SendAsync(
finally
{
activity?.Dispose();
activitySource?.Dispose();
}
}
}
7 changes: 2 additions & 5 deletions src/Middleware/UserAgentHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
if(request == null)
throw new ArgumentNullException(nameof(request));

ActivitySource? activitySource;
Activity? activity;
if (request.GetRequestOption<ObservabilityOptions>() is ObservabilityOptions obsOptions) {
activitySource = new ActivitySource(obsOptions.TracerInstrumentationName);
if (request.GetRequestOption<ObservabilityOptions>() is { } obsOptions) {
var activitySource = ActivitySourceRegistry.DefaultInstance.GetOrCreateActivitySource(obsOptions.TracerInstrumentationName);
activity = activitySource?.StartActivity($"{nameof(UserAgentHandler)}_{nameof(SendAsync)}");
activity?.SetTag("com.microsoft.kiota.handler.useragent.enable", true);
} else {
activity = null;
activitySource = null;
}
try {
var userAgentHandlerOption = request.GetRequestOption<UserAgentHandlerOption>() ?? _userAgentOption;
Expand All @@ -51,7 +49,6 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
return base.SendAsync(request, cancellationToken);
} finally {
activity?.Dispose();
activitySource?.Dispose();
}
}
}
Expand Down