| | | 1 | | using System.Data.Common; |
| | | 2 | | using System.Globalization; |
| | | 3 | | using System.Text.Json; |
| | | 4 | | using Elsa.Diagnostics.StructuredLogs.Models; |
| | | 5 | | using Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Models; |
| | | 6 | | |
| | | 7 | | namespace Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Services; |
| | | 8 | | |
| | | 9 | | public class RelationalStructuredLogMapper |
| | | 10 | | { |
| | 1 | 11 | | private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web); |
| | | 12 | | |
| | | 13 | | public RelationalStructuredLogRecord Map(StructuredLogEvent logEvent) |
| | | 14 | | { |
| | 2 | 15 | | return new() |
| | 2 | 16 | | { |
| | 2 | 17 | | Id = logEvent.Id, |
| | 2 | 18 | | Sequence = logEvent.Sequence, |
| | 2 | 19 | | Timestamp = FormatTimestamp(logEvent.Timestamp), |
| | 2 | 20 | | ReceivedAt = FormatTimestamp(logEvent.ReceivedAt), |
| | 2 | 21 | | Level = logEvent.Level, |
| | 2 | 22 | | Category = logEvent.Category, |
| | 2 | 23 | | EventId = logEvent.EventId, |
| | 2 | 24 | | EventName = logEvent.EventName, |
| | 2 | 25 | | Message = logEvent.Message, |
| | 2 | 26 | | MessageTemplate = logEvent.MessageTemplate, |
| | 2 | 27 | | ExceptionJson = logEvent.Exception == null ? null : JsonSerializer.Serialize(logEvent.Exception, JsonOptions |
| | 2 | 28 | | ScopesJson = JsonSerializer.Serialize(logEvent.Scopes, JsonOptions), |
| | 2 | 29 | | PropertiesJson = JsonSerializer.Serialize(logEvent.Properties, JsonOptions), |
| | 2 | 30 | | TraceId = logEvent.TraceId, |
| | 2 | 31 | | SpanId = logEvent.SpanId, |
| | 2 | 32 | | CorrelationId = logEvent.CorrelationId, |
| | 2 | 33 | | TenantId = logEvent.TenantId, |
| | 2 | 34 | | WorkflowDefinitionId = logEvent.WorkflowDefinitionId, |
| | 2 | 35 | | WorkflowInstanceId = logEvent.WorkflowInstanceId, |
| | 2 | 36 | | SourceId = logEvent.SourceId |
| | 2 | 37 | | }; |
| | | 38 | | } |
| | | 39 | | |
| | | 40 | | public StructuredLogEvent Map(DbDataReader reader) |
| | | 41 | | { |
| | 3 | 42 | | return new() |
| | 3 | 43 | | { |
| | 3 | 44 | | Id = reader.GetString(reader.GetOrdinal("Id")), |
| | 3 | 45 | | Sequence = reader.GetInt64(reader.GetOrdinal("Sequence")), |
| | 3 | 46 | | Timestamp = ParseTimestamp(reader.GetString(reader.GetOrdinal("Timestamp"))), |
| | 3 | 47 | | ReceivedAt = ParseTimestamp(reader.GetString(reader.GetOrdinal("ReceivedAt"))), |
| | 3 | 48 | | Level = (StructuredLogLevel)reader.GetInt32(reader.GetOrdinal("Level")), |
| | 3 | 49 | | Category = reader.GetString(reader.GetOrdinal("Category")), |
| | 3 | 50 | | EventId = reader.GetInt32(reader.GetOrdinal("EventId")), |
| | 3 | 51 | | EventName = ReadNullableString(reader, "EventName"), |
| | 3 | 52 | | Message = reader.GetString(reader.GetOrdinal("Message")), |
| | 3 | 53 | | MessageTemplate = ReadNullableString(reader, "MessageTemplate"), |
| | 3 | 54 | | Exception = Deserialize<StructuredLogException>(ReadNullableString(reader, "ExceptionJson")), |
| | 3 | 55 | | Scopes = DeserializeDictionary(ReadNullableString(reader, "ScopesJson")), |
| | 3 | 56 | | Properties = DeserializeDictionary(ReadNullableString(reader, "PropertiesJson")), |
| | 3 | 57 | | TraceId = ReadNullableString(reader, "TraceId"), |
| | 3 | 58 | | SpanId = ReadNullableString(reader, "SpanId"), |
| | 3 | 59 | | CorrelationId = ReadNullableString(reader, "CorrelationId"), |
| | 3 | 60 | | TenantId = ReadNullableString(reader, "TenantId"), |
| | 3 | 61 | | WorkflowDefinitionId = ReadNullableString(reader, "WorkflowDefinitionId"), |
| | 3 | 62 | | WorkflowInstanceId = ReadNullableString(reader, "WorkflowInstanceId"), |
| | 3 | 63 | | SourceId = reader.GetString(reader.GetOrdinal("SourceId")) |
| | 3 | 64 | | }; |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | public static string FormatTimestamp(DateTimeOffset timestamp) |
| | | 68 | | { |
| | 11 | 69 | | return timestamp.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture); |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | public static DateTimeOffset ParseTimestamp(string timestamp) |
| | | 73 | | { |
| | 7 | 74 | | return DateTimeOffset.Parse(timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeSt |
| | | 75 | | } |
| | | 76 | | |
| | | 77 | | private static string? ReadNullableString(DbDataReader reader, string name) |
| | | 78 | | { |
| | 33 | 79 | | var ordinal = reader.GetOrdinal(name); |
| | 33 | 80 | | return reader.IsDBNull(ordinal) ? null : reader.GetString(ordinal); |
| | | 81 | | } |
| | | 82 | | |
| | | 83 | | private static IDictionary<string, string?> DeserializeDictionary(string? json) |
| | | 84 | | { |
| | 6 | 85 | | if (string.IsNullOrWhiteSpace(json)) |
| | 4 | 86 | | return new Dictionary<string, string?>(); |
| | | 87 | | |
| | 2 | 88 | | return JsonSerializer.Deserialize<Dictionary<string, string?>>(json, JsonOptions) ?? new Dictionary<string, stri |
| | | 89 | | } |
| | | 90 | | |
| | | 91 | | private static T? Deserialize<T>(string? json) |
| | | 92 | | { |
| | 3 | 93 | | return string.IsNullOrWhiteSpace(json) ? default : JsonSerializer.Deserialize<T>(json, JsonOptions); |
| | | 94 | | } |
| | | 95 | | } |