Serilog, Seq with Hangfire.Console


#1

I’m using Serilog for writing to disk and Seq.

I’d like to log to both disk, seq and console simultaneously using a single line of code. E.g.
Log.Information("This is some task related query");
As suppose to:
Log.Information("This is some task related message"); context.WriteLine("This is some task related message");

I followed a guide on how to extend Serilog sink to include Hangfire.Console logs too (This article here https://www.dennis-s.dk/2019-07/hangfire-console-serilog-sink). However, when Seq tries to serialise the PerformContext property, it throws an error.

could not be formatted into JSON for Seq and will be dropped: System.NotSupportedException: The value x is not of a type supported by this visitor.

Anyone had any experience in getting this behaviour? Or would you just separate the two? Thanks.


#2

Hi @Spinks90

You can solve this issue by using scalar, structured, dictionary property value

internal class PerformContextValue : LogEventPropertyValue

Replace LogEventProperyValue with ScalarValue

The full class would be:

```
internal class PerformContextValue : ScalarValue
{
    public PerformContextValue(object value) : base(value)
    {
    }

    // The context attached to this property value
    public PerformContext PerformContext { get; set; }
    /// <inheritdoc />
    public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
    {
        // How the value will be rendered in Json output, etc.
        // Not important for the function of this code..
        output.Write(PerformContext.BackgroundJob.Id);
    }
}
```

Why?

Serilog is using structured data toolkit to serialize objects.
PerformContextValue is unknown for serilog, so you need to let serilog know it is a scalar value

Reference: https://gist.github.com/nblumhardt/bd74fafc61d0c50ec07e0f3715df0d00#file-serilogstructureddatatoolkit-cs


#3

Then you can send null in the constructor

public class HangfireConsoleSerilogEnricher : ILogEventEnricher
{
    // The context used to enrich log events
    public PerformContext PerformContext { get; set; }
    /// <inheritdoc />
    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
        var prop = new LogEventProperty("PerformContext",  new PerformContextValue(null) { PrformContext = PerformContext} );
        logEvent.AddOrUpdateProperty(prop);
    }
}

#4

Awesome, that works a treat. Thanks! :+1: