Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Improve AppInsights setup #2597

Merged
merged 6 commits into from
Nov 3, 2022
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
7 changes: 3 additions & 4 deletions src/ApiService/ApiService/ApiService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="Azure.ResourceManager.Monitor" Version="1.0.0-beta.2" />
<PackageReference Include="Faithlife.Utility" Version="0.12.2" />
<PackageReference Include="Microsoft.ApplicationInsights.DependencyCollector" Version="2.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.0.0-preview3" />
<PackageReference Include="Semver" Version="2.1.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.0" />
Expand All @@ -20,9 +20,8 @@
<PackageReference Include="Microsoft.Azure.Management.OperationalInsights" Version="0.24.0-preview" />
<PackageReference Include="Microsoft.Azure.Management.Monitor" Version="0.28.0-preview" />

<PackageReference Include="Microsoft.Extensions.Logging.ApplicationInsights" Version="2.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.3.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.6.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" OutputItemType="Analyzer" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
<PackageReference Include="Azure.Data.Tables" Version="12.5.0" />
<PackageReference Include="Azure.ResourceManager.Compute" Version="1.0.0-beta.8" />
<PackageReference Include="Azure.Core" Version="1.25.0" />
Expand Down
78 changes: 40 additions & 38 deletions src/ApiService/ApiService/Log.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Text;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;

namespace Microsoft.OneFuzz.Service;

