From 2220161e87a359a8bf3dc045b06cc0d17c1da719 Mon Sep 17 00:00:00 2001 From: Zafer Balkan Date: Wed, 20 Nov 2024 14:30:35 +0200 Subject: [PATCH] Implemented syslog with Serilog --- Apps/LogExporterApp/LogExporterApp.csproj | 4 +- .../Strategy/SyslogExportStrategy.cs | 145 +++++++++--------- 2 files changed, 77 insertions(+), 72 deletions(-) diff --git a/Apps/LogExporterApp/LogExporterApp.csproj b/Apps/LogExporterApp/LogExporterApp.csproj index 6c6107d2..16020dbf 100644 --- a/Apps/LogExporterApp/LogExporterApp.csproj +++ b/Apps/LogExporterApp/LogExporterApp.csproj @@ -20,7 +20,9 @@ - + + + diff --git a/Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs b/Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs index a01c81c6..eee2d356 100644 --- a/Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs +++ b/Apps/LogExporterApp/Strategy/SyslogExportStrategy.cs @@ -17,9 +17,10 @@ along with this program. If not, see . */ -using SyslogNet.Client; -using SyslogNet.Client.Serialization; -using SyslogNet.Client.Transport; +using Serilog; +using Serilog.Events; +using Serilog.Parsing; +using Serilog.Sinks.Syslog; using System; using System.Collections.Generic; using System.Linq; @@ -33,21 +34,17 @@ namespace LogExporter.Strategy private const string _appName = "Technitium DNS Server"; - private const string _msgId = "dnslog"; - - private const string _sdId = "dnsparams"; + private const string _sdId = "meta"; private const string DEFAUL_PROTOCOL = "udp"; private const int DEFAULT_PORT = 514; - private readonly string _host; + private readonly Facility _facility = Facility.Local6; - private readonly string _processId; + private readonly Rfc5424Formatter _formatter; - private readonly ISyslogMessageSender _sender; - - private readonly ISyslogMessageSerializer _serializer; + private readonly Serilog.Core.Logger _sender; private bool disposedValue; @@ -60,18 +57,18 @@ namespace LogExporter.Strategy port ??= DEFAULT_PORT; protocol ??= DEFAUL_PROTOCOL; + var conf = new LoggerConfiguration(); + _sender = protocol.ToLowerInvariant() switch { - "tls" => new SyslogEncryptedTcpSender(address, port.Value), - "tcp" => new SyslogTcpSender(address, port.Value), - "udp" => new SyslogUdpSender(address, port.Value), - "local" => new SyslogLocalSender(), + "tls" => conf.WriteTo.TcpSyslog(address, port.Value, _appName, FramingType.OCTET_COUNTING, SyslogFormat.RFC5424, _facility, useTls: true).Enrich.FromLogContext().CreateLogger(), + "tcp" => conf.WriteTo.TcpSyslog(address, port.Value, _appName, FramingType.OCTET_COUNTING, SyslogFormat.RFC5424, _facility, useTls: false).Enrich.FromLogContext().CreateLogger(), + "udp" => conf.WriteTo.UdpSyslog(address, port.Value, _appName, SyslogFormat.RFC5424, _facility).Enrich.FromLogContext().CreateLogger(), + "local" => conf.WriteTo.LocalSyslog(_appName, _facility).Enrich.FromLogContext().CreateLogger(), _ => throw new Exception("Invalid protocol specified"), }; - _serializer = new SyslogRfc5424MessageSerializer(); - _processId = Environment.ProcessId.ToString(); - _host = Environment.MachineName; + _formatter = new Rfc5424Formatter(_facility, _appName, null, _sdId, Environment.MachineName); } #endregion constructor @@ -80,11 +77,12 @@ namespace LogExporter.Strategy public Task ExportAsync(List logs) { - return Task.Run(() => - { - var messages = new List(logs.Select(Convert)); - _sender.Send(messages, _serializer); - }); + var tasks = logs + .Select(log => Task.Run(() => + Log.Information(_formatter.FormatMessage(Convert(log)))) + ); + + return Task.WhenAll(tasks); } #endregion public @@ -115,83 +113,88 @@ namespace LogExporter.Strategy #region private - private SyslogMessage Convert(LogEntry log) + private LogEvent Convert(LogEntry log) { - // Create the structured data with all key details from LogEntry - var elements = new StructuredDataElement(_sdId, new Dictionary(StringComparer.OrdinalIgnoreCase) + // Initialize properties with base log details + var properties = new List { - { "timestamp", log.Timestamp.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") }, - { "clientIp", log.ClientIp }, - { "clientPort", log.ClientPort.ToString() }, - { "dnssecOk", log.DnssecOk.ToString() }, - { "protocol", log.Protocol.ToString() }, - { "rCode", log.ResponseCode.ToString() } - }); + new LogEventProperty("timestamp", new ScalarValue(log.Timestamp.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"))), + new LogEventProperty("clientIp", new ScalarValue(log.ClientIp)), + new LogEventProperty("clientPort", new ScalarValue(log.ClientPort.ToString())), + new LogEventProperty("dnssecOk", new ScalarValue(log.DnssecOk.ToString())), + new LogEventProperty("protocol", new ScalarValue(log.Protocol.ToString())), + new LogEventProperty("rCode", new ScalarValue(log.ResponseCode.ToString())) + }; - // Add each question to the structured data + // Add each question as properties if (log.Questions?.Count > 0) { for (int i = 0; i < log.Questions.Count; i++) { var question = log.Questions[i]; - elements.Parameters.Add($"qName_{i}", question.QuestionName); - elements.Parameters.Add($"qType_{i}", question.QuestionType.HasValue ? question.QuestionType.Value.ToString() : "unknown"); - elements.Parameters.Add($"qClass_{i}", question.QuestionClass.HasValue ? question.QuestionClass.Value.ToString() : "unknown"); - elements.Parameters.Add($"qSize_{i}", question.Size.ToString()); + properties.Add(new LogEventProperty($"qName_{i}", new ScalarValue(question.QuestionName))); + properties.Add(new LogEventProperty($"qType_{i}", new ScalarValue(question.QuestionType?.ToString() ?? "unknown"))); + properties.Add(new LogEventProperty($"qClass_{i}", new ScalarValue(question.QuestionClass?.ToString() ?? "unknown"))); + properties.Add(new LogEventProperty($"qSize_{i}", new ScalarValue(question.Size.ToString()))); } + + // Generate questions summary + var questionSummary = string.Join("; ", log.Questions.Select((q, i) => + $"QNAME_{i}: {q.QuestionName}, QTYPE: {q.QuestionType?.ToString() ?? "unknown"}, QCLASS: {q.QuestionClass?.ToString() ?? "unknown"}")); + properties.Add(new LogEventProperty("questionsSummary", new ScalarValue(questionSummary))); + } + else + { + properties.Add(new LogEventProperty("questionsSummary", new ScalarValue(string.Empty))); } - // Add each answer to the structured data + // Add each answer as properties if (log.Answers?.Count > 0) { for (int i = 0; i < log.Answers.Count; i++) { var answer = log.Answers[i]; - elements.Parameters.Add($"aType_{i}", answer.RecordType.ToString()); - elements.Parameters.Add($"aData_{i}", answer.RecordData); - elements.Parameters.Add($"aClass_{i}", answer.RecordClass.ToString()); - elements.Parameters.Add($"aTtl_{i}", answer.RecordTtl.ToString()); - elements.Parameters.Add($"aSize_{i}", answer.Size.ToString()); - elements.Parameters.Add($"aDnssecStatus_{i}", answer.DnssecStatus.ToString()); + properties.Add(new LogEventProperty($"aType_{i}", new ScalarValue(answer.RecordType.ToString()))); + properties.Add(new LogEventProperty($"aData_{i}", new ScalarValue(answer.RecordData))); + properties.Add(new LogEventProperty($"aClass_{i}", new ScalarValue(answer.RecordClass.ToString()))); + properties.Add(new LogEventProperty($"aTtl_{i}", new ScalarValue(answer.RecordTtl.ToString()))); + properties.Add(new LogEventProperty($"aSize_{i}", new ScalarValue(answer.Size.ToString()))); + properties.Add(new LogEventProperty($"aDnssecStatus_{i}", new ScalarValue(answer.DnssecStatus.ToString()))); } + + // Generate answers summary + var answerSummary = string.Join(", ", log.Answers.Select(a => a.RecordData)); + properties.Add(new LogEventProperty("answersSummary", new ScalarValue(answerSummary))); + } + else + { + properties.Add(new LogEventProperty("answersSummary", new ScalarValue(string.Empty))); } - // Include request and response tags if present + // Add request and response tags if present if (log.RequestTag != null) { - elements.Parameters.Add("requestTag", log.RequestTag.ToString()); + properties.Add(new LogEventProperty("requestTag", new ScalarValue(log.RequestTag.ToString()))); } if (log.ResponseTag != null) { - elements.Parameters.Add("responseTag", log.ResponseTag.ToString()); + properties.Add(new LogEventProperty("responseTag", new ScalarValue(log.ResponseTag.ToString()))); } - // Build a comprehensive message summary - string questionSummary = log.Questions?.Count > 0 - ? string.Join("; ", log.Questions.Select(q => - $"QNAME: {q.QuestionName}; QTYPE: {q.QuestionType?.ToString() ?? "unknown"}; QCLASS: {q.QuestionClass?.ToString() ?? "unknown"}")) - : string.Empty; + // Define the message template to match the original summary format + const string templateText = "{questionsSummary}; RCODE: {rCode}; ANSWER: [{answersSummary}]"; - // Build the answer summary in the desired format - string answerSummary = log.Answers?.Count > 0 - ? string.Join(", ", log.Answers.Select(a => a.RecordData)) - : string.Empty; + // Parse the template + var template = new MessageTemplateParser().Parse(templateText); - // Construct the message summary string to match the desired format - string messageSummary = $"{questionSummary}; RCODE: {log.ResponseCode}; ANSWER: [{answerSummary}]"; - - // Create and return the syslog message - return new SyslogMessage( - log.Timestamp, - Facility.UserLevelMessages, - Severity.Informational, - _host, - _appName, - _processId, - _msgId, - messageSummary, - elements + // Create the LogEvent and return it + return new LogEvent( + timestamp: log.Timestamp, + level: LogEventLevel.Information, + exception: null, + messageTemplate: template, + properties: properties ); }