diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/TracerProviderBuilderExtensions.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorExporterHelperExtensions.cs similarity index 57% rename from sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/TracerProviderBuilderExtensions.cs rename to sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorExporterHelperExtensions.cs index bda7cdab745d..281bf2522ef0 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/TracerProviderBuilderExtensions.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorExporterHelperExtensions.cs @@ -9,31 +9,26 @@ namespace OpenTelemetry.Exporter.AzureMonitor /// /// test /// - public static class TracerProviderBuilderExtensions + public static class AzureMonitorExporterHelperExtensions { /// /// Registers an Azure Monitor trace exporter that will receive instances. /// /// builder to use. /// Exporter configuration options. - /// Activity processor configuration. /// The instance of to chain the calls. - public static TracerProviderBuilder UseAzureMonitorTraceExporter(this TracerProviderBuilder builder, Action configure = null, Action processorConfigure = null) + public static TracerProviderBuilder AddAzureMonitorTraceExporter(this TracerProviderBuilder builder, Action 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))); } } } diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporter.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporter.cs index 8e3f7eae9fc3..65f170794f7f 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporter.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporter.cs @@ -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 @@ -25,22 +24,24 @@ public AzureMonitorTraceExporter(AzureMonitorExporterOptions options) } /// - public override async Task ExportAsync(IEnumerable batchActivity, CancellationToken cancellationToken) + public override ExportResult Export(in Batch 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; - } - - /// - public override Task ShutdownAsync(CancellationToken cancellationToken) - { - throw new NotImplementedException(); } } } diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporterEventSource.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporterEventSource.cs index f4538ab74742..ad9158886810 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporterEventSource.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTraceExporterEventSource.cs @@ -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()); } } @@ -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); diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTransmitter.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTransmitter.cs index 74ca3da418c3..4cfef1a6f9d8 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTransmitter.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/AzureMonitorTransmitter.cs @@ -46,7 +46,7 @@ public AzureMonitorTransmitter(AzureMonitorExporterOptions exporterOptions) serviceRestClient = new ServiceRestClient(new ClientDiagnostics(options), HttpPipelineBuilder.Build(options), endpoint: ingestionEndpoint); } - internal async ValueTask AddBatchActivityAsync(IEnumerable batchActivity, CancellationToken cancellationToken) + internal async ValueTask AddBatchActivityAsync(Batch batchActivity, bool async, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { @@ -64,8 +64,18 @@ internal async ValueTask AddBatchActivityAsync(IEnumerable batchA telemetryItems.Add(telemetryItem); } + Azure.Response response; + + if (async) + { + 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(); } diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/OpenTelemetry.Exporter.AzureMonitor.csproj b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/OpenTelemetry.Exporter.AzureMonitor.csproj index f21959f91483..81baf49e72f3 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/OpenTelemetry.Exporter.AzureMonitor.csproj +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/src/OpenTelemetry.Exporter.AzureMonitor.csproj @@ -9,7 +9,7 @@ - + diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/DemoTrace.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/DemoTrace.cs index d89cb622ffaa..5763fd30aaf8 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/DemoTrace.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/DemoTrace.cs @@ -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()) { diff --git a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/InstrumentationWithActivitySource.cs b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/InstrumentationWithActivitySource.cs index 8c3b00c2bc3d..b0f6d2c21702 100644 --- a/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/InstrumentationWithActivitySource.cs +++ b/sdk/monitor/OpenTelemetry.Exporter.AzureMonitor/tests/OpenTelemetry.Exporter.AzureMonitor.Demo.Tracing/InstrumentationWithActivitySource.cs @@ -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)) @@ -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; @@ -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 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()); } } }