Expand Down Expand Up @@ -65,58 +64,61 @@ public interface ILog {
class AppInsights : ILog {
private readonly TelemetryClient _telemetryClient;

public AppInsights(TelemetryConfiguration config) {
_telemetryClient = new TelemetryClient(config);
public AppInsights(TelemetryClient client) {
_telemetryClient = client;
}

public void Log(Guid correlationId, LogStringHandler message, SeverityLevel level, IReadOnlyDictionary<string, string> tags, string? caller) {
Dictionary<string, string> copyTags = new(tags);
copyTags["CorrelationId"] = correlationId.ToString();
if (message.Tags is not null) {
foreach (var kv in message.Tags) {
copyTags[kv.Key] = kv.Value;
private static void Copy<K, V>(IDictionary<K, V> target, IReadOnlyDictionary<K, V>? source) {
if (source is not null) {
foreach (var kvp in source) {
target[kvp.Key] = kvp.Value;
}
}
}

if (caller is not null) copyTags["CalledBy"] = caller;
_telemetryClient.TrackTrace(message.ToString(), level, copyTags);
public void Log(Guid correlationId, LogStringHandler message, SeverityLevel level, IReadOnlyDictionary<string, string> tags, string? caller) {
var telemetry = new TraceTelemetry(message.ToString(), level);

// copy properties
Copy(telemetry.Properties, tags);
telemetry.Properties["CorrelationId"] = correlationId.ToString();
if (caller is not null) telemetry.Properties["CalledBy"] = caller;
Copy(telemetry.Properties, message.Tags);

_telemetryClient.TrackTrace(telemetry);
}

public void LogEvent(Guid correlationId, LogStringHandler evt, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller) {
Dictionary<string, string> copyTags = new(tags);
copyTags["CorrelationId"] = correlationId.ToString();
if (caller is not null) copyTags["CalledBy"] = caller;
var telemetry = new EventTelemetry(evt.ToString());

if (evt.Tags is not null) {
foreach (var kv in evt.Tags) {
copyTags[kv.Key] = kv.Value;
}
}
// copy properties
Copy(telemetry.Properties, tags);
telemetry.Properties["CorrelationId"] = correlationId.ToString();
if (caller is not null) telemetry.Properties["CalledBy"] = caller;
Copy(telemetry.Properties, evt.Tags);

Dictionary<string, double>? copyMetrics = null;
if (metrics is not null) {
copyMetrics = new(metrics);
}
// copy metrics
Copy(telemetry.Metrics, metrics);

_telemetryClient.TrackEvent(evt.ToString(), properties: copyTags, metrics: copyMetrics);
_telemetryClient.TrackEvent(telemetry);
}

public void LogException(Guid correlationId, Exception ex, LogStringHandler message, IReadOnlyDictionary<string, string> tags, IReadOnlyDictionary<string, double>? metrics, string? caller) {
Dictionary<string, string> copyTags = new(tags);
copyTags["CorrelationId"] = correlationId.ToString();
if (caller is not null) copyTags["CalledBy"] = caller;
{
var telemetry = new ExceptionTelemetry(ex);

if (message.Tags is not null) {
foreach (var kv in message.Tags) {
copyTags[kv.Key] = kv.Value;
}
}
// copy properties
Copy(telemetry.Properties, tags);
telemetry.Properties["CorrelationId"] = correlationId.ToString();
if (caller is not null) telemetry.Properties["CalledBy"] = caller;
Copy(telemetry.Properties, message.Tags);

// copy metrics
Copy(telemetry.Metrics, metrics);

Dictionary<string, double>? copyMetrics = null;
if (metrics is not null) {
copyMetrics = new(metrics);
_telemetryClient.TrackException(telemetry);
}
_telemetryClient.TrackException(ex, copyTags, copyMetrics);

Log(correlationId, $"[{message}] {ex.Message}", SeverityLevel.Error, tags, caller);
}

Expand Down Expand Up @@ -366,12 +368,12 @@ public interface ILogSinks {
public class LogSinks : ILogSinks {
private readonly List<ILog> _loggers;

public LogSinks(IServiceConfig config, TelemetryConfiguration telemetryConfiguration) {
public LogSinks(IServiceConfig config, TelemetryClient telemetryClient) {
_loggers = new List<ILog>();
foreach (var dest in config.LogDestinations) {
_loggers.Add(
dest switch {
LogDestination.AppInsights => new AppInsights(telemetryConfiguration),
LogDestination.AppInsights => new AppInsights(telemetryClient),
LogDestination.Console => new Console(),
_ => throw new Exception($"Unhandled Log Destination type: {dest}"),
}
Expand Down
30 changes: 9 additions & 21 deletions src/ApiService/ApiService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Azure.Core.Serialization;
using Azure.Identity;
using Microsoft.ApplicationInsights.DependencyCollector;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -44,13 +43,16 @@ public async Async.Task Invoke(FunctionContext context, FunctionExecutionDelegat
//Move out expensive resources into separate class, and add those as Singleton
// ArmClient, Table Client(s), Queue Client(s), HttpClient, etc.
public static async Async.Task Main() {
var configuration = new ServiceConfiguration();

using var host =
new HostBuilder()
.ConfigureFunctionsWorkerDefaults(
builder => {
builder.UseMiddleware<LoggingMiddleware>();
}
)
.ConfigureFunctionsWorkerDefaults(builder => {
builder.UseMiddleware<LoggingMiddleware>();
builder.AddApplicationInsights(options => {
options.ConnectionString = $"InstrumentationKey={configuration.ApplicationInsightsInstrumentationKey}";
});
})
.ConfigureServices((context, services) => {
services.Configure<JsonSerializerOptions>(options => {
options = EntityConverter.GetJsonSerializerOptions();
Expand Down Expand Up @@ -110,32 +112,18 @@ public static async Async.Task Main() {
.AddScoped<INodeMessageOperations, NodeMessageOperations>()
.AddScoped<ISubnet, Subnet>()
.AddScoped<IAutoScaleOperations, AutoScaleOperations>()

.AddSingleton<TelemetryConfiguration>(provider => {
var config = provider.GetRequiredService<IServiceConfig>();
return new() {
ConnectionString = $"InstrumentationKey={config.ApplicationInsightsInstrumentationKey}",
};
})
.AddSingleton<GraphServiceClient>(new GraphServiceClient(new DefaultAzureCredential()))
.AddSingleton<DependencyTrackingTelemetryModule>()
.AddSingleton<ICreds, Creds>()
.AddSingleton<EntityConverter>()
.AddSingleton<IServiceConfig, ServiceConfiguration>()
.AddSingleton<IServiceConfig>(configuration)
.AddSingleton<IStorage, Storage>()
.AddSingleton<ILogSinks, LogSinks>()
.AddHttpClient()
.AddMemoryCache();
})
.Build();

// Set up Application Insights dependency tracking:
{
var telemetryConfig = host.Services.GetRequiredService<TelemetryConfiguration>();
var module = host.Services.GetRequiredService<DependencyTrackingTelemetryModule>();
module.Initialize(telemetryConfig);
}

// Initialize expected Storage tables:
await SetupStorage(
host.Services.GetRequiredService<IStorage>(),
Expand Down
Loading