Is there a way to change the log level of certain events dynamically? (maybe by namespace or a predicate)
I'm looking for something like .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) but what I really want to do is change the level of Information events coming from the Microsoft namespace to Verbose. Events of higher importance should be left as-is.
EDIT:
Enrichers can't change the log event level. 

It's possible, but not entirely straightforward, so strap yourself in!
Instead of an enricher, you'll need to create a wrapper around the target sink. The wrapper will receive events from the logging pipeline, (fairly cheaply) create new events with identical properties, and forward them to the actual sink:
class LevelBoostingWrapper : ILogEventSink, IDisposable
{
    readonly ILogEventSink _wrappedSink;
    public LevelBoostingWrapper(ILogEventSink wrappedSink)
    {
        _wrappedSink = wrappedSink;
    }
    public void Emit(LogEvent logEvent)
    {
        if (logEvent.Level == LogEventLevel.Warning)
        {
            var boosted = new LogEvent(
                logEvent.Timestamp,
                LogEventLevel.Error, // <- the boost
                logEvent.Exception,
                logEvent.MessageTemplate,
                logEvent.Properties
                    .Select(kvp => new LogEventProperty(kvp.Key, kvp.Value)));
            _wrappedSink.Emit(boosted);
        }
        else
        {
            _wrappedSink.Emit(logEvent);
        }
    }
    public void Dispose()
    {
        (_wrappedSink as IDisposable)?.Dispose();
    }
}
The actual criterion for deciding which events to modify is up to you, of course.
This little extension makes it more pleasant to set up the wrapper:
static class LoggerSinkConfigurationExtensions
{
    public static LoggerConfiguration Boosted(
        this LoggerSinkConfiguration lsc,
        Action<LoggerSinkConfiguration> writeTo)
    {
        return LoggerSinkConfiguration.Wrap(
            lsc,
            wrapped => new LevelBoostingWrapper(wrapped),
            writeTo);
    }
}
Finally, in the logger configuration, apply the wrapper:
Log.Logger = new LoggerConfiguration()
    .WriteTo.Boosted(wt => wt.Console())
    .CreateLogger();
Log.Information("This will be unchanged");
Log.Warning("This will be boosted to Error");
       
Log.CloseAndFlush();
                        This sink wrapping is great for default sinks. However I would like to configure serilog using a config file, but also wrap the configured sinks to modify specific calls to a lower loglevel.
This is the configuration of my sinks in appsetting.json
{
"Serilog": {
    "MinimumLevel": {
        "Default": "Verbose",
        "Override": {
            "Microsoft": "Warning",
            "System": "Warning"
        }
    },
    "WriteTo": [
        {
            "Name": "Console",
            "Args": {
                "outputTemplate": "===> {Timestamp:HH:mm:ss.fff} [{Level}] {Message}{NewLine}{Exception}"
            }
        },
        {
            "Name": "RollingFile",
            "Args": {
                "pathFormat": "c:\path\file.txt",
                "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] - {Message}{NewLine}{Exception}"
            }
        },
        {
            "Name": "DurableHttpUsingTimeRolledBuffers",
            "Args": {
                "requestUri": "https://[elastic]",
                "bufferPathFormat": "c:\path\file.json"
            }
        }
Now what I do is create a logger from this config:
var configuration = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  .AddJsonFile("appsettings.json", false, true)
                  .Build();
var configLoggerConfig = new LoggerConfiguration().ReadFrom.Configuration(configuration);
var configLogger = configLoggerConfig.CreateLogger();
And then try to wrap it:
var wrappedLoggerconfig = new LoggerConfiguration().WriteTo.LogLevelModifyingSink(wt => wt.Sink(configLogger), modifiers, levelSwitch);
        Log.Logger = wrappedLoggerconfig.CreateLogger();
The modifiers is a class that contains the logic for a specific event to be modified. The LogModifyingSink extentionmethod looks lik this:
public static LoggerConfiguration LogLevelModifyingSink(
         this LoggerSinkConfiguration loggerConfiguration,
         Action<LoggerSinkConfiguration> writeTo,
         ILogLevelModifiers logLevelModifiers,
         LoggingLevelSwitch levelSwitch)
    {
        return LoggerSinkConfiguration.Wrap(
            loggerConfiguration,
            wrapped => new LogLevelModifyingSink(wrapped, logLevelModifiers),
            writeTo,
            LogEventLevel.Verbose,
            levelSwitch); 
    }
In which the LogLevelModifyingSink is the wrappersink that emits the modified log:
public class LogLevelModifyingSink : ILogEventSink, IDisposable
{
    readonly ILogEventSink wrappedSink;
    readonly IEnumerable<LogLevelModifier> logLevelModifiers;
    public LogLevelModifyingSink(ILogEventSink wrappedSink, ILogLevelModifiers logLevelModifiers)
    {
        this.wrappedSink = wrappedSink;
        this.logLevelModifiers = logLevelModifiers?.GetLevelModifiers();
    }
    public void Dispose()
    {
        (wrappedSink as IDisposable)?.Dispose();
    }
    public void Emit(LogEvent logEvent)
    {
        var message = logEvent.RenderMessage();
        Console.WriteLine(DateTimeOffset.Now.ToString() + " " + message);
        if (wrappedSink != null && logLevelModifiers != null && logLevelModifiers.Any())
        {
            foreach(var modifier in logLevelModifiers)
            {
                if (modifier.ShouldModify(logEvent))
                {
                    wrappedSink.Emit(modifier.ModifyEvent(logEvent));
                    return;
                }
            }
        }
        wrappedSink.Emit(logEvent);
    }
}
Now this works partly. The logmessages are handled by the wrapper for all 3 sinks, however, by creating a logger from a new configuration, the settings for minumumlevel and minimumlevel overrides are not conveyed from the config file and there seams no way to get these settings at runtime. I feel like this solution is not the way to go since I create a logger twice. So my question is, is there a better way to wrap sinks from configuration? Is it possible at all? And how can I preserve my configured settings?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With