Serilog - Multiple Log Files

Serilog - multiple log files

I think you need:

.ByIncludingOnly(evt => evt.Level == LogEventLevel.Warning)

Edit:

In many cases it's now more succinct to use Serilog.Sinks.Map. With it, the example can be written as:

Log.Logger = new LoggerConfiuration()
.WriteTo.Map(
evt => evt.Level,
(level, wt) => wt.RollingFile("Logs\\" + level + "-{Date}.log"))
.CreateLogger();

Serilog : Log to different files

You can do this by first making sure the performance counter events are tagged with either a particular property value (OpenMappedContext() in LibLog) or from a particular type/namespace.

var log = LogProvider.For<MyApp.Performance.SomeCounter>()
log.Info(...);

When configuring Serilog, a sub-logger with filter applied can send just the required events to the second file.

Log.Logger = new LoggerConfiguration()
.WriteTo.Logger(lc => lc
.Filter.ByExcluding(Matching.FromSource("MyApp.Performance"))
.WriteTo.File("first.json", new JsonFormatter()))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(Matching.FromSource("MyApp.Performance"))
.WriteTo.File("second.json", new JsonFormatter()))
.CreateLogger();

Parallel logging in multiple files using Serilog

Solution

I came to this way but I don't really like it.

First, configure static logger:

Log.Logger = new LoggerConfiguration()
.WriteTo.Map("Username", "log", (fileName, wt) => wt.File($"logs/{fileName}-.txt", rollingInterval: RollingInterval.Day), restrictedToMinimumLevel: LogEventLevel.Debug)
.CreateLogger();

I used Serilog.Sinks.Map for this, which I use to attach property key trigger "Username" for log files name.

I created my own Logger class for easier property attaching and for the new log template:

internal class Logger
{
public ILogger Log;
public Logger(User user)
{
User = user;
Log = Serilog.Log.ForContext("Username", user.Username);
}

public User User { get; set; }

public void Information(string message)
{
Log.Information(string.IsNullOrEmpty(User.Username) ? message : $"{{Username}} | {message}", User.Username);
}
}

And the usage:

new Logger(User).Information("The most stupid solution but it works honestly.");

Yes, I have to create new Logger() each time I want to log something with a specific user.

Hope someone can show me better way.

Can I log to separate files using Serilog?

You can definitely do that.

  1. You need to import package Serilog.Sinks.File

  2. Then you have to configure Serilog.

    In program.cs do following thing.

    Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.File(
    @"<<your log file path>>",
    fileSizeLimitBytes: 10000000,
    rollOnFileSizeLimit: true,
    shared: true,
    flushToDiskInterval: TimeSpan.FromSeconds(1))
    .CreateLogger();

In buildWebHost function add UseSerilog().

public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog() // <-- Add this line
.Build();

Update 1

I have used EventId property. This is just demo that how you can use different file based on eventId but for your requirement you have to implement additional thing your own.

Program.cs

public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Logger(cc => cc.Filter.ByIncludingOnly(WithProperty("EventId",1001)).WriteTo.File("Test1001.txt",flushToDiskInterval: TimeSpan.FromSeconds(1)))
.WriteTo.Logger(cc => cc.Filter.ByIncludingOnly(WithProperty("EventId", 2001)).WriteTo.File("Test2001.txt", flushToDiskInterval: TimeSpan.FromSeconds(1)))
.CreateLogger();

CreateWebHostBuilder(args).Build().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseSerilog()
.UseStartup<Startup>();

public static Func<LogEvent, bool> WithProperty(string propertyName, object scalarValue)
{
if (propertyName == null) throw new ArgumentNullException("propertyName");
ScalarValue scalar = new ScalarValue(scalarValue);
return e=>
{
LogEventPropertyValue propertyValue;
if (e.Properties.TryGetValue(propertyName, out propertyValue))
{
var stValue = propertyValue as StructureValue;
if (stValue != null)
{
var value = stValue.Properties.Where(cc => cc.Name == "Id").FirstOrDefault();
bool result = scalar.Equals(value.Value);
return result;
}
}
return false;
};
}
}

My HomeController.cs

  public class HomeController : Controller
{
ILogger<HomeController> logger;
public HomeController(ILogger<HomeController> logger)
{
this.logger = logger;
}
public IActionResult Index()
{
logger.Log(LogLevel.Information,new EventId(1001), "This is test 1");
logger.Log(LogLevel.Information, new EventId(2001), "This is test 2");
return View();
}
}

Note: Main thing is that you have to use some type of filter.

Serilog - can not log to multiple files based on property

You misunderstood what the second parameter of Map is. It's not a filter... It's just a default value for your keyPropertyName, in case it's not present in the log event.

The decision to select the sink based on the value of the type property has to be done by you in the body of the Map configuration.

e.g.

Log.Logger = new LoggerConfiguration()
.WriteTo.Map("type", string.Empty, (type, wt) =>
{
if (type.Equals("audit"))
{
wt.File(auditLogPath);
}
else if (type.Equals("normal"))
{
wt.File(logPath)
.WriteTo.Console();
}
})
.Enrich.FromLogContext()
.CreateLogger();

Also notice that without enabling enrichment via LogContext the properties you're pushing wouldn't be visible to Map, so you need the .Enrich.FromLogContext() above.

Serilog clobbering over multiple logs?

It seems that the two apps/scripts that you are executing with ScriptHookVDotNET are sharing the same static context where you're storing the logger instance (Log.Logger), so the last script that runs changes the instance and affects the first script.

ScriptHookVDotNET doesn't seem provide the isolation you'd expect in a regular .NET app, thus I'd suggest asking on their Gitter chat if this by design and what would be the recommended "ScriptHookVDotNET way" of creating an isolated static context.

One possible workaround is to store your logger instance as an instance variable that belongs to a specific script, which can be shared with the instance methods of the script. e.g.

public class Main : Script
{
private ILogger _log = Logger.None;

public Main()
{
_log = new LoggerConfiguration()
.MinimumLevel.Debug()
.Filter.ByExcluding(e => e.MessageTemplate.Text.Contains("Could not find file"))
.WriteTo.File("testA-.log", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 1)
.CreateLogger();

KeyDown += OnKeyDown;
Interval = 1000;
}

private void OnKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.T)
{
_log.Information("Create AI Script and store as AIone");
// ...
}

// ...
}
}


Related Topics



Leave a reply



Submit