Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure Monitor Exporter - Adopt OpenTelemetry .NET beta2 changes #14687

Merged
merged 3 commits into from
Aug 29, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,26 @@ namespace OpenTelemetry.Exporter.AzureMonitor
/// <summary>
/// test
/// </summary>
public static class TracerProviderBuilderExtensions
public static class AzureMonitorExporterHelperExtensions
{
/// <summary>
/// Registers an Azure Monitor trace exporter that will receive <see cref="System.Diagnostics.Activity"/> instances.
/// </summary>
/// <param name="builder"><see cref="TracerProviderBuilder"/> builder to use.</param>
/// <param name="configure">Exporter configuration options.</param>
/// <param name="processorConfigure">Activity processor configuration.</param>
/// <returns>The instance of <see cref="TracerProviderBuilder"/> to chain the calls.</returns>
public static TracerProviderBuilder UseAzureMonitorTraceExporter(this TracerProviderBuilder builder, Action<AzureMonitorExporterOptions> configure = null, Action<ActivityProcessorPipelineBuilder> processorConfigure = null)
public static TracerProviderBuilder AddAzureMonitorTraceExporter(this TracerProviderBuilder builder, Action<AzureMonitorExporterOptions> configure = null)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

return builder.AddProcessorPipeline(pipeline =>
{
var options = new AzureMonitorExporterOptions();
configure?.Invoke(options);
var options = new AzureMonitorExporterOptions();
configure?.Invoke(options);

var exporter = new AzureMonitorTraceExporter(options);
processorConfigure?.Invoke(pipeline);
pipeline.SetExporter(exporter);
});
// TODO: Pick Simple vs Batching based on AzureMonitorExporterOptions
return builder.AddProcessor(new BatchExportActivityProcessor(new AzureMonitorTraceExporter(options)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core.Pipeline;
using OpenTelemetry.Trace;

namespace OpenTelemetry.Exporter.AzureMonitor
Expand All @@ -25,22 +24,24 @@ public AzureMonitorTraceExporter(AzureMonitorExporterOptions options)
}

/// <inheritdoc/>
public override async Task<ExportResult> ExportAsync(IEnumerable<Activity> batchActivity, CancellationToken cancellationToken)
public override ExportResult Export(in Batch<Activity> batch)
{
if (batchActivity == null)
// Prevent Azure Monitor's HTTP operations from being instrumented.
using var scope = SuppressInstrumentationScope.Begin();

try
{
throw new ArgumentNullException(nameof(batchActivity));
// TODO: Handle return value, it can be converted as metrics.
// TODO: Validate CancellationToken and async pattern here.
this.AzureMonitorTransmitter.AddBatchActivityAsync(batch, false, CancellationToken.None).EnsureCompleted();
return ExportResult.Success;
}
catch (Exception ex)
{
AzureMonitorTraceExporterEventSource.Log.FailedExport(ex);
return ExportResult.Failure;
}

// Handle return value, it can be converted as metrics.
await this.AzureMonitorTransmitter.AddBatchActivityAsync(batchActivity, cancellationToken).ConfigureAwait(false);
return ExportResult.Success;
}

/// <inheritdoc/>
public override Task ShutdownAsync(CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ internal sealed class AzureMonitorTraceExporterEventSource : EventSource
public static AzureMonitorTraceExporterEventSource Log = new AzureMonitorTraceExporterEventSource();

[NonEvent]
public void ConfigurationStringParseWarning(string message)
public void FailedExport(Exception ex)
{
if (this.IsEnabled(EventLevel.Warning, EventKeywords.All))
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.WarnToParseConfigurationString(message);
this.FailedExport(ex.ToInvariantString());
}
}

Expand All @@ -39,8 +39,8 @@ public void ConnectionStringError(Exception ex)
}
}

[Event(1, Message = "{0}", Level = EventLevel.Warning)]
public void WarnToParseConfigurationString(string message) => this.WriteEvent(1, message);
[Event(1, Message = "Failed to export activities: '{0}'", Level = EventLevel.Error)]
public void FailedExport(string exception) => this.WriteEvent(1, exception);

[Event(2, Message = "Error creating SdkVersion : '{0}'", Level = EventLevel.Warning)]
public void WarnSdkVersionCreateException(string message) => this.WriteEvent(2, message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public AzureMonitorTransmitter(AzureMonitorExporterOptions exporterOptions)
serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), HttpPipelineBuilder.Build(options), endpoint: ingestionEndpoint);
}

