1

I'm using Serilog to log to a SEQ server via a custom sink in my C# application. Many functionalities, including RPC server implementations, are derived from a company library, .Tools.

I'm encountering an issue where Serilog incorrectly identifies the SourceContext for log messages originating from an abstract base class.

The Problem:

I have an abstract class, ARpcServer, and an implementing class, RpcNcCreator. When Log.Information messages are emitted from within ARpcServer, Serilog's SourceContext property incorrectly shows the implementing class (RpcNcCreator) as the origin, instead of ARpcServer.

This is problematic because I want to suppress Information level logs specifically from ARpcServer (e.g., setting its minimum level to Warning), while still allowing Information logs from RpcNcCreator to be processed. If I try to suppress logs based on the RpcNcCreator namespace, I lose all Information level logs from that implementation, which is not desired.

Question:

Is there a way to configure Serilog, or modify the logging approach, so that it accurately captures the SourceContext based on the actual class where the Log.Information call is made (i.e., ARpcServer in the example above), rather than the concrete implementing class?

APP-Code:

namespace PPS.Kommunikation.Rpc
 public class RpcNcCreator : ARpcServer<TriggerUebergabeProdDaten, TriggerErgebnisNCDaten>
  {
    /// <summary>
    /// Liefert ein fertig konfiguriertes <see cref="RpcNcCreator"/>-Objekt.
    /// </summary>
    /// <param name="loggerSingleton">Log-Objekt.</param>
    /// <param name="iOptions">Options-Objekt.</param>
    public RpcNcCreator(ILogger<RpcNcCreator> loggerSingleton, IOptions<RpcNcCreatorOptions> iOptions)
      : base(loggerSingleton)
    {
      Debug.Assert(iOptions != null);
      Debug.Assert(iOptions.Value != null);

      this.AnfrageExchange = iOptions.Value.AnfrageExchange;
    }
//here are methodes that call eg.
private void DocumentStatus()
 => logger.LogInformation("...");
}

Library-Code:

namespace SpeedMaster.Tools.Kommunikation.RPC.Server;
public abstract class ARpcServer<TArbeit, TErgebnis> : ARpcServer<TErgebnis>, IDisposable
{

  public ARpcServer(ILogger<ARpcServer<TArbeit, TErgebnis>> loggerSingleton)
  {
    _logger = loggerSingleton;
  }
    //Here in the BaseClass also are logs made eg.
    private Task SendeStatusAnfrage(bool nurWennArbeitDaIst)
  {
    this._logger.LogInformation("Sende Status-Anfrage an alle Clients.");
}

AppSettings.json

//Sample from the 
"Serilog": {
    "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.Seq", "SpeedMaster.Tools", "PPS" ],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Error",
        "Microsoft.AspNetCore": "Warning",
        "System.Net.Http.HttpClient": "Warning",
        "SpeedMaster.Tools.Kommunikation.RPC.Server.Internal.ClientStatusChecker": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "Seq",
        "Args": {
          "restrictedToMinimumLevel": "Information",
          "serverUrl": "https://at02-logsammler.speed.intern:45341",
          "apiKey": null
        }
      },
      {
        "Name": "Console",
        "Args": {
          "theme": "PPS.Models.SerilogThemes.SpeedMasterThemes::Speedy, PPS",
          "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] ({SourceContext:l}) {Message:lj}{NewLine}{Exception}"
        }
      }
    ]
  },

enter image description here

Update for Panagiotis Kanavos:
I extendet the code with the constructor, what a college explaint to me that the logger from RpcNcCreator is given to the Base.

The differenz from logger and _logger lies in that ARpcServer uses a Tradtional constructor with field assignment while the RpcNcCreator uses a primary constructor where its not recommendet to use a '_' as prefix for the variable name.

5
  • 1
    Serilog works just fine. If the logger is created inside RpcNcCreator, the context is RpcNcCreator. If you want to use a different context use .ForContext<ARpcServer>(). This is shown in the docs as well. How are the loggers created? How are logger and _logger related? Commented May 22 at 7:56
  • 1
    Please post the code that creates the loggers and initializes the fields. If ARpcServer._logger is initialized from RpcNcCreator.logger in the constructor, you could use _logger=logger.ForContext<ARpcServer>(); Commented May 22 at 7:59
  • @PanagiotisKanavos i extended the sample code, but logger.ForContext<ARpcServer>() i dont know how i should yous the only ForContext i found is Serilog.Log.ForContext what creates a Serilog instance but i work with ILogger<> instances Commented May 23 at 4:09
  • ILogger is a Serilog interface. Unless you mean the Microsoft.Extensions.Logging interface, in which case what you ask has little if anything to do with Serilog. Commented May 23 at 7:08
  • I understand so far as that Serilog uses the MS ILogging interface, the Serilog part in my question bases on that i need to restrict certain namespaces via the Appsettings part of Serilog. Iam still a junior dev so i might mistake here some stuff Commented May 23 at 8:39

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.