Search code examples
azureasp.net-coreserilogseq-logginggetseq

Serilog Seq Sink not always capturing events


I'm running Seq on an Azure instance (Windows Server 2012 R2 Datacenter) and logging with Serilog from a console application running on my local workstation. I have 3 sinks configured - File, Console and Seq. I'm also running on dnxcore50 (just in case you were thinking my setup wasn't dodgy enough). All my events are showing up in console and the file 100% of the time. Seq is only capturing event about 1 in 5 or more runs, that is it will either capture all the events for the run or none of them. I am using the free single user license to evaluate Seq, and I haven't found anything to suggest there are any limitations that would cause this behavior.

I've set up SelfLog.Out on the loggers, but that logs nothing at all, other than my test line which I added to make sure the self logging could at least write to the specified file.

public abstract class SerilogBaseLogger<T> : SerilogTarget
{
    protected SerilogBaseLogger(SerilogOptions options, string loggerType)
    {
        LoggerConfiguration = new LoggerConfiguration();
        Options = options;
        LevelSwitch.MinimumLevel = options.LogLevel.ToSerilogLevel();

        var file = File.CreateText(Path.Combine(options.LogFilePath, string.Format("SelfLog - {0}.txt", loggerType)));
        file.AutoFlush = true;
        SelfLog.Out = file;
        SelfLog.WriteLine("Testing self log.");
    }

    // ... snip ....
}

public class SerilogSeqTarget<T> : SerilogBaseLogger<T>
{
    public string Server => Options.Seq.Server;

    public SerilogSeqTarget(SerilogOptions options)
        : base(options, string.Format("SerilogSeqTarget[{0}]", typeof(T)))
    {
        LoggerConfiguration
            .WriteTo
            .Seq(Server);

        InitializeLogger();
    }

    public override string ToString()
    {
        return string.Format("SerilogSeqTarget<{0}>", typeof(T).Name);
    }
}

public class SerilogLoggerFactory<TType> : LoggerFactory<SerilogTarget>
{
    // .... snip ....

    public override IMyLoggerInterface GetLogger()
    {
        var myLogger = new SerilogDefaultLogger()
            .AddTarget(SerilogTargetFactory<TType>.GetFileTarget(Options))
            .AddTarget(SerilogTargetFactory<TType>.GetConsoleTarget(Options))
            .AddTarget(SerilogTargetFactory<TType>.GetSeqTarget(Options));

        myLogger.Info("Logger initialized with {@options} and targets: {targets}", Options, ((SerilogDefaultLogger)myLogger).Targets);

        return myLogger;
    }
}

public class SerilogTargetFactory<TType>
{
    // ... snip ...

    public static SerilogTarget GetSeqTarget(SerilogOptions options)
    {
        return !string.IsNullOrEmpty(options.Seq.Server)
                   ? new SerilogSeqTarget<TType>(options)
                   : null;
    }
}

Any suggestions? Is this just a side-effect of being on the bleeding edge, working with pre-release everything (although in that case I'd expect things to fail somewhat consistently)?


Solution

  • when targeting dnxcore50/CoreCLR, Serilog can't hook into AppDomain shutdown to guarantee that any buffered messages are always flushed. (AppDomain doesn't exist in that profile :-)

    There are a couple of options:

    1. Dispose the loggers (especially the Seq one) on shutdown:

      (logger as IDisposable)?.Dispose();

    2. Use the static Log class and call its CloseAndFlush() method on shutdown:

      Log.CloseAndFlush();

    The latter - using the static Log class instead of the various individual ILogger instances - is probably the quickest and easiest to get going with, but it has exactly the same effect as disposing the loggers so either approach should do it.