0

I'm new to .NET and to webservice development, so i'm not exactly sure how to implement the requirement i have.

  • My webservice gets a POST request with some data, which i need to process to generate a pdf file: name_YYYYMMDDHHmmss.pdf.
  • For monitoring this i want to have a separate logfile for each request, named like the output file: name_YYYYMMDDHHmmss.log
  • I would like to avoid passing a config object into every class/function in which i need to add stuff to the log file

I've managed to install Serilog and it works for what i need, but not when i get concurrent requests. I'm also not exactly sure how simultaneous requests are handled in .NET (i have no thread specific code written so far), but as far as i can tell, when i change Global Logger file name, that object is shared across all threads so all of them write to the same file.

I've looked at a bunch of solutions, but i haven't managed to find nothing that suits this, and it seems most people have everything into 1 file...

Is there any clue or tips you can give me? I'm open to using something other than Serilog.

  • 1
    Seems like you could use a custom sink like [this one](https://github.com/sschutten/serilog-sinks-contextrollingfile) and [push a context property](https://github.com/serilog/serilog/wiki/Enrichment) when you go to create a particular PDF. – StriplingWarrior Jun 30 '21 at 16:41
  • Any updates on this? – Tom Jan 21 '22 at 21:29
  • @tom I'm afraid not. I've researched this further and the issue seems to be related to how global varibles are shared in each thread. In next version of my app the plan is to include a reference to a parent object whenever a new object is created and have the child object ask the parent to generate a new log. This way it should be possible to separate the logs per request. – P.Mendes Jan 24 '22 at 11:10

2 Answers2

0

One way to have dynamic file names based on a specific context is by using the Serilog.Sinks.Map and then, via a middleware in the request pipeline, you can add a property to the log context that drives the file name to be used when writing to the log.

Examples of similar usage of Serilog.Sinks.Map to decide which file name to use at run-time:

C. Augusto Proiete
  • 24,684
  • 2
  • 63
  • 91
  • I've looked at that but i'm struggling to see how to do it with Sinks.Map. I would still need to pass the file name in each Log call i make, and that's what i'm trying to avoid. I would basically need to generate the file name for each request at the start of the processing code, and pass that through every function call to be able to use it. Am I misunderstanding this? Or is there a way to have some global varible not shared with other threads? – P.Mendes Jul 01 '21 at 09:21
0

The best solution that I found to this problem was using Serilog.Sinks.Map. I configured my Logger something like this:

Log.Logger = new LoggerConfiguration()
    .WriteTo.Map("Name", "Default", (name, wt) => {
        var fileName = name == "Default" ? "log" : $"{log-{name}}" 

        wt.File($"./{fileName}-.txt");
    }).CreateLogger();

Then on my controller, on each method where I needed this feature, I enclosed all the instructions inside a LongContext like this:

[HttpGet]
public IHttpActionResult Get() {
   using (LogContext.PushProperty("Name", "theFileName") {
       // ...
       _myService.Method1();
       // ...
   }
}
public class MyService : IMyService {
   // ...

   public void Method1() {
      // ...
      Log.Information("This is what happened at this point…");
      // ...
   }
   // ...
}

So all the Log's inside will use that context and it will write on a different file with the name you set for that context without having to modify any Log.Information/Error/Warning/etc that you already have on your code. This is the ugly part... you have to define a context on a root place in order to make those Logs write on a different file. So for a controller method, the first thing you have to do is to enclose all with a LogContext.

Tom
  • 328
  • 3
  • 12
  • Thanks for the reply. My main difficulty was with "theFileName". I would need to pass it as input into every method/object that needs to log. Or am i missing something? – P.Mendes Jan 27 '22 at 09:40
  • No you don’t need to pass anything more, if you set a context for those logs. I updated the answer, check it out – Tom Jan 27 '22 at 22:15