Remove application insights exporter (#658)

* remove application insights exporter

* switch example to use zipkin

Co-authored-by: Sergey Kanzhelev <S.Kanzhelev@live.com>
This commit is contained in:
Reiley Yang 2020-05-04 13:49:49 -07:00 committed by GitHub
parent ecb6928bb2
commit 011b21054f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 7 additions and 3548 deletions

View File

@ -39,8 +39,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".vsts", ".vsts", "{61188153
.vsts\ci-myget-update.yml = .vsts\ci-myget-update.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.ApplicationInsights", "src\OpenTelemetry.Exporter.ApplicationInsights\OpenTelemetry.Exporter.ApplicationInsights.csproj", "{4493F5D9-874E-4FBF-B2F3-37890BD910E0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Stackdriver", "src\OpenTelemetry.Exporter.Stackdriver\OpenTelemetry.Exporter.Stackdriver.csproj", "{DE1B4783-C01F-4672-A6EB-695F1717105B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Api", "src\OpenTelemetry.Api\OpenTelemetry.Api.csproj", "{99F8A331-05E9-45A5-89BA-4C54E825E5B2}"
@ -61,8 +59,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp.AspNetCore.3.1", "t
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Stackdriver.Tests", "test\OpenTelemetry.Exporter.Stackdriver.Tests\OpenTelemetry.Exporter.Stackdriver.Tests.csproj", "{6875032B-DFDC-4CDE-A283-37CA7F99926A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.ApplicationInsights.Tests", "test\OpenTelemetry.Exporter.ApplicationInsights.Tests\OpenTelemetry.Exporter.ApplicationInsights.Tests.csproj", "{1FA1F509-7722-48E3-9A35-16CBB6774957}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Adapter.StackExchangeRedis", "src\OpenTelemetry.Adapter.StackExchangeRedis\OpenTelemetry.Adapter.StackExchangeRedis.csproj", "{6B681D72-D68A-44CC-8C75-53B9A322E6EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Adapter.StackExchangeRedis.Tests", "test\OpenTelemetry.Adapter.StackExchangeRedis.Tests\OpenTelemetry.Adapter.StackExchangeRedis.Tests.csproj", "{CA98AF29-0852-4ADD-A66B-7E96266EE7B7}"

View File

@ -27,7 +27,6 @@
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.ZPages\OpenTelemetry.Exporter.ZPages.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry\OpenTelemetry.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.ApplicationInsights\OpenTelemetry.Exporter.ApplicationInsights.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj" />
</ItemGroup>
</Project>

View File

@ -29,7 +29,6 @@ namespace Samples
///
/// dotnet Exporters.dll zipkin -u http://localhost:9411/api/v2/spans
/// dotnet Exporters.dll jaeger -h localhost -o 6831
/// dotnet Exporters.dll appInsights
/// dotnet Exporters.dll prometheus -i 15 -p 9184 -d 2
///
/// The above must be run from the project bin folder
@ -38,11 +37,10 @@ namespace Samples
/// <param name="args">Arguments from command line.</param>
public static void Main(string[] args)
{
Parser.Default.ParseArguments<JaegerOptions, ZipkinOptions, ApplicationInsightsOptions, PrometheusOptions, HttpClientOptions, StackdriverOptions, LightStepOptions, ZPagesOptions, ConsoleOptions, OtlpOptions>(args)
Parser.Default.ParseArguments<JaegerOptions, ZipkinOptions, PrometheusOptions, HttpClientOptions, StackdriverOptions, LightStepOptions, ZPagesOptions, ConsoleOptions, OtlpOptions>(args)
.MapResult(
(JaegerOptions options) => TestJaeger.Run(options.Host, options.Port),
(ZipkinOptions options) => TestZipkin.Run(options.Uri),
(ApplicationInsightsOptions options) => TestApplicationInsights.Run(),
(PrometheusOptions options) => TestPrometheus.RunAsync(options.Port, options.PushIntervalInSecs, options.DurationInMins),
(HttpClientOptions options) => TestHttpClient.Run(),
(RedisOptions options) => TestRedis.Run(options.Uri),
@ -89,11 +87,6 @@ namespace Samples
public string Uri { get; set; }
}
[Verb("appInsights", HelpText = "Specify the options required to test ApplicationInsights")]
internal class ApplicationInsightsOptions
{
}
[Verb("prometheus", HelpText = "Specify the options required to test Prometheus")]
internal class PrometheusOptions
{

View File

@ -1,56 +0,0 @@
// <copyright file="TestApplicationInsights.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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.Threading;
using OpenTelemetry.Context;
using OpenTelemetry.Exporter.ApplicationInsights;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Trace.Configuration;
namespace Samples
{
internal class TestApplicationInsights
{
private static readonly string FrontendKey = "my.org/keys/frontend";
internal static object Run()
{
DistributedContext.Carrier = AsyncLocalDistributedContextCarrier.Instance; // Enable asynclocal carrier for the context
DistributedContext dc = DistributedContextBuilder.CreateContext(FrontendKey, "mobile-ios9.3.5");
using var tracerFactory = TracerFactory.Create(builder => builder
.SetResource(Resources.CreateServiceResource("my-service"))
.UseApplicationInsights(config => config.InstrumentationKey = "instrumentation-key"));
var tracer = tracerFactory.GetTracer("application-insights-test");
using (DistributedContext.SetCurrent(dc))
using (tracer.StartActiveSpan("incoming request", out var span))
{
span.AddEvent("Start processing video.");
Thread.Sleep(TimeSpan.FromMilliseconds(10));
span.AddEvent("Finished processing video.");
}
Thread.Sleep(TimeSpan.FromMilliseconds(5100));
Console.WriteLine("Done... wait for events to arrive to backend!");
Console.ReadLine();
return null;
}
}
}

View File

@ -14,7 +14,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\src\OpenTelemetry.Adapter.AspNetCore\OpenTelemetry.Adapter.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Adapter.Dependencies\OpenTelemetry.Adapter.Dependencies.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.ApplicationInsights\OpenTelemetry.Exporter.ApplicationInsights.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
</ItemGroup>

View File

@ -40,10 +40,10 @@ namespace API
{
builder
//.SetSampler(Samplers.AlwaysSample)
.UseApplicationInsights(telemetryConfiguration =>
.UseZipkin(options =>
{
var instrumentationKey = this.Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
telemetryConfiguration.InstrumentationKey = instrumentationKey;
options.ServiceName = "test-zipkin";
options.Endpoint = new Uri(this.Configuration.GetValue<string>("Zipkin:Endpoint"));
})
.AddRequestAdapter()
.AddDependencyAdapter();

View File

@ -7,7 +7,7 @@
}
},
"AllowedHosts": "*",
"ApplicationInsights": {
"InstrumentationKey": "<Insert your InstrumentationKey here>"
"Zipkin": {
"Endpoint": "http://localhost:9411/api/v2/spans"
}
}

View File

@ -1,685 +0,0 @@
// <copyright file="ApplicationInsightsTraceExporter.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using OpenTelemetry.Trace.Export;
namespace OpenTelemetry.Exporter.ApplicationInsights
{
/// <summary>
/// Application Insights trace exporter.
/// </summary>
public class ApplicationInsightsTraceExporter : SpanExporter, IDisposable
{
private const string InProcDependencyType = "InProc";
private const string QueueMessageDependencyType = "Queue Message";
private const string EventHubsDependencyType = "Microsoft.EventHub";
private const string HttpDependencyType = "Http";
private readonly TelemetryClient telemetryClient;
private readonly string serviceEndpoint;
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationInsightsTraceExporter"/> class.
/// </summary>
/// <param name="telemetryConfiguration">Telemetry configuration to use.</param>
public ApplicationInsightsTraceExporter(TelemetryConfiguration telemetryConfiguration)
{
this.telemetryClient = new TelemetryClient(telemetryConfiguration);
this.telemetryClient.Context.GetInternalContext().SdkVersion = "ot:" + GetAssemblyVersion();
this.serviceEndpoint = telemetryConfiguration.TelemetryChannel.EndpointAddress;
}
/// <inheritdoc/>
public override Task<ExportResult> ExportAsync(IEnumerable<SpanData> spanDataList, CancellationToken cancellationToken)
{
foreach (var span in spanDataList)
{
bool shouldExport = true;
string httpUrlAttr = null;
foreach (var attr in span.Attributes)
{
if (attr.Key == "http.url")
{
httpUrlAttr = attr.Value.ToString();
if (httpUrlAttr == this.serviceEndpoint)
{
shouldExport = false;
break;
}
}
}
if (!shouldExport)
{
continue;
}
this.ExtractGenericProperties(
span,
out var name,
out var resultCode,
out var statusDescription,
out var traceId,
out var spanId,
out var parentId,
out var success,
out var duration,
out var roleName,
out var roleInstance,
out var version);
// BUILDING resulting telemetry
OperationTelemetry result;
if (span.Kind == SpanKind.Client || span.Kind == SpanKind.Internal || span.Kind == SpanKind.Producer)
{
result = new DependencyTelemetry
{
Name = name,
ResultCode = resultCode,
Type = span.Kind == SpanKind.Internal ? InProcDependencyType :
span.Kind == SpanKind.Producer ? QueueMessageDependencyType : null,
};
}
else
{
result = new RequestTelemetry
{
Name = name,
ResponseCode = resultCode,
};
result.Context.Operation.Name = name;
}
string component = this.GetComponent(span);
switch (component)
{
case "http":
this.SetHttpProperties(
span,
result,
httpUrlAttr);
break;
case "eventhubs":
case "Microsoft.EventHub":
this.SetEventHubsProperties(span, result);
break;
default:
if (result is DependencyTelemetry dependency && component != null)
{
dependency.Type = string.IsNullOrEmpty(dependency.Type) ? component : string.Concat(dependency.Type, " | ", component);
}
foreach (var attribute in span.Attributes)
{
AddPropertyWithAdjustedName(result.Properties, attribute.Key, attribute.Value.ToString());
}
break;
}
if (span.Links.Count() != 0)
{
var linksJson = new StringBuilder();
linksJson.Append('[');
foreach (var link in span.Links)
{
var linkTraceId = link.Context.TraceId.ToHexString();
// avoiding json serializers for now because of extra dependency.
// System.Text.Json is starting at 4.6.1 while exporter is 4.6
// also serialization is trivial and looks like `links` property with json blob
// [{"operation_Id":"5eca8b153632494ba00f619d6877b134","id":"d4c1279b6e7b7c47"},
// {"operation_Id":"ff28988d0776b44f9ca93352da126047","id":"bf4fa4855d161141"}]
linksJson
.Append('{')
.Append("\"operation_Id\":")
.Append('\"')
.Append(linkTraceId)
.Append('\"')
.Append(',');
linksJson
.Append("\"id\":")
.Append('\"')
.Append(link.Context.SpanId.ToHexString())
.Append('\"');
// we explicitly ignore sampling flag, tracestate and attributes at this point.
linksJson.Append("},");
}
// trim last comma, json does not support it
if (linksJson.Length > 0)
{
linksJson.Remove(linksJson.Length - 1, 1);
}
linksJson.Append("]");
result.Properties["_MS.links"] = linksJson.ToString();
}
foreach (var t in span.Events)
{
var log = new TraceTelemetry(t.Name);
if (t.Timestamp != null)
{
log.Timestamp = t.Timestamp;
}
foreach (var attr in t.Attributes)
{
var value = attr.Value.ToString();
AddPropertyWithAdjustedName(log.Properties, attr.Key, value);
}
log.Context.Operation.Id = traceId;
log.Context.Operation.ParentId = spanId;
log.Context.Cloud.RoleName = roleName;
log.Context.Cloud.RoleInstance = roleInstance;
log.Context.Component.Version = version;
this.telemetryClient.Track(log);
}
result.Success = success;
if (statusDescription != null)
{
AddPropertyWithAdjustedName(result.Properties, "statusDescription", statusDescription);
}
result.Timestamp = span.StartTimestamp;
result.Context.Operation.Id = traceId;
result.Context.Cloud.RoleName = roleName;
result.Context.Cloud.RoleInstance = roleInstance;
result.Context.Component.Version = version;
if (parentId != null)
{
result.Context.Operation.ParentId = parentId;
}
result.Id = spanId;
foreach (var ts in span.Context.Tracestate)
{
result.Properties[ts.Key] = ts.Value;
}
result.Duration = duration;
// TODO: deal with those:
// span.ChildSpanCount
// span.Context.TraceOptions;
this.telemetryClient.Track(result);
}
return Task.FromResult(ExportResult.Success);
}
/// <inheritdoc/>
public override Task ShutdownAsync(CancellationToken cancellationToken)
{
// TODO cancellation support
this.telemetryClient.Flush();
return Task.CompletedTask;
}
/// <inheritdoc/>
public void Dispose()
{
this.ShutdownAsync(CancellationToken.None).ContinueWith(_ => { }).Wait();
}
private static void AddPropertyWithAdjustedName(IDictionary<string, string> props, string name, string value)
{
if (name == "component" || name == "az.namespace" || name == "kind")
{
// these attributes are reflected in telemetry types and should not be populated.
return;
}
var n = name;
var i = 0;
while (props.ContainsKey(n))
{
n = name + "_" + i;
++i;
}
props.Add(n, value);
}
private static string GetAssemblyVersion()
{
try
{
return typeof(ApplicationInsightsTraceExporter).GetTypeInfo().Assembly.GetCustomAttributes<AssemblyFileVersionAttribute>()
.First()
.Version;
}
catch (Exception)
{
return "0.0.0";
}
}
private void ExtractGenericProperties(
SpanData span,
out string name,
out string resultCode,
out string statusDescription,
out string traceId,
out string spanId,
out string parentId,
out bool? success,
out TimeSpan duration,
out string roleName,
out string roleInstance,
out string serviceVersion)
{
name = span.Name;
roleName = null;
statusDescription = null;
traceId = span.Context.TraceId.ToHexString();
spanId = span.Context.SpanId.ToHexString();
parentId = null;
if (span.ParentSpanId != default)
{
parentId = span.ParentSpanId.ToHexString();
}
resultCode = null;
success = null;
if (span.Status.IsValid)
{
resultCode = span.Status.CanonicalCode.ToString();
success = span.Status.IsOk;
if (!string.IsNullOrEmpty(span.Status.Description))
{
statusDescription = span.Status.Description;
}
}
duration = span.EndTimestamp - span.StartTimestamp;
string serviceName = null;
string serviceNamespace = null;
serviceVersion = null;
roleInstance = null;
foreach (var attribute in span.LibraryResource.Attributes)
{
if (attribute.Key == Resource.ServiceNameKey && attribute.Value is string)
{
serviceName = (string)attribute.Value;
}
else if (attribute.Key == Resource.ServiceNamespaceKey && attribute.Value is string)
{
serviceNamespace = (string)attribute.Value;
}
else if (attribute.Key == Resource.ServiceVersionKey && attribute.Value is string)
{
serviceVersion = (string)attribute.Value;
}
else if (attribute.Key == Resource.ServiceInstanceIdKey && attribute.Value is string)
{
roleInstance = (string)attribute.Value;
}
}
if (serviceName != null && serviceNamespace != null)
{
roleName = string.Concat(serviceNamespace, ".", serviceName);
}
else
{
roleName = serviceName;
}
}
private void SetHttpProperties(SpanData span, OperationTelemetry telemetry, string httpUrlAttr)
{
string httpStatusCodeAttr = null;
string httpMethodAttr = null;
string httpPathAttr = null;
string httpHostAttr = null;
string httpRouteAttr = null;
string httpPortAttr = null;
foreach (var attr in span.Attributes)
{
switch (attr.Key)
{
case "http.method":
httpMethodAttr = attr.Value.ToString();
break;
case "http.path":
httpPathAttr = attr.Value.ToString();
break;
case "http.host":
httpHostAttr = attr.Value.ToString();
break;
case "http.status_code":
httpStatusCodeAttr = attr.Value.ToString();
break;
case "http.user_agent":
telemetry.Context.User.UserAgent = attr.Value.ToString();
break;
case "http.route":
httpRouteAttr = attr.Value.ToString();
break;
case "http.port":
httpPortAttr = attr.Value.ToString();
break;
case "http.url":
// break without doing anything - this will prevent adding url to custom property bag.
// httpUrlAttr is already populated.
break;
default:
AddPropertyWithAdjustedName(telemetry.Properties, attr.Key, attr.Value.ToString());
break;
}
}
string resultCode = null;
if (httpStatusCodeAttr != null)
{
resultCode = httpStatusCodeAttr.ToString(CultureInfo.InvariantCulture);
}
Uri url = null;
if (httpUrlAttr != null)
{
Uri.TryCreate(httpUrlAttr, UriKind.RelativeOrAbsolute, out url);
}
string httpMethod = null;
string httpPath = null;
string httpHost = null;
string httpRoute = null;
string httpPort = null;
if (httpMethodAttr != null)
{
httpMethod = httpMethodAttr;
}
if (httpPathAttr != null)
{
httpPath = httpPathAttr;
}
if (httpHostAttr != null)
{
httpHost = httpHostAttr;
}
if (httpRouteAttr != null)
{
httpRoute = httpRouteAttr;
}
if (httpRouteAttr != null)
{
httpRoute = httpRouteAttr;
}
if (httpPortAttr != null)
{
httpPort = httpPortAttr;
}
// restore optional fields when possible
if ((httpPathAttr == null) && (url != null))
{
if (url.IsAbsoluteUri)
{
httpPath = url.LocalPath;
}
else
{
var idx = url.OriginalString.IndexOf('?');
if (idx != -1)
{
httpPath = url.OriginalString.Substring(0, idx);
}
else
{
httpPath = url.OriginalString;
}
}
}
if (url == null)
{
var urlString = string.Empty;
if (!string.IsNullOrEmpty(httpHost))
{
urlString += "https://" + httpHost;
if (!string.IsNullOrEmpty(httpPort))
{
urlString += ":" + httpPort;
}
}
if (!string.IsNullOrEmpty(httpPath))
{
if (httpPath[0] != '/')
{
urlString += '/';
}
urlString += httpPath;
}
if (!string.IsNullOrEmpty(urlString))
{
Uri.TryCreate(urlString, UriKind.RelativeOrAbsolute, out url);
}
}
// overwriting
if (httpPath != null || httpMethod != null || httpRoute != null)
{
if (httpRoute != null)
{
telemetry.Name = (httpMethod + " " + httpRoute).Trim();
}
else
{
telemetry.Name = (httpMethod + " " + httpPath).Trim();
}
}
if (telemetry is DependencyTelemetry dependency)
{
if (url != null)
{
dependency.Data = url.OriginalString;
dependency.Target = url.IsAbsoluteUri ? url.Authority : null;
}
else
{
dependency.Data = null;
dependency.Target = null;
}
dependency.ResultCode = resultCode;
if (string.IsNullOrEmpty(dependency.Type))
{
dependency.Type = HttpDependencyType;
}
}
else if (telemetry is RequestTelemetry request)
{
if (url != null)
{
request.Url = url;
}
request.ResponseCode = resultCode;
request.Context.Operation.Name = request.Name;
}
}
private void SetEventHubsProperties(SpanData span, OperationTelemetry telemetry)
{
string endpoint = null;
string queueName = null;
foreach (var attribute in span.Attributes)
{
if (attribute.Key == "peer.address" && attribute.Value is string)
{
endpoint = (string)attribute.Value;
}
else if (attribute.Key == "message_bus.destination")
{
queueName = (string)attribute.Value;
}
else
{
AddPropertyWithAdjustedName(telemetry.Properties, attribute.Key, attribute.Value.ToString());
}
}
string eventHubsInfo = null;
// Target/source uniquely identifies the resource, we use both: queueName and endpoint
if (endpoint != null && queueName != null)
{
eventHubsInfo = string.Concat(endpoint, "/", queueName);
}
if (telemetry is DependencyTelemetry dependency)
{
dependency.Type = dependency.Type == QueueMessageDependencyType ? string.Concat(dependency.Type, " | ", EventHubsDependencyType) : EventHubsDependencyType;
if (eventHubsInfo != null)
{
dependency.Target = eventHubsInfo;
}
}
else if (telemetry is RequestTelemetry request)
{
if (eventHubsInfo != null)
{
request.Source = eventHubsInfo;
}
if (this.TryGetAverageTimeInQueueForBatch(span, out long enqueuedTime))
{
request.Metrics["timeSinceEnqueued"] = enqueuedTime;
}
}
}
private bool TryGetAverageTimeInQueueForBatch(SpanData span, out long avgTimeInQueue)
{
avgTimeInQueue = 0;
int linksCount = 0;
foreach (var link in span.Links)
{
if (!this.TryGetEnqueuedTime(link, out var msgEnqueuedTime))
{
// instrumentation does not consistently report enqueued time, ignoring whole span
return false;
}
avgTimeInQueue += Math.Max(span.StartTimestamp.ToUnixTimeMilliseconds() - msgEnqueuedTime, 0);
linksCount++;
}
if (linksCount == 0)
{
return false;
}
avgTimeInQueue /= linksCount;
return true;
}
private bool TryGetEnqueuedTime(Link link, out long enqueuedTime)
{
enqueuedTime = 0;
foreach (var attribute in link.Attributes)
{
if (attribute.Key == "enqueuedTime")
{
if (attribute.Value is string strValue)
{
return long.TryParse(strValue, out enqueuedTime);
}
// future case
if (attribute.Value is long longValue)
{
enqueuedTime = longValue;
return true;
}
}
}
return false;
}
private string GetComponent(SpanData span)
{
foreach (var attr in span.Attributes)
{
if (attr.Key == "component" && attr.Value is string strValue)
{
return strValue;
}
if (attr.Key == "az.namespace" && attr.Value is string azNamespace)
{
return azNamespace;
}
if (attr.Key.StartsWith("http."))
{
return "http";
}
}
return null;
}
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
<Description>Application Insights exporter for Open Telemetry.</Description>
<PackageTags>$(PackageTags);application-insights;azure;distributed-tracing</PackageTags>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Implementation\**" />
<EmbeddedResource Remove="Implementation\**" />
<None Remove="Implementation\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenTelemetry\OpenTelemetry.csproj" />
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.11.0" />
</ItemGroup>
</Project>

View File

@ -1,34 +0,0 @@
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.ApplicationInsights.Tests" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
#if SIGNED
internal static class AssemblyInfo
{
public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
public const string MoqPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7";
}
#else
internal static class AssemblyInfo
{
public const string PublicKey = "";
public const string MoqPublicKey = "";
}
#endif

View File

@ -1,115 +0,0 @@
# Using OpenTelemetry with Application Insights
## Create Application Insights resource
If you don't have resource yet [create one](https://docs.microsoft.com/en-us/azure/azure-monitor/app/create-new-resource).
## Setup for applications with dependency injection
If you have ASP.NET Core web application or worker service, or if you simply use use `Microsoft.Extensions.DependencyInjection`,
just call `AddOpenTelemetry` during host builder configuration in `ConfigureServices` (in `Startup` or `Program`).
1. Install packages (latest version)
``` xml
<PackageReference Include="OpenTelemetry" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Adapter.AspNetCore" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Adapter.Dependencies" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Exporter.ApplicationInsights" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="0.2.0-alpha.182" />
```
2. Set up OpenTelemetry
```csharp
services.AddOpenTelemetry((sp, builder) =>
{
builder
.SetResource(Resources.CreateServiceResource("my-service")) // set any service name as you would like to appear on the Application Map
.UseApplicationInsights(telemetryConfiguration =>
{
telemetryConfiguration.InstrumentationKey = Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
})
.AddRequestAdapter() // regirsters ASP.NET Core incoming requests tracking
.AddDependencyAdapter(); // regirsters outgoing requests tracking
});
```
## Applications without dependency injection
1. Install OpenTelemetry packages (latest version)
``` xml
<PackageReference Include="OpenTelemetry" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Adapter.Dependencies" Version="0.2.0-alpha.182" />
<PackageReference Include="OpenTelemetry.Exporter.ApplicationInsights" Version="0.2.0-alpha.182" />
```
2. Create `TraceFactory` and make sure to keep it alive during application lifetime.
Avoid creating multiple `TracerFactories` and dispose factory before application exits.
```csharp
var tracerFactory = TracerFactory.Create(builder =>
builder
.SetResource(Resources.CreateServiceResource("my-service")) // set any service name as you would like to appear on the Application Map
.UseApplicationInsights(telemetryConfiguration =>
{
telemetryConfiguration.InstrumentationKey = "your instrumentation key";
})
.AddDependencyAdapter(); // regirsters outgoing requests tracking
var tracer = tracerFactory.GetTracer("http-client-test");
// start root span
using (tracer.StartActiveSpan("root span", out _))
{
// do stuff, start other spans, etc
}
// dispose before exit to flush all spans
tracerFactory.Dispose();
```
### Export exception and logs from ILogger
OpenTelemetry does not support logging yet, but you can export your logs to Application Insights.
Here is [more info](https://docs.microsoft.com/en-us/azure/azure-monitor/app/ilogger) on ILogger support with Application Insights.
Logs will be correlated to the OpenTelemetry spans.
1. In addition to OpenTelemetry packages, install `Microsoft.Extensions.Logging.ApplicationInsights` (latest).
```xml
<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.12.1" />
```
2. Configure logging per [documentation](https://docs.microsoft.com/en-us/azure/azure-monitor/app/ilogger).
Here is the full example:
```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
//....
.ConfigureServices((context, services) =>
{
var instrumentationKey = context.Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
// set up OpenTelemetry
services.AddOpenTelemetry(b => b
.SetResource(Resources.CreateServiceResource("line-counter")) // use unique name
.UseApplicationInsights(o => o.InstrumentationKey = instrumentationKey)
.AddDependencyAdapter()
.AddRequestAdapter());
// set up correlation between spans and logs
services.Configure<TelemetryConfiguration>(telemetryConfiguration =>
{
telemetryConfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
});
})
.ConfigureLogging((context, builder) =>
{
var instrumentationKey = context.Configuration.GetValue<string>("ApplicationInsights:InstrumentationKey");
// configure Application Insights provider for ILogger
builder.AddApplicationInsights(instrumentationKey);
});
```

View File

@ -1,88 +0,0 @@
// <copyright file="TracerBuilderExtensions.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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 Microsoft.ApplicationInsights.Extensibility;
using OpenTelemetry.Exporter.ApplicationInsights;
using OpenTelemetry.Trace.Export;
namespace OpenTelemetry.Trace.Configuration
{
/// <summary>
/// Extension methods to simplify registering of Application Insights exporter.
/// </summary>
public static class TracerBuilderExtensions
{
/// <summary>
/// Enables Application Insights exporter.
/// </summary>
/// <param name="builder">Trace builder to use.</param>
/// <param name="configure">Configuration options.</param>
/// <returns>The instance of <see cref="TracerBuilder"/> to chain the calls.</returns>
public static TracerBuilder UseApplicationInsights(this TracerBuilder builder, Action<TelemetryConfiguration> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var configuration = new TelemetryConfiguration();
configure(configuration);
return builder.AddProcessorPipeline(b => b
.SetExporter(new ApplicationInsightsTraceExporter(configuration))
.SetExportingProcessor(e => new BatchingSpanProcessor(e)));
}
/// <summary>
/// Enables Application Insights exporter.
/// </summary>
/// <param name="builder">Trace builder to use.</param>
/// <param name="applicationInsightsConfigure">Configuration options.</param>
/// <param name="processorConfigure">Span processor configuration.</param>
/// <returns>The instance of <see cref="TracerBuilder"/> to chain the calls.</returns>
public static TracerBuilder UseApplicationInsights(this TracerBuilder builder, Action<TelemetryConfiguration> applicationInsightsConfigure, Action<
SpanProcessorPipelineBuilder> processorConfigure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (applicationInsightsConfigure == null)
{
throw new ArgumentNullException(nameof(applicationInsightsConfigure));
}
if (processorConfigure == null)
{
throw new ArgumentNullException(nameof(processorConfigure));
}
var options = new TelemetryConfiguration();
applicationInsightsConfigure(options);
return builder.AddProcessorPipeline(b =>
{
b.SetExporter(new ApplicationInsightsTraceExporter(options));
processorConfigure.Invoke(b);
});
}
}
}

View File

@ -11,34 +11,6 @@ There is also a constructor for specifying path to the service account credentia
4. Instantiate a new instance of `StackdriverExporter` with your Google Cloud's ProjectId
5. See [sample][stackdriver-sample] for example use.
### Advanced configuration
You may want to filter on enrich spans and send them to multiple destinations (e.g. for debugging or telemetry self-diagnostics purposes).
You may configure multiple processing pipelines for each destination like shown in below example.
In this example
1. First pipeline sends all sampled in spans to Zipkin
2. Second pipeline sends spans to ApplicationInsights, but filters them first with custom built `FilteringSpanProcessor`
3. Third pipeline adds custom `DebuggingSpanProcessor` that simply logs all calls to debug output
```csharp
using (var tracerFactory = TracerFactory.Create(builder => builder
.UseZipkin(o =>
{
o.Endpoint = new Uri(zipkinUri);
})
.UseApplicationInsights(
o => o.InstrumentationKey = "your-instrumentation-key",
p => p.AddProcessor(nextProcessor => new FilteringSpanProcessor(nextProcessor)))
.AddProcessorPipeline(pipelineBuilder => pipelineBuilder.AddProcessor(_ => new DebuggingSpanProcessor()))))
.SetResource(Resources.CreateServiceResource("test-zipkin"))
{
// ...
}
```
#### Traces
```csharp

View File

@ -1,95 +0,0 @@
// <copyright file="StubTelemetryChannel.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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 theLicense 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 Microsoft.ApplicationInsights.Channel;
using System;
namespace OpenTelemetry.Exporter.ApplicationInsights.Tests
{
/// <summary>
/// A stub of <see cref="ITelemetryChannel"/>.
/// </summary>
public sealed class StubTelemetryChannel : ITelemetryChannel
{
/// <summary>
/// Initializes a new instance of the <see cref="StubTelemetryChannel"/> class.
/// </summary>
public StubTelemetryChannel()
{
OnSend = telemetry => { };
OnFlush = () => { };
OnDispose = () => { };
}
/// <summary>
/// Gets or sets a value indicating whether this channel is in developer mode.
/// </summary>
public bool? DeveloperMode { get; set; }
/// <summary>
/// Gets or sets a value indicating the channel's URI. To this URI the telemetry is expected to be sent.
/// </summary>
public string EndpointAddress { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to throw an error.
/// </summary>
public bool ThrowError { get; set; }
/// <summary>
/// Gets or sets the callback invoked by the <see cref="Send"/> method.
/// </summary>
public Action<ITelemetry> OnSend { get; set; }
/// <summary>
/// Gets or sets the callback invoked by the <see cref="Flush"/> method.
/// </summary>
public Action OnFlush { get; set; }
/// <summary>
/// Gets or sets the callback invoked by the <see cref="Dispose"/> method.
/// </summary>
public Action OnDispose { get; set; }
/// <summary>
/// Implements the <see cref="ITelemetryChannel.Send"/> method by invoking the <see cref="OnSend"/> callback.
/// </summary>
public void Send(ITelemetry item)
{
if (ThrowError)
{
throw new Exception("test error");
}
OnSend(item);
}
/// <summary>
/// Implements the <see cref="IDisposable.Dispose"/> method.
/// </summary>
public void Dispose()
{
OnDispose();
}
/// <summary>
/// Implements the <see cref="ITelemetryChannel.Flush" /> method.
/// </summary>
public void Flush()
{
OnFlush();
}
}
}

View File

@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Unit test project for ApplicationInsights Exporter for OpenTelemetry</Description>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);net46</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Content Include="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\OpenTelemetry.Exporter.ApplicationInsights\OpenTelemetry.Exporter.ApplicationInsights.csproj" />
<ProjectReference Include="..\..\src\OpenTelemetry\OpenTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Moq" Version="4.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="True" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
</Project>

View File

@ -1,60 +0,0 @@
// <copyright file="TracerBuilderConfigurationTests.cs" company="OpenTelemetry Authors">
// Copyright 2018, 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.Collections.Concurrent;
using Microsoft.ApplicationInsights.Channel;
using OpenTelemetry.Trace;
using OpenTelemetry.Trace.Configuration;
using OpenTelemetry.Trace.Export;
using Xunit;
namespace OpenTelemetry.Exporter.ApplicationInsights.Tests
{
public class TracerBuilderConfigurationTest
{
[Fact]
public void UseApplicationInsights_ConfiguresExporter()
{
var sentItems = new ConcurrentQueue<ITelemetry>();
ITelemetryChannel channel = new StubTelemetryChannel
{
OnSend = t => sentItems.Enqueue(t),
EndpointAddress = "http://foo",
};
var tracer = TracerFactory.Create(b => b
.UseApplicationInsights(
o => o.TelemetryChannel = channel,
p => p.SetExportingProcessor(e => new SimpleSpanProcessor(e))))
.GetTracer(null);
tracer.StartSpan("foo").End();
Assert.Single(sentItems);
}
[Fact]
public void UseApplicationInsights_BadArgs()
{
TracerBuilder builder = null;
Assert.Throws<ArgumentNullException>(() => builder.UseApplicationInsights(_ => { }));
Assert.Throws<ArgumentNullException>(() => TracerFactory.Create(b => b.UseApplicationInsights(null)));
}
}
}

View File

@ -1,3 +0,0 @@
{
"parallelizeTestCollections": true
}