Skip to content

Commit a3b57f9

Browse files
authored
Resilience telemetry API (App-vNext#1138)
1 parent 6c80878 commit a3b57f9

30 files changed

+878
-32
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ tab_width = 2
1818
[*.cs]
1919

2020
# Put any C# specific settings here
21+
dotnet_code_quality.CA1062.null_check_validation_methods = NotNull

.github/dependabot.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ updates:
2121
- dependency-name: "System.ValueTuple"
2222
- dependency-name: "Microsoft.Extensions.Options"
2323
- dependency-name: "Microsoft.Extensions.Logging.Abstractions"
24+
- dependency-name: "Microsoft.Extensions.Logging"
2425
- dependency-name: "System.Diagnostics.DiagnosticSource"
2526
- dependency-name: "System.Threading.RateLimiting"

src/Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="7.0.1" />
88
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
99
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
10+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="7.0.0" />
1011
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
1112
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
1213
<PackageVersion Include="Moq" Version="4.18.4" />
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Polly.Registry;
2+
3+
namespace Polly.Core.Tests.Registry;
4+
public class ResilienceStrategyRegistryOptionsTests
5+
{
6+
[Fact]
7+
public void Ctor_EnsureDefaults()
8+
{
9+
ResilienceStrategyRegistryOptions<object> options = new();
10+
11+
options.KeyFormatter.Should().NotBeNull();
12+
options.KeyFormatter(null!).Should().Be("");
13+
options.KeyFormatter("ABC").Should().Be("ABC");
14+
}
15+
}

src/Polly.Core.Tests/Registry/ResilienceStrategyRegistryTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.ComponentModel.DataAnnotations;
22
using System.Globalization;
33
using Polly.Registry;
4+
using Polly.Telemetry;
45

56
namespace Polly.Core.Tests.Registry;
67

@@ -117,6 +118,24 @@ public void AddBuilder_GetStrategy_EnsureCalled()
117118
strategies.Keys.Should().HaveCount(3);
118119
}
119120

121+
[Fact]
122+
public void AddBuilder_EnsureStrategyKey()
123+
{
124+
var called = false;
125+
var registry = CreateRegistry();
126+
registry.TryAddBuilder(StrategyId.Create("A"), (key, builder) =>
127+
{
128+
builder.AddStrategy(new TestResilienceStrategy());
129+
builder.Properties.TryGetValue(TelemetryUtil.StrategyKey, out var val).Should().BeTrue();
130+
val.Should().Be(key.ToString());
131+
called = true;
132+
});
133+
134+
registry.Get(StrategyId.Create("A", "Instance1"));
135+
136+
called.Should().BeTrue();
137+
}
138+
120139
[Fact]
121140
public void TryGet_NoBuilder_Null()
122141
{

src/Polly.Core.Tests/ResilienceContextTests.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ public void Get_EnsureDefaults()
1717
}
1818

1919
[Fact]
20-
public void Get_EnsurePooled()
20+
public async Task Get_EnsurePooled()
2121
{
22-
var context = ResilienceContext.Get();
22+
await TestUtils.AssertWithTimeoutAsync(() =>
23+
{
24+
var context = ResilienceContext.Get();
2325

24-
ResilienceContext.Return(context);
26+
ResilienceContext.Return(context);
2527

26-
ResilienceContext.Get().Should().BeSameAs(context);
28+
ResilienceContext.Get().Should().BeSameAs(context);
29+
});
2730
}
2831

2932
[Fact]
@@ -33,17 +36,20 @@ public void Return_Null_Throws()
3336
}
3437

3538
[Fact]
36-
public void Return_EnsureDefaults()
39+
public async Task Return_EnsureDefaults()
3740
{
38-
using var cts = new CancellationTokenSource();
39-
var context = ResilienceContext.Get();
40-
context.CancellationToken = cts.Token;
41-
context.Initialize<bool>(true);
42-
context.CancellationToken.Should().Be(cts.Token);
43-
context.Properties.Set(new ResiliencePropertyKey<int>("abc"), 10);
44-
ResilienceContext.Return(context);
41+
await TestUtils.AssertWithTimeoutAsync(() =>
42+
{
43+
using var cts = new CancellationTokenSource();
44+
var context = ResilienceContext.Get();
45+
context.CancellationToken = cts.Token;
46+
context.Initialize<bool>(true);
47+
context.CancellationToken.Should().Be(cts.Token);
48+
context.Properties.Set(new ResiliencePropertyKey<int>("abc"), 10);
49+
ResilienceContext.Return(context);
4550

46-
AssertDefaults(context);
51+
AssertDefaults(context);
52+
});
4753
}
4854

