Skip to content

Commit

Permalink
[AzureMonitorExporter] transmitter factory (#34354)
Browse files Browse the repository at this point in the history
* transmitter factory

* cleanup
  • Loading branch information
TimothyMothra authored Feb 21, 2023
1 parent 5a0d69b commit 1875198
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class AzureMonitorLogExporter : BaseExporter<LogRecord>
private readonly AzureMonitorPersistentStorage? _persistentStorage;
private AzureMonitorResource? _resource;

public AzureMonitorLogExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(new AzureMonitorTransmitter(options, credential))
public AzureMonitorLogExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(TransmitterFactory.Instance.Get(options, credential))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class AzureMonitorMetricExporter : BaseExporter<Metric>
private readonly AzureMonitorPersistentStorage? _persistentStorage;
private AzureMonitorResource? _resource;

public AzureMonitorMetricExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(new AzureMonitorTransmitter(options, credential))
public AzureMonitorMetricExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(TransmitterFactory.Instance.Get(options, credential))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal class AzureMonitorTraceExporter : BaseExporter<Activity>
private readonly AzureMonitorPersistentStorage? _persistentStorage;
private AzureMonitorResource? _resource;

public AzureMonitorTraceExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(new AzureMonitorTransmitter(options, credential))
public AzureMonitorTraceExporter(AzureMonitorExporterOptions options, TokenCredential? credential = null) : this(TransmitterFactory.Instance.Get(options, credential))
{
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Collections.Generic;
using Azure.Core;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals
{
/// <summary>
/// This Factory encapsulates the <see cref="AzureMonitorTransmitter"/>.
/// An ideal users will create a single exporter for each signal (Logs, Metrics, Traces).
/// This factory should ensure that only one instance of the Transmitter is created for
/// any unique connection string.
/// </summary>
internal class TransmitterFactory
{
public static TransmitterFactory Instance = new();

internal readonly Dictionary<string, AzureMonitorTransmitter> _transmitters = new();
private readonly object _lockObj = new();

public AzureMonitorTransmitter Get(AzureMonitorExporterOptions azureMonitorExporterOptions, TokenCredential? tokenCredential = null)
{
var key = azureMonitorExporterOptions.ConnectionString ?? string.Empty;

if (!_transmitters.TryGetValue(key, out AzureMonitorTransmitter transmitter))
{
lock (_lockObj)
{
if (!_transmitters.TryGetValue(key, out transmitter))
{
transmitter = new AzureMonitorTransmitter(azureMonitorExporterOptions, tokenCredential);

_transmitters.Add(key, transmitter);
}
}
}

return transmitter;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Monitor.OpenTelemetry.Exporter.Internals;
using Xunit;

namespace Azure.Monitor.OpenTelemetry.Exporter.Tests
{
public class TransmitterFactoryTests
{
/// <summary>
/// Users may not specify their connection string in the <see cref="AzureMonitorExporterOptions"/>.
/// In this scenario, the sdk would use the environment variable.
///
/// This test confirms that the factory correctly handles a null value.
/// </summary>
[Fact]
public void VerifyNullConnectionString()
{
Environment.SetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING", "InstrumentationKey=00000000-0000-0000-0000-000000000000;");

try
{
var factory = new TransmitterFactory();
var options = new AzureMonitorExporterOptions();

var transmitter = factory.Get(options);

Assert.Single(factory._transmitters);
Assert.True(factory._transmitters.ContainsKey(string.Empty));
}
finally
{
Environment.SetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING", null);
}
}

[Fact]
public void VerifyRepeatedCallsGenerateOnlyOneTransmitter()
{
var factory = new TransmitterFactory();
var options = new AzureMonitorExporterOptions { ConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000;" };

var transmitter1 = factory.Get(options);
var transmitter2 = factory.Get(options);

Assert.Single(factory._transmitters);
Assert.True(factory._transmitters.ContainsKey(options.ConnectionString));
Assert.Equal(transmitter1, transmitter2);
}
}
}

0 comments on commit 1875198

Please sign in to comment.