internal async ValueTask<int> AddBatchActivityAsync(IEnumerable<Activity> batchActivity, CancellationToken cancellationToken)
internal async ValueTask<int> AddBatchActivityAsync(Batch<Activity> batchActivity, bool async, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
Expand All @@ -64,8 +64,18 @@ internal async ValueTask<int> AddBatchActivityAsync(IEnumerable<Activity> batchA
telemetryItems.Add(telemetryItem);
}

Azure.Response<TrackResponse> response;

if (async)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious - why would we need this branching logic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) Azure SDK forces to pass async bool passed as function argument when we don't await and use GetResult (EnsureCompleted())

{
response = await this.serviceRestClient.TrackAsync(telemetryItems, cancellationToken).ConfigureAwait(false);
}
else
{
response = this.serviceRestClient.TrackAsync(telemetryItems, cancellationToken).Result;
}

// TODO: Handle exception, check telemetryItems has items
var response = await this.serviceRestClient.TrackAsync(telemetryItems, cancellationToken).ConfigureAwait(false);
return response.Value.ItemsAccepted.GetValueOrDefault();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="System.Text.Json" />
<PackageReference Include="OpenTelemetry" Version="0.4.0-beta.2" IsImplicitlyDefined="true" />
<PackageReference Include="OpenTelemetry" Version="0.5.0-beta.2" IsImplicitlyDefined="true" />
</ItemGroup>

<!-- Shared source from Azure.Core -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ public static class DemoTrace

public static void Main()
{
OpenTelemetry.Sdk.CreateTracerProvider(builder => builder
.AddActivitySource("Samples.SampleServer")
.AddActivitySource("Samples.SampleClient")
.UseAzureMonitorTraceExporter(o => {
o.ConnectionString = "ConnectionString";
}));
using var tracerProvider = OpenTelemetry.Sdk.CreateTracerProviderBuilder()
.AddSource("Samples.SampleServer")
.AddSource("Samples.SampleClient")
.AddAzureMonitorTraceExporter(o => {
o.ConnectionString = $"InstrumentationKey=Ikey;";
})
.Build();

using (var sample = new InstrumentationWithActivitySource())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ public void Start(string url)
foreach (var headerKey in headerKeys)
{
string headerValue = context.Request.Headers[headerKey];
activity?.AddTag($"http.header.{headerKey}", headerValue);
activity?.SetTag($"http.header.{headerKey}", headerValue);
}

activity?.AddTag("http.url", context.Request.Url.AbsolutePath);
activity?.SetTag("http.url", context.Request.Url.AbsolutePath);

string requestContent;
using (var childSpan = source.StartActivity("ReadStream", ActivityKind.Consumer))
Expand All @@ -73,8 +73,8 @@ public void Start(string url)
childSpan.AddEvent(new ActivityEvent("StreamReader.ReadToEnd"));
}

activity?.AddTag("request.content", requestContent);
activity?.AddTag("request.length", requestContent.Length.ToString(CultureInfo.InvariantCulture));
activity?.SetTag("request.content", requestContent);
activity?.SetTag("request.length", requestContent.Length.ToString(CultureInfo.InvariantCulture));

var echo = Encoding.UTF8.GetBytes("echo: " + requestContent);
context.Response.ContentEncoding = Encoding.UTF8;
Expand Down Expand Up @@ -126,22 +126,22 @@ public void Start(string url)
#pragma warning restore CA2234 // Pass system uri objects instead of strings
activity?.AddEvent(new ActivityEvent("PostAsync:Ended"));

activity?.AddTag("http.url", url);
activity?.AddTag("http.status_code", $"{response.StatusCode:D}");
activity?.SetTag("http.url", url);
activity?.SetTag("http.status_code", $"{response.StatusCode:D}");

var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
activity?.AddTag("response.content", responseContent);
activity?.AddTag("response.length", responseContent.Length.ToString(CultureInfo.InvariantCulture));
activity?.SetTag("response.content", responseContent);
activity?.SetTag("response.length", responseContent.Length.ToString(CultureInfo.InvariantCulture));

foreach (var header in response.Headers)
{
if (header.Value is IEnumerable<object> enumerable)
{
activity?.AddTag($"http.header.{header.Key}", string.Join(",", enumerable));
activity?.SetTag($"http.header.{header.Key}", string.Join(",", enumerable));
}
else
{
activity?.AddTag($"http.header.{header.Key}", header.Value.ToString());
activity?.SetTag($"http.header.{header.Key}", header.Value.ToString());
}
}
}
Expand Down