mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-23 06:38:50 +00:00
Logging (#1737)
* Logging * Doing dependency injection * expose GetLoggers for better testing Co-authored-by: stas <statis@microsoft.com>
This commit is contained in:
@ -1,8 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
namespace Microsoft.OneFuzz.Service;
|
namespace Microsoft.OneFuzz.Service;
|
||||||
|
|
||||||
|
public enum LogDestination
|
||||||
|
{
|
||||||
|
Console,
|
||||||
|
AppInsights,
|
||||||
|
}
|
||||||
|
|
||||||
public static class EnvironmentVariables {
|
public static class EnvironmentVariables {
|
||||||
|
|
||||||
|
static EnvironmentVariables() {
|
||||||
|
LogDestinations = new LogDestination[] { LogDestination.AppInsights };
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Add environment variable to control where to write logs to
|
||||||
|
public static LogDestination[] LogDestinations { get; set; }
|
||||||
|
|
||||||
public static class AppInsights {
|
public static class AppInsights {
|
||||||
public static string? AppId { get => Environment.GetEnvironmentVariable("APPINSIGHTS_APPID"); }
|
public static string? AppId { get => Environment.GetEnvironmentVariable("APPINSIGHTS_APPID"); }
|
||||||
public static string? InstrumentationKey { get => Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY"); }
|
public static string? InstrumentationKey { get => Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY"); }
|
||||||
|
173
src/ApiService/ApiService/Log.cs
Normal file
173
src/ApiService/ApiService/Log.cs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.OneFuzz.Service;
|
||||||
|
|
||||||
|
public interface ILog {
|
||||||
|
void Log(Guid correlationId, String message, SeverityLevel level, IDictionary<string, string> tags, string? caller);
|
||||||
|
void LogEvent(Guid correlationId, String evt, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller);
|
||||||
|
void LogException(Guid correlationId, Exception ex, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller);
|
||||||
|
void Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppInsights : ILog {
|
||||||
|
private TelemetryClient telemetryClient =
|
||||||
|
new TelemetryClient(
|
||||||
|
new TelemetryConfiguration(EnvironmentVariables.AppInsights.InstrumentationKey));
|
||||||
|
|
||||||
|
public void Log(Guid correlationId, String message, SeverityLevel level, IDictionary<string, string> tags, string? caller) {
|
||||||
|
tags.Add("Correlation ID", correlationId.ToString());
|
||||||
|
if (caller is not null) tags.Add("CalledBy", caller);
|
||||||
|
telemetryClient.TrackTrace(message, level, tags);
|
||||||
|
}
|
||||||
|
public void LogEvent(Guid correlationId, String evt, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller) {
|
||||||
|
tags.Add("Correlation ID", correlationId.ToString());
|
||||||
|
if (caller is not null) tags.Add("CalledBy", caller);
|
||||||
|
telemetryClient.TrackEvent(evt, properties: tags, metrics: metrics);
|
||||||
|
}
|
||||||
|
public void LogException(Guid correlationId, Exception ex, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller) {
|
||||||
|
tags.Add("Correlation ID", correlationId.ToString());
|
||||||
|
|
||||||
|
if (caller is not null) tags.Add("CalledBy", caller);
|
||||||
|
telemetryClient.TrackException(ex, tags, metrics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush() {
|
||||||
|
telemetryClient.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Should we write errors and Exception to std err ?
|
||||||
|
class Console : ILog {
|
||||||
|
|
||||||
|
private string DictToString<T>(IDictionary<string, T>? d) {
|
||||||
|
if (d is null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return string.Join("", d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogTags(Guid correlationId, string? caller, IDictionary<string, string> tags) {
|
||||||
|
var ts = DictToString(tags);
|
||||||
|
if (!string.IsNullOrEmpty(ts))
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("[{0}:{1}] Tags:{2}", correlationId, caller, ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogMetrics(Guid correlationId, string? caller, IDictionary<string, double>? metrics) {
|
||||||
|
var ms = DictToString(metrics);
|
||||||
|
if (!string.IsNullOrEmpty(ms)) {
|
||||||
|
System.Console.Out.WriteLine("[{0}:{1}] Metrics:{2}", correlationId, caller, DictToString(metrics));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(Guid correlationId, String message, SeverityLevel level, IDictionary<string, string> tags, string? caller) {
|
||||||
|
System.Console.Out.WriteLine("[{0}:{1}][{2}] {3}", correlationId, caller, level, message);
|
||||||
|
LogTags(correlationId, caller, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogEvent(Guid correlationId, String evt, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller) {
|
||||||
|
System.Console.Out.WriteLine("[{0}:{1}][Event] {2}", correlationId, caller, evt);
|
||||||
|
LogTags(correlationId, caller, tags);
|
||||||
|
LogMetrics(correlationId, caller, metrics);
|
||||||
|
}
|
||||||
|
public void LogException(Guid correlationId, Exception ex, IDictionary<string, string> tags, IDictionary<string, double>? metrics, string? caller) {
|
||||||
|
System.Console.Out.WriteLine("[{0}:{1}][Exception] {2}", correlationId, caller, ex);
|
||||||
|
LogTags(correlationId, caller, tags);
|
||||||
|
LogMetrics(correlationId, caller, metrics);
|
||||||
|
}
|
||||||
|
public void Flush() {
|
||||||
|
System.Console.Out.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogTracer {
|
||||||
|
|
||||||
|
private List<ILog> loggers;
|
||||||
|
|
||||||
|
private IDictionary<string, string> tags = new Dictionary<string, string>();
|
||||||
|
private Guid correlationId;
|
||||||
|
|
||||||
|
public LogTracer(Guid correlationId, List<ILog> loggers) {
|
||||||
|
this.correlationId = correlationId;
|
||||||
|
this.loggers = loggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDictionary<string, string> Tags => tags;
|
||||||
|
|
||||||
|
public void Info(string message) {
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers) {
|
||||||
|
logger.Log(correlationId, message, SeverityLevel.Information, Tags, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warning(string message) {
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers) {
|
||||||
|
logger.Log(correlationId, message, SeverityLevel.Warning, Tags, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string message)
|
||||||
|
{
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Log(correlationId, message, SeverityLevel.Error, Tags, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Critical(string message)
|
||||||
|
{
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers)
|
||||||
|
{
|
||||||
|
logger.Log(correlationId, message, SeverityLevel.Critical, Tags, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Event(string evt, IDictionary<string, double>? metrics) {
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers) {
|
||||||
|
logger.LogEvent(correlationId, evt, Tags, metrics, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exception(Exception ex, IDictionary<string, double>? metrics) {
|
||||||
|
var caller = new StackTrace()?.GetFrame(1)?.GetMethod()?.Name;
|
||||||
|
foreach (var logger in loggers) {
|
||||||
|
logger.LogException(correlationId, ex, Tags, metrics, caller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceFlush() {
|
||||||
|
foreach (var logger in loggers) {
|
||||||
|
logger.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LogTracerFactory {
|
||||||
|
|
||||||
|
private List<ILog> loggers;
|
||||||
|
|
||||||
|
public LogTracerFactory(List<ILog> loggers) {
|
||||||
|
this.loggers = loggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LogTracer MakeLogTracer(Guid correlationId) {
|
||||||
|
return new LogTracer(correlationId, this.loggers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Azure.Functions.Worker.Configuration;
|
using Microsoft.Azure.Functions.Worker.Configuration;
|
||||||
|
using Azure.ResourceManager.Storage.Models;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.OneFuzz.Service;
|
namespace Microsoft.OneFuzz.Service;
|
||||||
|
|
||||||
public class Program
|
public class Program
|
||||||
{
|
{
|
||||||
|
public static List<ILog> GetLoggers() {
|
||||||
|
List<ILog> loggers = new List<ILog>();
|
||||||
|
foreach (var dest in EnvironmentVariables.LogDestinations)
|
||||||
|
{
|
||||||
|
loggers.Add(
|
||||||
|
dest switch
|
||||||
|
{
|
||||||
|
LogDestination.AppInsights => new AppInsights(),
|
||||||
|
LogDestination.Console => new Console(),
|
||||||
|
_ => throw new Exception(string.Format("Unhandled Log Destination type: {0}", dest)),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return loggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void Main()
|
public static void Main()
|
||||||
{
|
{
|
||||||
var host = new HostBuilder()
|
var host = new HostBuilder()
|
||||||
.ConfigureFunctionsWorkerDefaults()
|
.ConfigureFunctionsWorkerDefaults()
|
||||||
.Build();
|
.ConfigureServices((context, services) =>
|
||||||
|
services.AddSingleton<LogTracerFactory>(_ => new LogTracerFactory(GetLoggers()))
|
||||||
|
)
|
||||||
|
.Build();
|
||||||
|
|
||||||
host.Run();
|
host.Run();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user