Unable to fetch Serilog > SourceContext property value using code. SourceContext property is not getting generated against Properties field in log table. Trying to fetch SourceContext value of every method i.e., api, service, repository by using a common method globally instead of fetching SourceContext value in every method.
Using the below lines of code
appsetting.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "WithOpenTracingContext", "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId", "WithCorrelationId" ],
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "connection string",
"autoCreateSqlTable ": true,
"schemaName": "dbo",
"tableName": "LogTable",
"restrictedToMinimumLevel": "Information",
"batchPostingLimit": 50,
"period": "0.00:00:30",
"columnOptionsSection": {
"removeStandardColumns": [ "LogEvent", "MessageTemplate" ],
"customColumns": [
{
"ColumnName": "ClientIp",
"DataType": "VARCHAR",
"AllowNull": true,
"DataLength": 50,
"NonClusteredIndex": true
},
{
"ColumnName": "UserId",
"DataType": "INT",
"AllowNull": true
},
{
"ColumnName": "SessionId",
"DataType": "VARCHAR",
"AllowNull": true,
"DataLength": 50,
"NonClusteredIndex": true
}
]
}
}
}
],
"Using": [ "Serilog.Settings.Configuration" ]
}
configuration.cs
public static IConfigurationRoot ConfigureSqlLogger(this IConfigurationRoot _configuration)
{
LoggerConfiguration config =
new LoggerConfiguration()
.Enrich.WithClientIp()
.Enrich.WithClientAgent()
.ReadFrom.Configuration(_configuration);
Log.Logger = config.CreateLogger();
return _configuration;
}
Startup.cs
public Startup(IWebHostEnvironment environment)
{
try
{
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Configuration.ConfigureSqlLogger();
}
catch (Exception ex)
{
throw ex;
}
}
Related
logger:
ILogger logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration, "Logging")
.Enrich.FromLogContext()
.CreateLogger();
settings:
"Logging": {
"Using": [ "Serilog.Exceptions", "Serilog", "Serilog.Sinks.File"],
"MinimumLevel": {
"Default": "Information",
"Override": {
"System": "Information",
"Microsoft": "Information",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "./logs/events.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"buffered": true,
"outputTemplate": "{Timestamp:u};{host_ip};{enviroment_hostname};{EventId};{EventCategory};{EventSeverity};{Message:lj};{UserId};{ResourceType};{ResourceId}",
"restrictedToMinimumLevel": "Information"
}
}
],
"Enrich": [ "WithExceptionDetails", "WithProcId", "WithFacility", "ProcessHostEnricher", "WithSeverity" ]
}
In the file I get the following output:
2023-01-13 15:43:25Z;;;{ Id: 101 };UserRolesUnAssigned;UserManagement;Medium;User:system roles un-assigned:role1,role2,role3;system;;
{EventId} comes out as a json object.
I would need just the Id. so 101 instead of { Id: 101 }
EDIT:
LoggerMessageDefinition:
private static Action<ILogger, string, string, Exception?> UserRolesUnAssigned =>
LoggerMessage.Define<string, string>(LogLevel.Information, 101, UserMessageCodes[101].Template);
public static void LogUserRolesUnAssigned(this ILogger logger, string userId, string roleNames)
{
EventDefinition eventDefinition = UserMessageCodes[101];
using (LogContext.PushProperty(EventConstants.EventName, eventDefinition.Name))
using (LogContext.PushProperty(EventConstants.EventCategory, eventDefinition.Category))
using (LogContext.PushProperty(EventConstants.EventSeverity, eventDefinition.Severity))
UserRolesUnAssigned(logger,userId, roleNames, null);
}
Is there a way to do this out of the box or do I need to create a custom enricher for this?
I have the following settings defined, but I am still getting all put/get requests logged to my api, even though these are supposed to be "Microsoft".
Can anyone share advice?
"Serilog": {
"Using": [ "Serilog.Sinks.Console" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{
"Name": "Seq",
"Args": { "serverUrl": "http://logserver:5341" }
},
{
"Name": "File",
"Args": {
"path": "c:\\temp\\events.api.txt",
"rollingInterval": "Day"
}
}
],
"Properties": {
"Application": "Events API"
}
},
Example of a log:
[13:55:10 INF] Source:[::1] Request: PUT https localhost:44320/api
{json request object} Responded with [200] in 2ms
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseSerilog((hostingContext, loggerConfiguration) => loggerConfiguration
.ReadFrom.Configuration(hostingContext.Configuration)
.Enrich.FromLogContext());
});
Since I updated my code to .NET 5.0 and used a newer version of serilog, I cannot get any rolling file anymore
I tried debugging serilog , it creates a debug file but nothing is in there
private static void CreateLogger()
{
// Init Serilog configuration
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.logs.json")
.Build();
Log.Logger = new Serilog.LoggerConfiguration().ReadFrom.Configuration(configuration)
.Enrich.WithProperty("Application", GetAssemblyProductName())
.CreateLogger();
var file = File.CreateText(AppDomain.CurrentDomain.BaseDirectory + #"serilog.txt");
Serilog.Debugging.SelfLog.Enable(TextWriter.Synchronized(file));
Serilog.Debugging.SelfLog.Enable(msg => Debug.WriteLine(msg));
Serilog.Debugging.SelfLog.Enable(Console.Error);
}
here is my appsetings.log.json
{
"Serilog": {
"Using": [
"Serilog.Sinks.ApplicationInsights"
],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"System.Net.Http.HttpClient": "Warning"
}
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"pathFormat": "logs.txt",
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
"outputTemplate": "({SourceContext}) {Message}{NewLine}{Exception}",
"rollingInterval": "Day",
"fileSizeLimitBytes": 50000000,
"rollOnFileSizeLimit": true,
"retainedFileCountLimit": 15,
"buffered": true
}
},
{
"Name": "ApplicationInsights",
"Args": {
"restrictedToMinimumLevel": "Information",
"outputTemplate": "({SourceContext}) ({Application}/{MachineName}/{ThreadId}) {Message}{NewLine}{Exception}",
"telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
}
}
]
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "myapp"
}
}
}
am I missing something ?
thanks for helping me on this
Please someone correct me if this is wrong but I assumed you need to list the sinks you wanted to run in the Using section - therefore adding the File sink to your appsettings should fix this - as per below:
"Serilog": {
"Using": [
"Serilog.Sinks.ApplicationInsights","Serilog.Sinks.File"
],
I have configured Serilog to have a default MinimumLevel of Verbose.
But I want to confine the logging to SQL Server to Warning (and up).
My config (in JSON) looks like this:
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.MSSqlServer", "My.Framework.AspNet" ],
"Enrich": [ "FromLogContext", "WithEventType" ],
"MinimumLevel": {
"Default": "Verbose"
},
"WriteTo": [
{
"Name": "MSSqlServer",
"restrictedToMinimumLevel": "Warning",
"Args": {
"connectionString": "Server=yada yada yada ;MultipleActiveResultSets=True",
"tableName": "ErrorLogs",
"autoCreateSqlTable": false,
"columnOptionsSection": {
"disableTriggers": true,
"clusteredColumnstoreIndex": false,
"primaryKeyColumnName": "Id",
"addStandardColumns": [ "LogEvent" ],
"removeStandardColumns": [ "Properties" ],
"additionalColumns": [
{
"ColumnName": "EventType",
"DataType": "bigint",
"AllowNull": true
}
],
"id": {
"columnName": "Id",
"nonClusteredIndex": true
},
"eventType": {
"columnName": "EventType"
},
"message": {
"columnName": "Message"
},
"messageTemplate": {
"columnName": "MessageTemplate"
},
"level": {
"columnName": "Level",
"storeAsEnum": false
},
"timeStamp": {
"columnName": "TimeStamp",
"convertToUtc": true
},
"exception": {
"columnName": "Exception"
},
"logEvent": {
"columnName": "LogEvent"
}
}
}
}
],
"Properties": {
"Application": "App API"
}
}
}
Upon inspecting my logs, I'm seeing everything from Verbose up.
Is there something I'm doing wrong with the restrictedToMinimumLevel property?
I also tried placing it inside the Args property, with no luck.
Cheers
I haven't tried it but upon this GitHub issue I think restrictedToMinimumLevel should be in Args. So change your WriteTo configuration like this:
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"restrictedToMinimumLevel": "Warning",
"connectionString": "Server=yada yada yada ;MultipleActiveResultSets=True",
"tableName": "ErrorLogs",
"autoCreateSqlTable": false,
"columnOptionsSection": {
"disableTriggers": true,
"clusteredColumnstoreIndex": false,
"primaryKeyColumnName": "Id",
"addStandardColumns": [ "LogEvent" ],
"removeStandardColumns": [ "Properties" ],
"additionalColumns": [
{
"ColumnName": "EventType",
"DataType": "bigint",
"AllowNull": true
}
],
"id": {
"columnName": "Id",
"nonClusteredIndex": true
},
"eventType": {
"columnName": "EventType"
},
"message": {
"columnName": "Message"
},
"messageTemplate": {
"columnName": "MessageTemplate"
},
"level": {
"columnName": "Level",
"storeAsEnum": false
},
"timeStamp": {
"columnName": "TimeStamp",
"convertToUtc": true
},
"exception": {
"columnName": "Exception"
},
"logEvent": {
"columnName": "LogEvent"
}
}
}
}
]
For the restrictedToMinimumLevel to work via configuration through appSettings.json, the restrictedToMinimumLevel must be inside the Args section as it's described in the documentation. There's nothing unique about the MsSQLServer sink that would require the configuration to be any different.
As you can see, only events with log level >= Warning are stored in the SQL Server table:
Program.cs
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Debugging;
namespace ReproSerilogSO66019586
{
class Program
{
static void Main(string[] args)
{
SelfLog.Enable(msg => Console.WriteLine("ERROR: {0}", msg));
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.WriteTo.Console()
.CreateLogger();
Log.Verbose("This is Verbose!");
Log.Debug("This is Debug!");
Log.Information("This is Information!");
Log.Warning("This is Warning!");
Log.Error("This is Error!");
Log.Fatal("This is Fatal!");
Log.CloseAndFlush();
Console.ReadLine();
}
}
}
appSettings.json
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.MSSqlServer" ],
"Enrich": [ "FromLogContext" ],
"MinimumLevel": {
"Default": "Verbose"
},
"WriteTo": [
{
"Name": "MSSqlServer",
"Args": {
"restrictedToMinimumLevel": "Warning",
"connectionString": "Server=(local);MultipleActiveResultSets=True",
"tableName": "ErrorLogs",
"autoCreateSqlTable": true,
"columnOptionsSection": {
"disableTriggers": true,
"clusteredColumnstoreIndex": false,
"primaryKeyColumnName": "Id",
"addStandardColumns": [ "LogEvent" ],
"removeStandardColumns": [ "Properties" ],
"additionalColumns": [
{
"ColumnName": "EventType",
"DataType": "bigint",
"AllowNull": true
}
],
"id": {
"columnName": "Id",
"nonClusteredIndex": true
},
"message": {
"columnName": "Message"
},
"messageTemplate": {
"columnName": "MessageTemplate"
},
"level": {
"columnName": "Level",
"storeAsEnum": false
},
"timeStamp": {
"columnName": "TimeStamp",
"convertToUtc": true
},
"exception": {
"columnName": "Exception"
},
"logEvent": {
"columnName": "LogEvent"
}
}
}
}
],
"Properties": {
"Application": "App API"
}
}
}
I'm having trouble getting the EventType feature working in Serilog, as blogged about here.
I am using the following Nuget packages:
Serilog 2.8
Serilog.Settings.Configuration 3.0.1
Serilog.Sinks.File 4.0.0
Serilog.Sinks.MSSqlServer 5.1.2
First up, I created an EventTypeEnricher:
public class EventTypeEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var crypto = new SimpleCrypto.PBKDF2();
var hash = crypto.Compute(logEvent.MessageTemplate.Text);
var numericHash = BitConverter.ToUInt32(Encoding.UTF8.GetBytes(hash), 0);
var eventId = propertyFactory.CreateProperty("EventType", numericHash);
logEvent.AddPropertyIfAbsent(eventId);
}
}
This seems to work (more on that later, but at the end of that method, a property is added with an EventType value in the EventId variable can be observed while debugging).
I created an extension method which adds this enricher:
public static LoggerConfiguration WithEventType(this LoggerEnrichmentConfiguration enrichmentConfiguration)
{
if (enrichmentConfiguration == null) throw new ArgumentNullException(nameof(enrichmentConfiguration));
return enrichmentConfiguration.With<EventTypeEnricher>();
}
I then use that when I configure the Logger:
Log.Logger = new LoggerConfiguration()
.Enrich.WithEventType()
.ReadFrom.Configuration(configuration)
.CreateLogger();
I go to write the error like this:
logger.Write(LogEventLevel.Error,
contextFeature.Error,
MessageTemplates.LogEntryDetailMessageTemplate,
new LogEntryDetail
{
Exception = contextFeature.Error,
Message = "Bad Stuff",
Timestamp = DateTime.UtcNow,
MessageTemplate = MessageTemplates.LogEntryDetailMessageTemplate,
Severity = LogEventLevel.Error
});
My Serilog appsettings section is as follows:
"Serilog": {
"Using": [ "Serilog.Sinks.File", "Serilog.Sinks.MSSqlServer", "MyAssembly" ],
"Enrich": [ "EventTypeEnricher" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs//Errorlog.log",
"fileSizeLimitBytes": 1073741824,
"retainedFileCountLimit": 30,
"rollingInterval": "Day",
"rollOnFileSizeLimit": true
},
"restrictedToMinimumLevel": "Verbose"
},
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "Data Source=(local);Initial Catalog=ADb;User Id=Serilog;Password=securepwd;",
"tableName": "ErrorLogs",
"autoCreateSqlTable": false,
"period": 30,
"columnOptionsSection": {
"disableTriggers": true,
"clusteredColumnstoreIndex": false,
"primaryKeyColumnName": "Id",
"addStandardColumns": [ "LogEvent" ],
"removeStandardColumns": [ "Properties" ],
"additionalColumns": [
{
"ColumnName": "EventType",
"DataType": "int",
"AllowNull": true
}
],
"id": { "nonClusteredIndex": true },
"level": {
"columnName": "Level",
"storeAsEnum": false
},
"timeStamp": {
"columnName": "Timestamp",
"convertToUtc": true
},
"logEvent": {
"excludeAdditionalProperties": true,
"excludeStandardColumns": true
},
"message": { "columnName": "Message" },
"exception": { "columnName": "Exception" },
"messageTemplate": { "columnName": "MessageTemplate" }
}
},
"restrictedToMinimumLevel": "Verbose"
}
]
}
My database table looks like this:
CREATE TABLE [dbo].[ErrorLogs](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[EventType] [int] NULL,
[Message] [nvarchar](max) NULL,
[MessageTemplate] [nvarchar](max) NULL,
[Level] [nvarchar](128) NULL,
[TimeStamp] [datetime] NOT NULL,
[Exception] [nvarchar](max) NULL,
[Properties] [nvarchar](max) NULL,
[LogEvent] [nvarchar](max) NULL,
CONSTRAINT [PK_ErrorLogs] PRIMARY KEY NONCLUSTERED
The EventType column in the database is always null, despite the code in the custom enricher running.
It is not written to the file sink either.
Can anyone see what I am doing wrong or missing?
Cheers
Updating to Serilog.Sinks.MSSqlServer version 5.1.3 fixed the issue as current stable version 5.1.2 not reading all columnOptionsSection section
Install-Package Serilog.Sinks.MSSqlServer -Version 5.1.3
And below updated configuration will fix your issue as you miss table mapping for EventType field
"Serilog": {
"Using": [ "Serilog.Sinks.File", "Serilog.Sinks.MSSqlServer", "MyAssembly" ],
"Enrich": [ "WithEventType" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs//Errorlog.log",
"fileSizeLimitBytes": 1073741824,
"retainedFileCountLimit": 30,
"rollingInterval": "Day",
"rollOnFileSizeLimit": true
},
"restrictedToMinimumLevel": "Verbose"
},
{
"Name": "MSSqlServer",
"Args": {
"connectionString": "Data Source=(local);Initial Catalog=ADb;User Id=Serilog;Password=securepwd;",
"tableName": "ErrorLogs",
"autoCreateSqlTable": false,
"columnOptionsSection": {
"disableTriggers": true,
"clusteredColumnstoreIndex": false,
"primaryKeyColumnName": "Id",
"addStandardColumns": [ "LogEvent" ],
"additionalColumns": [
{
"ColumnName": "EventType",
"DataType": "int",
"AllowNull": true
}
],
"id": {
"columnName": "Id",
"nonClusteredIndex": true
},
"eventType": {
"columnName": "EventType"
},
"message": {
"columnName": "Message"
},
"messageTemplate": {
"columnName": "MessageTemplate"
},
"level": {
"columnName": "Level",
"storeAsEnum": false
},
"timeStamp": {
"columnName": "TimeStamp",
"convertToUtc": true
},
"exception": {
"columnName": "Exception"
},
"properties": {
"columnName": "Properties"
},
"logEvent": {
"columnName": "LogEvent"
}
}
}
}
]
}
And Logger configuration as below
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();