4955
[InlineData(true)]

src/Polly.Core.Tests/Strategy/ResilienceStrategyTelemetryTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ public void Report_NoOutcomeWhenNotSubscribed_None()
5050
_diagnosticSource.VerifyNoOtherCalls();
5151
}
5252

53+
[Fact]
54+
public void ResilienceStrategyTelemetry_NoDiagnosticSource_Ok()
55+
{
56+
var source = new ResilienceTelemetrySource("builder", new ResilienceProperties(), "strategy-name", "strategy-type");
57+
var sut = new ResilienceStrategyTelemetry(source, null);
58+
59+
sut.Invoking(s => s.Report("dummy", new TestArguments())).Should().NotThrow();
60+
sut.Invoking(s => s.Report("dummy", new Outcome<int>(1), new TestArguments())).Should().NotThrow();
61+
}
62+
5363
[Fact]
5464
public void Report_Outcome_OK()
5565
{

src/Polly.Core.Tests/Telemetry/TelemetryUtilTests.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ namespace Polly.Core.Tests.Telemetry;
66

77
public class TelemetryUtilTests
88
{
9+
[Fact]
10+
public void Ctor_Ok()
11+
{
12+
TelemetryUtil.DiagnosticSourceKey.Key.Should().Be("DiagnosticSource");
13+
TelemetryUtil.StrategyKey.Key.Should().Be("StrategyKey");
14+
}
15+
916
[Fact]
1017
public void CreateResilienceTelemetry_Ok()
1118
{
@@ -14,9 +21,7 @@ public void CreateResilienceTelemetry_Ok()
1421
telemetry.TelemetrySource.BuilderName.Should().Be("builder");
1522
telemetry.TelemetrySource.StrategyName.Should().Be("strategy-name");
1623
telemetry.TelemetrySource.StrategyType.Should().Be("strategy-type");
17-
telemetry.DiagnosticSource.Should().NotBeNull();
18-
19-
telemetry.DiagnosticSource.Should().BeOfType<DiagnosticListener>().Subject.Name.Should().Be("Polly");
24+
telemetry.DiagnosticSource.Should().BeNull();
2025
}
2126

2227
[Fact]

src/Polly.Core/Polly.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<Using Include="Polly.Utils" />
1919
<Using Remove="System.Net.Http" />
2020
<InternalsVisibleToTest Include="Polly.Core.Tests" />
21+
<InternalsVisibleToTest Include="Polly.Extensions" />
2122
</ItemGroup>
2223

2324
<ItemGroup>

src/Polly.Core/Registry/ResilienceStrategyRegistry.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics.CodeAnalysis;
2+
using Polly.Telemetry;
23

34
namespace Polly.Registry;
45

@@ -20,6 +21,7 @@ public sealed class ResilienceStrategyRegistry<TKey> : ResilienceStrategyProvide
2021
private readonly Func<ResilienceStrategyBuilder> _activator;
2122
private readonly ConcurrentDictionary<TKey, Action<TKey, ResilienceStrategyBuilder>> _builders;
2223
private readonly ConcurrentDictionary<TKey, ResilienceStrategy> _strategies;
24+
private readonly Func<TKey, string> _keyFormatter;
2325

2426
/// <summary>
2527
/// Initializes a new instance of the <see cref="ResilienceStrategyRegistry{TKey}"/> class with the default comparer.
@@ -42,6 +44,7 @@ public ResilienceStrategyRegistry(ResilienceStrategyRegistryOptions<TKey> option
4244
_activator = options.BuilderFactory;
4345
_builders = new ConcurrentDictionary<TKey, Action<TKey, ResilienceStrategyBuilder>>(options.BuilderComparer);
4446
_strategies = new ConcurrentDictionary<TKey, ResilienceStrategy>(options.StrategyComparer);
47+
_keyFormatter = options.KeyFormatter;
4548
}
4649

4750
/// <summary>
@@ -86,6 +89,7 @@ public override bool TryGet(TKey key, [NotNullWhen(true)] out ResilienceStrategy
8689
strategy = _strategies.GetOrAdd(key, key =>
8790
{
8891
var builder = _activator();
92+
builder.Properties.Set(TelemetryUtil.StrategyKey, _keyFormatter(key));
8993
configure(key, builder);
9094
return builder.Build();
9195
});

0 commit comments

Comments
 (0)