using Microsoft.ApplicationInsights.DataContracts; using System.Text; namespace Core.Logging { public class SeqLogger { private readonly SeqHttpClient _httpClient; private readonly string _environmentName; private readonly string _machineName; private readonly SeqConfiguration _configuration; public SeqLogger(SeqHttpClient httpClient, string environmentName, SeqConfiguration configuration) { _httpClient = httpClient; _environmentName = configuration.Environment; _machineName = Environment.MachineName; } public async Task LogAsync(TraceTelemetry trace, CancellationToken cancellationToken = default) { var seqEvent = new Dictionary { { "@t", trace.Timestamp.UtcDateTime.ToString("o") }, { "@mt", trace.Message }, { "@l", MapSeverityToLevel(trace.SeverityLevel) }, { "Environment", _environmentName }, { "MachineName", _machineName } }; foreach (var prop in trace.Properties) { seqEvent.Add(prop.Key, prop.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } public async Task LogAsync(EventTelemetry evt, CancellationToken cancellationToken = default) { var seqEvent = new Dictionary { { "@t", evt.Timestamp.UtcDateTime.ToString("o") }, { "@mt", evt.Name }, { "@l", "Information" }, { "Environment", _environmentName }, { "MachineName", _machineName } }; foreach (var prop in evt.Properties) { seqEvent.Add(prop.Key, prop.Value); } foreach (var metric in evt.Metrics) { seqEvent.Add($"metric_{metric.Key}", metric.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } public async Task LogAsync(ExceptionTelemetry ex, CancellationToken cancellationToken = default) { var seqEvent = new Dictionary { { "@t", ex.Timestamp.UtcDateTime.ToString("o") }, { "@mt", ex.Exception.Message }, { "@l", "Error" }, { "@x", FormatExceptionForSeq(ex.Exception) }, { "Environment", _environmentName }, { "MachineName", _machineName }, { "ExceptionType", ex.Exception.GetType().Name }, }; foreach (var prop in ex.Properties) { seqEvent.Add(prop.Key, prop.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } public async Task LogAsync(DependencyTelemetry dep, CancellationToken cancellationToken = default) { var seqEvent = new Dictionary { { "@t", dep.Timestamp.UtcDateTime.ToString("o") }, { "@mt", $"Dependency: {dep.Name}" }, { "@l", dep.Success??true ? "Information" : "Error" }, { "Environment", _environmentName }, { "MachineName", _machineName }, { "DependencyType", dep.Type }, { "Target", dep.Target }, { "Duration", dep.Duration.TotalMilliseconds } }; foreach (var prop in dep.Properties) { seqEvent.Add(prop.Key, prop.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } public async Task LogAsync(RequestTelemetry req, CancellationToken cancellationToken = default) { var seqEvent = new Dictionary { { "@t", req.Timestamp.UtcDateTime.ToString("o") }, { "@mt",$"{req.Properties["httpMethod"]} {req.Name}" }, { "@l", req.Success??true ? "Information" : "Error" }, { "Environment", _environmentName }, { "MachineName", _machineName }, { "Url", req.Url }, { "ResponseCode", req.ResponseCode }, { "Application", "sadasd" }, { "RequestMethod", req.Properties["httpMethod"] }, { "RequestId", req.Id }, { "ItemTypeFlag", req.ItemTypeFlag.ToString() }, { "@sp", "12"}, { "@tr", "23344"}, { "@sk","Server" }, { "@st", req.Timestamp.UtcDateTime.Subtract(req.Duration).ToString("o") } }; req.Properties.Remove("httpMethod"); //we should add a property with name { "Application", "<...>" } other the Seq Span is not looking ok foreach (var prop in req.Properties) { seqEvent.Add(prop.Key, prop.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } public async Task LogAsync(Microsoft.ApplicationInsights.Extensibility.IOperationHolder operationHolder, CancellationToken cancellationToken = default) { var req = operationHolder.Telemetry; var seqEvent = new Dictionary { { "@t", req.Timestamp.UtcDateTime.ToString("o") }, { "@mt",$"{req.Properties["httpMethod"]} {req.Name}" }, { "@l", req.Success??true ? "Information" : "Error" }, { "Environment", _environmentName }, { "MachineName", _machineName }, { "Url", req.Url }, { "ResponseCode", req.ResponseCode }, { "Application", "sadasd" }, { "RequestMethod", req.Properties["httpMethod"] }, { "RequestId", req.Id }, { "ItemTypeFlag", req.ItemTypeFlag.ToString() }, { "@sp", "12"}, { "@tr", "23344"}, { "@sk","Server" }, { "@st", req.Timestamp.UtcDateTime.Subtract(req.Duration).ToString("o") } }; req.Properties.Remove("httpMethod"); //we should add a property with name { "Application", "<...>" } other the Seq Span is not looking ok foreach (var prop in req.Properties) { seqEvent.Add(prop.Key, prop.Value); } await SendToSeqAsync(seqEvent, cancellationToken); } private async Task SendToSeqAsync(Dictionary seqEvent, CancellationToken cancellationToken) { var content = new StringContent( Newtonsoft.Json.JsonConvert.SerializeObject(seqEvent), Encoding.UTF8, "application/vnd.serilog.clef"); var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/ingest/clef") { Content = content }; var result = await _httpClient.SendAsync(requestMessage, cancellationToken); result.EnsureSuccessStatusCode(); } private string MapSeverityToLevel(SeverityLevel? severity) { return severity switch { SeverityLevel.Verbose => "Verbose", SeverityLevel.Information => "Information", SeverityLevel.Warning => "Warning", SeverityLevel.Error => "Error", SeverityLevel.Critical => "Fatal", _ => "Information" }; } private string FormatExceptionForSeq(Exception ex) { var sb = new StringBuilder(); var exceptionCount = 0; void FormatSingleException(Exception currentEx, int depth) { if (depth > 0) sb.AppendLine("\n--- Inner Exception ---"); sb.AppendLine($"Exception Type: {currentEx.GetType().FullName}"); sb.AppendLine($"Message: {currentEx.Message}"); sb.AppendLine($"Source: {currentEx.Source}"); sb.AppendLine($"HResult: 0x{currentEx.HResult:X8}"); sb.AppendLine("Stack Trace:"); sb.AppendLine(currentEx.StackTrace?.Trim()); if (currentEx.Data.Count > 0) { sb.AppendLine("Additional Data:"); foreach (var key in currentEx.Data.Keys) { sb.AppendLine($" {key}: {currentEx.Data[key]}"); } } } void RecurseExceptions(Exception currentEx, int depth = 0) { if (currentEx is AggregateException aggEx) { foreach (var inner in aggEx.InnerExceptions) { RecurseExceptions(inner, depth); depth++; } } else if (currentEx.InnerException != null) { RecurseExceptions(currentEx.InnerException, depth + 1); } FormatSingleException(currentEx, depth); exceptionCount++; } RecurseExceptions(ex); sb.Insert(0, $"EXCEPTION CHAIN ({exceptionCount} exceptions):\n"); return sb.ToString(); } } }