in Architettura Software

Serilog e write conditional

In un post precedente si è parlato della configurazione da utilizzare in Serilog per scrivere log all’interno di Microsoft SQL Server.

Nella configurazione è inoltre possibile definire una serie di regole che consentono di scrivere log in maniera condizionale, ad esempio all’interno di tabelle differenti in base al loro level.

Nello specifico, può essere utile creare una configurazione che consenta a Serilog di loggare all’interno di tabelle differenti (all’interno dello stesso db oppure nel cloud).

All’interno del Program.cs è possibile configurare la sezione relativa a Serilog utilizzando il codice seguente:

...
  
CreateLoggerConfiguration(builder);

builder.Services.AddLogging(c => c.AddSerilog(dispose: true));

...

rispettivamente per la creazione della configurazione del logger e la sua attivazione.

L’entry point consiste nella creazione della configurazione del logger che viene effettuata tramite:

static void CreateLoggerConfiguration(WebApplicationBuilder builder)
{
  
  var backgroundServiceName = new List<string>
	{
    	"SERVICE001",
    	"SERVICE002",
    	"SERVICE003",
    	"SERVICE004",
    	"SERVICE005"
	};
  
    Log.Logger = new LoggerConfiguration()
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return !backgroundServiceName.Contains(evtText.ToUpper(CultureInfo.InvariantCulture));
                }

                return true;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents", AutoCreateSqlTable = true, SchemaName = "log" }))
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return evtText.ToUpper(CultureInfo.InvariantCulture) == "DWH SERVICE";
                }

                return false;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "DwhService", AutoCreateSqlTable = true, SchemaName = "log" }))
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return evtText.ToUpper(CultureInfo.InvariantCulture) == "EMAIL SERVICE";
                }

                return false;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "EmailService", AutoCreateSqlTable = true, SchemaName = "log" }))
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return evtText.ToUpper(CultureInfo.InvariantCulture) == "EXCEL QUEUE SERVICE";
                }

                return false;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "ExcelQueue", AutoCreateSqlTable = true, SchemaName = "log" }))
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return evtText.ToUpper(CultureInfo.InvariantCulture) == "EXCEL TIMED SERVICE";
                }

                return false;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "ExcelTimed", AutoCreateSqlTable = true, SchemaName = "log" }))
    .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return evtText.ToUpper(CultureInfo.InvariantCulture) == "IMPORT EXTERNAL FILE";
                }

                return false;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "ImportExternalFile", AutoCreateSqlTable = true, SchemaName = "log" }))
    .CreateLogger();
}
Log.Logger = new LoggerConfiguration()

Possiamo quindi analizzare un singolo blocco condizionale, dal momento che tutti gli altri sono simili.

     .WriteTo.Conditional(
            ev =>
            {
                if (ev.Properties.ContainsKey("LoggerTitle"))
                {
                    var evtText = ev.Properties["LoggerTitle"].ToString().Replace("\"", string.Empty, StringComparison.InvariantCulture);
                    return !backgroundServiceName.Contains(evtText.ToUpper(CultureInfo.InvariantCulture));
                }

                return true;
            },
            wt => wt.MSSqlServer(
                connectionString: builder.Configuration.GetConnectionString("Log"),
                formatProvider: CultureInfo.InvariantCulture,
                sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents", AutoCreateSqlTable = true, SchemaName = "log" }))

Utilizzando WriteTo.Conditional viene attivata configurazione condizionale.

In particolare è possibile analizzare le proprietà dell’evento che stiamo considerando analizzandone le Properties. Una volta individuata quella che vogliamo utilizzare come filtro, possiamo procedere con la gestione.

Nell’esempio specifico è stata definita una lista con i nomi dei servizi che vogliamo loggare.

In particolare viene analizzata la presenza della proprietà LoggerTitle, all’interno del log che stiamo considerando.

Nel caso in cui sia presente viene ricercato il valore all’interno dei servizi che vogliamo loggare. Nel caso in cui sia presente, viene ritornato il valore true, altrimenti false.

Nella configurazione viene anche indicata la modalità con cui dovrà essere gestito il log, nell’esempio il log verrà salvato all’interno di sql server nella tabella log.LogEvents.

Vediamo, quindi, come viene attivata la scrittura del log:

var loggerTitle = "SERVICE001";
this.logger.Log(LogLevel.Information, "{LoggerTitle:l} - Starting", loggerTitle);