From 5f12c3fd3bea366cf9731beb7da70374eebbcad2 Mon Sep 17 00:00:00 2001 From: Denis Prokhorchik Date: Sun, 9 Jan 2022 18:55:15 +0300 Subject: [PATCH] feat(issue-125): add & implement the jsonformatter for elk in auth system --- .../Logging/ElasticJsonFormatter.cs | 103 ++++++++++++++++++ .../O2NextGen.Auth.Web.csproj | 10 ++ .../auth/O2NextGen.Auth.Web/Program.cs | 35 +++++- .../auth/O2NextGen.Auth.Web/appsettings.json | 22 +++- 4 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 src/Services/auth/O2NextGen.Auth.Web/Logging/ElasticJsonFormatter.cs diff --git a/src/Services/auth/O2NextGen.Auth.Web/Logging/ElasticJsonFormatter.cs b/src/Services/auth/O2NextGen.Auth.Web/Logging/ElasticJsonFormatter.cs new file mode 100644 index 00000000..4d149354 --- /dev/null +++ b/src/Services/auth/O2NextGen.Auth.Web/Logging/ElasticJsonFormatter.cs @@ -0,0 +1,103 @@ +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Serilog.Events; +using Serilog.Formatting; +using Serilog.Formatting.Json; + +namespace O2NextGen.Auth.Web.Logging +{ + public class ElasticJsonFormatter : ITextFormatter + { + private const string Prefix = "[ELASTIC]"; + + private readonly NamingStrategy namingStrategy; + private readonly JsonValueFormatter formatter; + private readonly string type; + + public ElasticJsonFormatter(NamingStrategy namingStrategy, JsonValueFormatter formatter, string type) + { + this.namingStrategy = namingStrategy; + this.formatter = formatter; + this.type = type; + } + + public ElasticJsonFormatter() + : this(new CamelCaseNamingStrategy(), new JsonValueFormatter("$type"), "commspoint") + { + } + + public void Format(LogEvent logEvent, TextWriter output) + { + output.Write(Prefix); + FormatBody(logEvent, output); + output.WriteLine(); + } + + private void FormatBody(LogEvent logEvent, TextWriter output) + { + // add json body + var writer = new JsonTextWriter(output); + writer.WriteStartObject(); + + // write level + writer.WritePropertyName("level"); + writer.WriteValue(FormatLogLevel(logEvent.Level)); + + // write type + writer.WritePropertyName("type"); + writer.WriteValue(type); + + // write timestamp + writer.WritePropertyName("timestamp"); + writer.WriteValue(logEvent.Timestamp.UtcDateTime); + + // write message + writer.WritePropertyName("message"); + writer.WriteValue(logEvent.RenderMessage()); + + // write exception if exists + if (logEvent.Exception != null) + { + writer.WritePropertyName("exception"); + writer.WriteValue(logEvent.Exception.ToString()); + } + + // write properties + writer.WritePropertyName("properties"); + writer.WriteStartObject(); + foreach (var property in logEvent.Properties) + { + var propertyName = namingStrategy.GetPropertyName(property.Key, false); + writer.WritePropertyName(propertyName); + + using (var stringWriter = new StringWriter()) + { + formatter.Format(property.Value, stringWriter); + writer.WriteRawValue(stringWriter.ToString()); + } + } + + writer.WriteEndObject(); + writer.Flush(); + } + + private static string FormatLogLevel(LogEventLevel level) + { + switch (level) + { + case LogEventLevel.Verbose: + return "TRACE"; + case LogEventLevel.Debug: + return "DEBUG"; + case LogEventLevel.Warning: + return "WARNING"; + case LogEventLevel.Error: + case LogEventLevel.Fatal: + return "ERROR"; + default: + return "INFO"; + } + } + } +} diff --git a/src/Services/auth/O2NextGen.Auth.Web/O2NextGen.Auth.Web.csproj b/src/Services/auth/O2NextGen.Auth.Web/O2NextGen.Auth.Web.csproj index b1577ee6..2456a634 100644 --- a/src/Services/auth/O2NextGen.Auth.Web/O2NextGen.Auth.Web.csproj +++ b/src/Services/auth/O2NextGen.Auth.Web/O2NextGen.Auth.Web.csproj @@ -8,6 +8,16 @@ + + + + + + + + + + diff --git a/src/Services/auth/O2NextGen.Auth.Web/Program.cs b/src/Services/auth/O2NextGen.Auth.Web/Program.cs index 0ccd3ea9..6b406198 100644 --- a/src/Services/auth/O2NextGen.Auth.Web/Program.cs +++ b/src/Services/auth/O2NextGen.Auth.Web/Program.cs @@ -1,17 +1,46 @@ -using Microsoft.AspNetCore; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Serilog; namespace O2NextGen.Auth.Web { public class Program { - public static void Main(string[] args) + public static readonly string Namespace = typeof(Program).Namespace; + + public static readonly string AppName = + Namespace.Substring(Namespace.LastIndexOf('.', Namespace.LastIndexOf('.') - 1) + 1); + + public static async Task Main(string[] args) { - CreateWebHostBuilder(args).Build().Run(); + try + { + var host = CreateWebHostBuilder(args).Build(); + Log.Information($"############### {AppName} ##############"); + Log.Information("################# Starting Application #################"); + await host.RunAsync(); + Log.Information($"============== {AppName} - state is started ====================="); + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) + .UseSerilog((context, configuration) => + { + configuration.ReadFrom.Configuration(context.Configuration); + }) .UseStartup(); } } diff --git a/src/Services/auth/O2NextGen.Auth.Web/appsettings.json b/src/Services/auth/O2NextGen.Auth.Web/appsettings.json index b7c4ed90..905fcee1 100644 --- a/src/Services/auth/O2NextGen.Auth.Web/appsettings.json +++ b/src/Services/auth/O2NextGen.Auth.Web/appsettings.json @@ -1,7 +1,27 @@ { + "Serilog": { + "Enrich": [ + "FromLogContext" + ], + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "Logs/log.txt", + "formatter": "O2NextGen.Auth.Web.Logging.ElasticJsonFormatter, O2NextGen.Auth.Web" + } + }, + { + "Name": "Console" + } + ] + }, "Logging": { + "IncludeScopes": false, "LogLevel": { - "Default": "Warning" + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*"