Skip to content

Commit 41d6d30

Browse files
ncipollinaclaude
andauthored
feat: add DynamoDB Distributed Lock Observability package (#30)
- Add DynamoDb.DistributedLock.Observability project with OpenTelemetry integration - Implement MeterProviderBuilder extension methods for easy metrics configuration - Update README with comprehensive observability documentation - Add observability package to solution and build configuration - Update CLAUDE.md with new architecture components 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3b04a65 commit 41d6d30

8 files changed

Lines changed: 148 additions & 35 deletions

File tree

CLAUDE.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ This is a .NET library that provides distributed locking using Amazon DynamoDB.
1313
- **DynamoDbLockOptions** (src/DynamoDb.DistributedLock/DynamoDbLockOptions.cs): Configuration options with retry settings
1414
- **Retry System** (src/DynamoDb.DistributedLock/Retry/): Exponential backoff retry policy with jitter
1515
- **Dependency Injection** (src/DynamoDb.DistributedLock/Extensions/ServiceCollectionExtensions.cs): Configuration binding and service registration
16+
- **Metrics** (src/DynamoDb.DistributedLock/Metrics/): System.Diagnostics.Metrics integration for observability
17+
- **Observability Package** (src/DynamoDb.DistributedLock.Observability/): OpenTelemetry integration extensions
1618

1719
## Development Commands
1820

@@ -55,8 +57,11 @@ dotnet pack --configuration Release
5557

5658
- The library uses **Directory.Build.props** for shared MSBuild properties
5759
- Test projects are automatically excluded from packing via `IsTestProject=true`
58-
- Both main library and tests target .NET 8.0 and 9.0
60+
- Both main library and observability package target .NET 8.0 and 9.0
5961
- Tests use `UseMicrosoftTestingPlatformRunner=true` for modern test execution
62+
- The solution contains two projects:
63+
- **DynamoDb.DistributedLock**: Core distributed lock library
64+
- **DynamoDb.DistributedLock.Observability**: OpenTelemetry integration extensions
6065

6166
## Code Patterns
6267

Directory.Build.props

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionPrefix>1.2.0</VersionPrefix>
3+
<VersionPrefix>1.2.1</VersionPrefix>
44
<PackageLicenseExpression>MIT</PackageLicenseExpression>
55

66
<!-- Other useful metadata -->
77
<RepositoryUrl>https://github.com/LayeredCraft/dynamodb-distributed-lock</RepositoryUrl>
88
<RepositoryType>git</RepositoryType>
99
<Authors>Nick Cipollina</Authors>
1010
<PackageProjectUrl>https://github.com/LayeredCraft/dynamodb-distributed-lock</PackageProjectUrl>
11+
<!-- NuGet.org specific -->
12+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
13+
<IsPackable>true</IsPackable>
14+
<IncludeSymbols>true</IncludeSymbols>
15+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
16+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
17+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
1118
</PropertyGroup>
1219
<!-- Globally disable packing for any test project that sets IsTestProject=true -->
1320
<PropertyGroup Condition="'$(IsTestProject)' == 'true'">
1421
<IsPackable>false</IsPackable>
1522
</PropertyGroup>
1623

24+
<ItemGroup>
25+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
26+
<PrivateAssets>all</PrivateAssets>
27+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
28+
</PackageReference>
29+
</ItemGroup>
30+
1731
</Project>

DynamoDb.DistributedLock.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
2828
EndProject
2929
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamoDb.DistributedLock.Tests", "test\DynamoDb.DistributedLock.Tests\DynamoDb.DistributedLock.Tests.csproj", "{1D5ED003-9182-475F-8FAB-3701D2BC900C}"
3030
EndProject
31+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamoDb.DistributedLock.Observability", "src\DynamoDb.DistributedLock.Observability\DynamoDb.DistributedLock.Observability.csproj", "{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD}"
32+
EndProject
3133
Global
3234
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3335
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,7 @@ Global
3941
GlobalSection(NestedProjects) = preSolution
4042
{AA9CF908-DFDA-4959-9B32-83FA714B8A79} = {33AD2D83-0DD4-4EE5-876F-25FF078D881E}
4143
{1D5ED003-9182-475F-8FAB-3701D2BC900C} = {3DE24C5E-21FE-4689-AD86-2301A2DF465F}
44+
{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD} = {33AD2D83-0DD4-4EE5-876F-25FF078D881E}
4245
EndGlobalSection
4346
GlobalSection(ProjectConfigurationPlatforms) = postSolution
4447
{AA9CF908-DFDA-4959-9B32-83FA714B8A79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -49,5 +52,9 @@ Global
4952
{1D5ED003-9182-475F-8FAB-3701D2BC900C}.Debug|Any CPU.Build.0 = Debug|Any CPU
5053
{1D5ED003-9182-475F-8FAB-3701D2BC900C}.Release|Any CPU.ActiveCfg = Release|Any CPU
5154
{1D5ED003-9182-475F-8FAB-3701D2BC900C}.Release|Any CPU.Build.0 = Release|Any CPU
55+
{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56+
{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
57+
{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
58+
{B2E3F4A6-8C9D-4E7B-A1F5-2D3C6789ABCD}.Release|Any CPU.Build.0 = Release|Any CPU
5259
EndGlobalSection
5360
EndGlobal

README.md

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515

1616
---
1717

18-
## 📦 Package
18+
## 📦 Packages
1919

2020
| Package | Build | NuGet | Downloads |
2121
|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
2222
| **DynamoDb.DistributedLock** | [![Build](https://github.com/LayeredCraft/dynamodb-distributed-lock/actions/workflows/build.yaml/badge.svg)](https://github.com/LayeredCraft/dynamodb-distributed-lock/actions/workflows/build.yaml) | [![NuGet](https://img.shields.io/nuget/v/DynamoDb.DistributedLock.svg)](https://www.nuget.org/packages/DynamoDb.DistributedLock) | [![NuGet Downloads](https://img.shields.io/nuget/dt/DynamoDb.DistributedLock.svg)](https://www.nuget.org/packages/DynamoDb.DistributedLock) |
23+
| **DynamoDb.DistributedLock.Observability** | [![Build](https://github.com/LayeredCraft/dynamodb-distributed-lock/actions/workflows/build.yaml/badge.svg)](https://github.com/LayeredCraft/dynamodb-distributed-lock/actions/workflows/build.yaml) | [![NuGet](https://img.shields.io/nuget/v/DynamoDb.DistributedLock.Observability.svg)](https://www.nuget.org/packages/DynamoDb.DistributedLock.Observability) | [![NuGet Downloads](https://img.shields.io/nuget/dt/DynamoDb.DistributedLock.Observability.svg)](https://www.nuget.org/packages/DynamoDb.DistributedLock.Observability) |
2324

2425
---
2526

@@ -225,32 +226,66 @@ However, the partition and sort key attribute names are fully configurable via `
225226
226227
---
227228

228-
## 📈 Telemetry
229+
## 📈 Observability
229230

230-
This library uses [System.Diagnostics.Metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics) `Meter`s to collect metrics. These metrics can be opted in to be exported to your preferred telemetry system (e.g., OpenTelemetry, console output) using standard dotnet telemetry exporters.
231+
This library uses [System.Diagnostics.Metrics](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics) to collect telemetry data. These metrics can be exported to your preferred observability system (e.g., OpenTelemetry, Prometheus, console output) using standard .NET telemetry exporters.
231232

232233
A full list of metric names can be found in the [MetricNames](src/DynamoDb.DistributedLock/Metrics/MetricNames.cs) class.
233234

234-
By default no metrics are exported to your collector, but you can enable them by configuring the `Meter` in your application:
235+
### 📦 Observability Package (Recommended)
236+
237+
For simplified OpenTelemetry integration, install the observability package:
238+
239+
```bash
240+
dotnet add package DynamoDb.DistributedLock.Observability
241+
```
242+
243+
Then configure metrics collection with a single method call:
244+
245+
```csharp
246+
using DynamoDb.DistributedLock.Observability;
247+
248+
services.AddOpenTelemetry()
249+
.WithMetrics(metrics =>
250+
metrics
251+
// Automatically registers the DynamoDB distributed lock meter
252+
.AddDynamoDbDistributedLock()
253+
// Configure your preferred exporter, e.g., OpenTelemetry Protocol (OTLP)
254+
.AddOtlpExporter(options => options.Endpoint = otlpEndpoint)
255+
);
256+
```
257+
258+
### 🔧 Manual Configuration
259+
260+
If you prefer manual configuration without the observability package:
235261

236-
[OpenTelemetry](https://opentelemetry.io/docs/languages/dotnet/)
237262
```csharp
238263
services.AddOpenTelemetry()
239264
.WithMetrics(metrics =>
240265
metrics
241-
// There is only one meter used by this library
242-
// and this constant value refers to its name
243-
// this causes the telemetry system to collect metrics emitted during lock operations
266+
// Register the meter name manually
244267
.AddMeter(DynamoDb.DistributedLock.Metrics.MetricNames.MeterName)
245-
// Views can be used to filter out any metrics you do not want to collect
246-
// while still collecting all metrics from the Meter
268+
// Views can be used to filter out specific metrics
247269
.AddView(DynamoDb.DistributedLock.Metrics.MetricNames.LockReleaseTimer, MetricStreamConfiguration.Drop)
248-
// Configure your preferred exporter, e.g., OpenTelemetry Protocol (OTLP)
270+
// Configure your preferred exporter
249271
.AddOtlpExporter(options => options.Endpoint = otlpEndpoint)
250272
);
251273
```
252274

253-
Other options for collection of metrics are available, including local development options such as [dotnet-counters](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) or the [.Net Aspire standalone dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone)
275+
### 🔍 Available Metrics
276+
277+
The library emits the following metrics for observability:
278+
279+
- **Lock acquisition counters**: Track successful and failed lock acquisitions
280+
- **Lock release counters**: Track successful and failed lock releases
281+
- **Retry counters**: Track retry attempts and exhausted retries
282+
- **Timing histograms**: Measure lock acquisition and release durations
283+
284+
### 🧪 Local Development
285+
286+
Other options for metrics collection during development:
287+
- [dotnet-counters](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) for real-time metrics viewing
288+
- [.NET Aspire dashboard](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/standalone) for local observability
254289

255290
---
256291

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Nullable>enable</Nullable>
5+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
6+
<LangVersion>default</LangVersion>
7+
8+
<PackageId>DynamoDb.DistributedLock.Observability</PackageId>
9+
<Title>DynamoDb.DistributedLock.Observability</Title>
10+
<Description>OpenTelemetry integration for DynamoDb.DistributedLock metrics collection.</Description>
11+
<PackageTags>dynamodb;distributed-lock;observability;opentelemetry;metrics;aws;serverless</PackageTags>
12+
<PackageIcon>icon.png</PackageIcon>
13+
<PackageReadmeFile>README.md</PackageReadmeFile>
14+
<Copyright>Copyright © 2025 Nick Cipollina</Copyright>
15+
16+
<!-- Documentation -->
17+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
18+
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<PackageReference Include="OpenTelemetry" Version="1.12.0" />
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<ProjectReference Include="..\DynamoDb.DistributedLock\DynamoDb.DistributedLock.csproj" />
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<None Include="..\..\icon.png" Pack="true" PackagePath="" Visible="False"/>
31+
<None Include="..\..\README.md" Pack="true" PackagePath="" Visible="False"/>
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
36+
<_Parameter1>DynamoDb.DistributedLock.Observability.Tests</_Parameter1>
37+
</AssemblyAttribute>
38+
</ItemGroup>
39+
40+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using DynamoDb.DistributedLock.Metrics;
3+
using OpenTelemetry.Metrics;
4+
5+
namespace DynamoDb.DistributedLock.Observability;
6+
7+
/// <summary>
8+
/// Extension methods for <see cref="MeterProviderBuilder"/> to add DynamoDB Distributed Lock metrics.
9+
/// </summary>
10+
public static class MeterProviderBuilderExtensions
11+
{
12+
/// <summary>
13+
/// Enables metrics collection for DynamoDB Distributed Lock operations.
14+
/// This includes counters for lock acquisition/release events and histograms for timing measurements.
15+
/// </summary>
16+
/// <param name="builder">The <see cref="MeterProviderBuilder"/> to configure.</param>
17+
/// <returns>The same <paramref name="builder"/> instance for chaining.</returns>
18+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> is null.</exception>
19+
public static MeterProviderBuilder AddDynamoDbDistributedLock(this MeterProviderBuilder builder)
20+
{
21+
ArgumentNullException.ThrowIfNull(builder);
22+
23+
return builder.AddMeter(MetricNames.MeterName);
24+
}
25+
}

src/DynamoDb.DistributedLock/DynamoDb.DistributedLock.csproj

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,17 @@
1313
<PackageReadmeFile>README.md</PackageReadmeFile>
1414
<Copyright>Copyright © 2025 Nick Cipollina</Copyright>
1515

16-
<!-- NuGet.org specific -->
17-
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
18-
<IsPackable>true</IsPackable>
19-
<IncludeSymbols>true</IncludeSymbols>
20-
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
21-
<PublishRepositoryUrl>true</PublishRepositoryUrl>
22-
<EmbedUntrackedSources>true</EmbedUntrackedSources>
23-
2416
<!-- Documentation -->
2517
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2618
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
2719
</PropertyGroup>
2820

2921
<ItemGroup>
30-
<PackageReference Include="AWSSDK.DynamoDBv2" Version="4.0.3.1" />
31-
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.2" />
32-
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.7" />
33-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.7" />
34-
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.7" />
35-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
36-
<PrivateAssets>all</PrivateAssets>
37-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
38-
</PackageReference>
22+
<PackageReference Include="AWSSDK.DynamoDBv2" Version="4.0.5.1" />
23+
<PackageReference Include="AWSSDK.Extensions.NETCore.Setup" Version="4.0.2.2" />
24+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.8" />
25+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.8" />
26+
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.8" />
3927
</ItemGroup>
4028

4129
<ItemGroup>

test/DynamoDb.DistributedLock.Tests/DynamoDb.DistributedLock.Tests.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,13 @@
2626
<PackageReference Include="AutoFixture.AutoNSubstitute" Version="4.18.1"/>
2727
<PackageReference Include="AutoFixture.Xunit3" Version="4.19.0"/>
2828
<PackageReference Include="AwesomeAssertions" Version="9.1.0"/>
29-
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.7"/>
30-
<PackageReference Include="Microsoft.Extensions.Diagnostics" Version="9.0.7" />
29+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.8" />
3130
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" Version="9.8.0" />
3231
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
3332
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.14.2" />
3433
<PackageReference Include="NSubstitute" Version="5.3.0"/>
35-
<PackageReference Include="xunit.v3" Version="3.0.0"/>
36-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
34+
<PackageReference Include="xunit.v3" Version="3.0.1" />
35+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
3736
<PrivateAssets>all</PrivateAssets>
3837
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3938
</PackageReference>

0 commit comments

Comments
 (0)