Reputation: 3439
I am using Serilog with serilog-sinks-console in my C# project and I am wondering how can I modify format of console output without creating whole new formatter. I just need to adjust one thing info java (slf4j/logback) like format.
From this:
00:19:49 [DBG] Microsoft.AspNetCore.Hosting.Internal.WebHost - App starting
00:19:49 [DBG] Microsoft.AspNetCore.Hosting.Internal.WebHost - App started
into this:
00:19:49 [DBG] m.a.h.i.WebHost - App starting
00:19:49 [DBG] m.a.h.i.WebHost - App started
or just this simple format:
00:19:49 [DBG] WebHost - App starting
00:19:49 [DBG] WebHost - App started
Upvotes: 9
Views: 7559
Reputation: 420
For those who would like the abbreviated variant where Microsoft.AspNetCore.Hosting.Internal.WebHost
becomes M.A.H.Internal.WebHost
(depending on the configured target length) the original logback implementation can be found here: https://github.com/qos-ch/logback/blob/master/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java
Directly converted to a Serilog enricher that would be:
public class AbbreviatedSourceContextEnricher : ILogEventEnricher {
private readonly int _targetLength;
public AbbreviatedSourceContextEnricher( int targetLength ) {
_targetLength = targetLength;
}
public void Enrich( LogEvent logEvent, ILogEventPropertyFactory propertyFactory ) {
var typeName = logEvent.Properties.GetValueOrDefault( "SourceContext" ).ToString();
typeName = typeName.Substring( 1, typeName.Length - 2 );
// If you prefer to not override the SourceContext property you can create a new property:
// logEvent.AddOrUpdateProperty( propertyFactory.CreateProperty( "AbbrSourceContext", Abbreviate( typeName ) ) );
logEvent.AddOrUpdateProperty( propertyFactory.CreateProperty( "SourceContext", Abbreviate( typeName ) ) );
}
private string Abbreviate( string fqClassName ) {
if ( fqClassName == null ) {
throw new ArgumentNullException( nameof(fqClassName) );
}
var inLen = fqClassName.Length;
if ( inLen < _targetLength ) {
return fqClassName;
}
var buf = new StringBuilder( inLen );
var rightMostDotIndex = fqClassName.LastIndexOf( "." );
if ( rightMostDotIndex == -1 )
return fqClassName;
// length of last segment including the dot
var lastSegmentLength = inLen - rightMostDotIndex;
var leftSegments_TargetLen = _targetLength - lastSegmentLength;
if ( leftSegments_TargetLen < 0 )
leftSegments_TargetLen = 0;
var leftSegmentsLen = inLen - lastSegmentLength;
// maxPossibleTrim denotes the maximum number of characters we aim to trim
// the actual number of character trimmed may be higher since segments, when
// reduced, are reduced to just one character
var maxPossibleTrim = leftSegmentsLen - leftSegments_TargetLen;
var trimmed = 0;
var inDotState = true;
var i = 0;
for ( ; i < rightMostDotIndex; i++ ) {
char c = fqClassName.ToCharArray()[i];
if ( c == '.' ) {
// if trimmed too many characters, let us stop
if ( trimmed >= maxPossibleTrim )
break;
buf.Append( c );
inDotState = true;
}
else {
if ( inDotState ) {
buf.Append( c );
inDotState = false;
}
else {
trimmed++;
}
}
}
// append from the position of i which may include the last seen DOT
buf.Append( fqClassName[i..] );
return buf.ToString();
}
}
Upvotes: 0
Reputation: 61875
F# riff on Mojmir's answer (HT @Kostas Rontogiannis):
/// Converts SourceContext which is the fully qualified type name to a short version, using just the type name.
type SourceContextShortEnricher () =
interface Serilog.Core.ILogEventEnricher with
member __.Enrich(logEvent : Serilog.Events.LogEvent, lepf : Serilog.Core.ILogEventPropertyFactory) =
match logEvent.Properties.TryGetValue "SourceContext" with
| true, (:? Serilog.Events.ScalarValue as v) when v <> null && v.Value <> null ->
let typeName =
string v.Value
|> fun s -> match s.LastIndexOf("[[") with -1 -> s | pos -> s.Substring(0, pos)
|> fun s -> match s.LastIndexOf('.') with -1 -> s | idx when s.Length = idx - 1 -> s | idx -> s.Substring(idx + 1)
logEvent.AddPropertyIfAbsent(lepf.CreateProperty("SourceContextShort", typeName))
logEvent.RemovePropertyIfPresent "SourceContext"
| _ -> ()
Upvotes: 0
Reputation: 3439
Thanks for the direction to @Ruben Bartelink. If anyone else will be wondering how to do such thing here is the simple example:
Enricher:
class SimpleClassEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var typeName = logEvent.Properties.GetValueOrDefault("SourceContext").ToString();
var pos = typeName.LastIndexOf('.');
typeName = typeName.Substring(pos + 1, typeName.Length - pos - 2);
logEvent.AddOrUpdateProperty(propertyFactory.CreateProperty("SourceContext", typeName));
}
}
then usage:
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.With(new SimpleClassEnricher())
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} - {Message:lj}{NewLine}{Exception}")
.CreateLogger();
Upvotes: 11