Skip to content

Commit

Permalink
[Azure] Application Insights Sampler (open-telemetry#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
Austin-Tan authored Aug 23, 2021
1 parent 5b147dd commit 739c6b7
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 0 deletions.
14 changes: 14 additions & 0 deletions opentelemetry-dotnet-contrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Previ
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests", "test\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests.csproj", "{4172D671-3AAC-443F-9EB0-A6608B154AF0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AzureMonitor", "src\OpenTelemetry.Contrib.Extensions.AzureMonitor\OpenTelemetry.Contrib.Extensions.AzureMonitor.csproj", "{9C2D6D1A-8580-4527-B718-E206D0690635}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests", "test\OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests\OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests.csproj", "{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -261,6 +265,14 @@ Global
{4172D671-3AAC-443F-9EB0-A6608B154AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4172D671-3AAC-443F-9EB0-A6608B154AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4172D671-3AAC-443F-9EB0-A6608B154AF0}.Release|Any CPU.Build.0 = Release|Any CPU
{9C2D6D1A-8580-4527-B718-E206D0690635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C2D6D1A-8580-4527-B718-E206D0690635}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C2D6D1A-8580-4527-B718-E206D0690635}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9C2D6D1A-8580-4527-B718-E206D0690635}.Release|Any CPU.Build.0 = Release|Any CPU
{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -300,6 +312,8 @@ Global
{B978939B-278C-43A3-AD12-32EA9BBD27D0} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{D2C68560-C252-41A9-B742-2CEB7D760E0F} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{4172D671-3AAC-443F-9EB0-A6608B154AF0} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{9C2D6D1A-8580-4527-B718-E206D0690635} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{771651AA-010F-4FF6-9CB2-BAFFE5E04FC2} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// <copyright file="ApplicationInsightsSampler.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
using System;

using OpenTelemetry.Trace;

namespace OpenTelemetry.Contrib.Extensions.AzureMonitor
{
/// <summary>
/// Sample configurable for OpenTelemetry exporters for compatibility
/// with Application Insight SDKs.
/// </summary>
public class ApplicationInsightsSampler : Sampler
{
private readonly float samplingRatio;

/// <summary>
/// Initializes a new instance of the <see cref="ApplicationInsightsSampler"/> class.
/// </summary>
/// <param name="samplingRatio">Ratio of telemetry that should be sampled.</param>
public ApplicationInsightsSampler(float samplingRatio)
{
// Ensure passed ratio is between 0 and 1, inclusive
if (samplingRatio < 0 || samplingRatio > 1)
{
throw new ArgumentOutOfRangeException(nameof(samplingRatio), "Ratio must be between 0 and 1, inclusive.");
}

this.samplingRatio = samplingRatio;
this.Description = "ApplicationInsightsSampler{" + samplingRatio + "}";
}

/// <summary>
/// Computational method using the DJB2 Hash algorithm to decide whether to sample
/// a given telemetry item, based on its Trace Id.
/// </summary>
/// <param name="samplingParameters">Parameters of telemetry item used to make sampling decision.</param>
/// <returns>Returns whether or not we should sample telemetry in the form of a <see cref="SamplingResult"/> class.</returns>
public override SamplingResult ShouldSample(in SamplingParameters samplingParameters)
{
double sampleScore = DJB2SampleScore(samplingParameters.TraceId.ToHexString().ToLowerInvariant());
return new SamplingResult(sampleScore < this.samplingRatio);
}

private static double DJB2SampleScore(string traceIdHex)
{
// Calculate DJB2 hash code from hex-converted TraceId
int hash = 5381;

for (int i = 0; i < traceIdHex.Length; i++)
{
hash = ((hash << 5) + hash) + (int)traceIdHex[i];
}

// Take the absolute value of the hash
if (hash == int.MinValue)
{
hash = int.MaxValue;
}
else
{
hash = Math.Abs(hash);
}

// Divide by MaxValue for value between 0 and 1 for sampling score
double samplingScore = (double)hash / int.MaxValue;
return samplingScore;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net461;net5.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="[1.0.1,2.0.0)" />
</ItemGroup>

</Project>
15 changes: 15 additions & 0 deletions src/OpenTelemetry.Contrib.Extensions.AzureMonitor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Application Insights Sampler for OpenTelemetry .NET

The ```Application Insights Sampler``` should be utilized when
compatibility with Application Insights SDKs is desired, as it
implements the same hash algorithm when deciding to sample telemetry.

## Installation

```shell
dotnet add package OpenTelemetry.Contrib.Extensions.AzureMonitor
```

## References

* [OpenTelemetry Project](https://opentelemetry.io/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// <copyright file="ApplicationInsightsSamplerTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Diagnostics;

using OpenTelemetry.Trace;
using Xunit;

namespace OpenTelemetry.Contrib.Extensions.AzureMonitor.Tests
{
public class ApplicationInsightsSamplerTests
{
[Fact]
public void VerifyHashAlgorithmCorrectness()
{
byte[] testBytes = new byte[]
{
0x8F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
0, 0, 0, 0, 0, 0, 0, 0,
};
byte[] testBytes2 = new byte[]
{
0x0F, 0x1F, 0x2F, 0x3F,
0x4F, 0x5F, 0x6F, 0x7F,
0x8F, 0x9F, 0xAF, 0xBF,
0xCF, 0xDF, 0xEF, 0xFF,
};
ActivityTraceId testId = ActivityTraceId.CreateFromBytes(testBytes);
ActivityTraceId testId2 = ActivityTraceId.CreateFromBytes(testBytes2);

ActivityContext parentContext = default(ActivityContext);
SamplingParameters testParams = new SamplingParameters(parentContext, testId, "TestActivity", ActivityKind.Internal);
SamplingParameters testParams2 = new SamplingParameters(parentContext, testId2, "TestActivity", ActivityKind.Internal);

var zeroSampler = new ApplicationInsightsSampler(0);
ApplicationInsightsSampler oneSampler = new ApplicationInsightsSampler(1);

// 0.86 is below the sample score for testId1, but strict enough to drop testId2
ApplicationInsightsSampler ratioSampler = new ApplicationInsightsSampler(0.86f);

Assert.Equal(SamplingDecision.Drop, zeroSampler.ShouldSample(testParams).Decision);
Assert.Equal(SamplingDecision.Drop, zeroSampler.ShouldSample(testParams2).Decision);

Assert.Equal(SamplingDecision.RecordAndSample, oneSampler.ShouldSample(testParams).Decision);
Assert.Equal(SamplingDecision.RecordAndSample, oneSampler.ShouldSample(testParams2).Decision);

Assert.Equal(SamplingDecision.Drop, ratioSampler.ShouldSample(testParams).Decision);
Assert.Equal(SamplingDecision.RecordAndSample, ratioSampler.ShouldSample(testParams2).Decision);
}

[Fact]
public void ApplicationInsightsSamplerGoodArgs()
{
ApplicationInsightsSampler pointFiveSampler = new ApplicationInsightsSampler(0.5f);
Assert.NotNull(pointFiveSampler);

ApplicationInsightsSampler zeroSampler = new ApplicationInsightsSampler(0f);
Assert.NotNull(zeroSampler);

ApplicationInsightsSampler oneSampler = new ApplicationInsightsSampler(1f);
Assert.NotNull(oneSampler);
}

[Fact]
public void ApplicationInsightsSamplerBadArgs()
{
Assert.Throws<ArgumentOutOfRangeException>(() => new ApplicationInsightsSampler(-2f));
Assert.Throws<ArgumentOutOfRangeException>(() => new ApplicationInsightsSampler(2f));
}

[Fact]
public void GetDescriptionMatchesSpec()
{
var expectedDescription = "ApplicationInsightsSampler{0.5}";
Assert.Equal(expectedDescription, new ApplicationInsightsSampler(0.5f).Description);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
<PackageReference Include="OpenTelemetry" Version="[1.0.1,2.0.0)" />
<PackageReference Include="xunit" Version="2.4.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\OpenTelemetry.Contrib.Extensions.AzureMonitor\OpenTelemetry.Contrib.Extensions.AzureMonitor.csproj" />
</ItemGroup>

</Project>

0 comments on commit 739c6b7

Please sign in to comment.