Guides

For Serilog users

Every Serilog API and its logit spelling, and the two things that differ.

logit is Serilog's model — templates, structured capture, enrichment, filtering, sinks, output templates, CLEF — written for JavaScript. If you know Serilog you know logit; this page is the dictionary.

The dictionary#

Seriloglogit
new LoggerConfiguration()…CreateLogger()new LoggerConfiguration()…createLogger() — or createLogger({ … }) with an options object
Log.Logger = loggerconfigure({ … }) reconfigures the global log in place
Log.Information(…), Log.Error(ex, …)log.info(…), log.error(err, …)
Log.ForContext<T>() / ForContext("SourceContext", "…")log.forSource('app.db')
Log.ForContext("UserId", id)log.forContext('UserId', id) or log.forContext({ UserId: id })
Log.ForContext("Order", order, destructureObjects: true)log.forContext('Order', order, true)
Log.CloseAndFlush()await closeAndFlush()
Log.IsEnabled(level)log.isEnabled(level)
LogContext.PushProperty("RequestId", id) in a usingusing _ = LogContext.push({ RequestId: id }) — or LogContext.run({ … }, fn)
Enrich.FromLogContext()on by default (logContext: false to turn it off)
Enrich.WithProperty("App", "shop").enrich.withProperty('App', 'shop') / enrichers: [withProperty(…)]
Enrich.WithMachineName(), WithProcessId(), WithEnvironmentName()withMachineName(), withProcessId(), withEnvironment()
Enrich.With(new MyEnricher())enrichers: [(event) => event.addPropertyIfAbsent(…)]
MinimumLevel.Debug().minimumLevel.debug() / minimumLevel: 'debug'
MinimumLevel.Override("Microsoft", Warning).minimumLevel.override('next', 'warn') / overrides: { next: 'warn' }
MinimumLevel.ControlledBy(levelSwitch).minimumLevel.controlledBy(levelSwitch) / minimumLevel: levelSwitch
LoggingLevelSwitchLevelSwitch
WriteTo.Console(outputTemplate: "…").writeTo.console({ format: 'text', outputTemplate: '…' })
WriteTo.Console(new CompactJsonFormatter()).writeTo.console({ format: 'clef' })
WriteTo.File("log-.txt", rollingInterval: Day, retainedFileCountLimit: 14)fileSink({ path: 'log.txt', rolling: 'day', retainedFileCount: 14 })
WriteTo.Seq("http://…", apiKey: …).writeTo.seq({ serverUrl, apiKey })
WriteTo.OpenTelemetry(…)otlpHttpSink({ … })
WriteTo.Logger(lc => …).writeTo.logger((lc) => …) or a Logger in sinks
WriteTo.Conditional(pred, wt => …)conditional(pred, sink)
AuditTo.Sink(…)auditSinks: [sink] / audit(sink)
restrictedToMinimumLevel:restrictedToMinimumLevel: on every sink, or restricted(sink, level)
Filter.ByExcluding(Matching.FromSource("…")).filter.byExcluding(Matching.fromSource('…'))
Filter.ByIncludingOnly(Matching.WithProperty<int>("Count", c => c < 10)).filter.byIncludingOnly(Matching.withProperty('Count', (c: number) => c < 10))
Destructure.ByTransforming<T>(t => …).destructure.byTransforming(T, (t) => …)
Destructure.ToMaximumDepth(n), ToMaximumStringLength, ToMaximumCollectionCountthe same names on .destructure, or destructuring: { maxDepth, maxStringLength, maxCollectionCount }
IDestructuringPolicy(value: object) => { value } | undefined
Serilog.Enrichers.Sensitive (by key)destructuring: { redact: [...] }
SelfLog.Enable(Console.Error)selfLog.enable('stderr')
UseSerilogRequestLogging()withLogging() (Next.js) / requestLogger() (Express)
IDiagnosticContext.Set(…)diagnosticContext.set(…)
Operation.Begin(…), Operation.Time(…) (SerilogTimings)log.beginOperation(…), log.timed(…)
CompactJsonFormatter / RenderedCompactJsonFormatterclefFormatter() / clefFormatter({ renderMessage: true })
MessageTemplateTextFormattertextFormatter({ outputTemplate })
Serilog.Expressionson the roadmap
Serilog.Settings.Configurationon the roadmap; LOGIT_LEVEL / LOGIT_OVERRIDES today

Level names#

The methods are JavaScript's: verbose debug info warn error fatal. Serilog's names are accepted anywhere a level is parsed — 'Information', 'Warning' — and are what CLEF's @l and {Level} in an output template print, so Seq dashboards and output templates carry over unchanged.

What differs, and why#

Plain objects are data. In Serilog an object without @ is ToString()'d. In JavaScript a literal { city, zip } has no ToString worth logging, and dumping it is what everyone wants — so plain objects and arrays are captured structurally without an operator. @ keeps its job for class instances, which otherwise render with their own toString() / toJSON() or class name. $ is unchanged. See Structured data.

There is a one-off properties form. log.info({ userId }, 'Signed in') adds properties to one event — a nod to pino and winston users. Serilog has no equivalent; forContext is the idiomatic way in both.

No Log.Logger assignment. The global log is a logger whose pipeline configure() swaps; loggers derived from it before the call follow the new configuration. A logger from createLogger() is independent, as a Serilog Logger instance is.

Enrichers are functions, filters are predicates, sinks are objects with emit — no interfaces to implement.

using needs TypeScript 5.2+. Without it, LogContext.run(props, fn) is the callback form and op.complete() / op.abandon() are explicit.

Example#

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .Enrich.WithProperty("Application", "shop")
    .WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
    .WriteTo.Seq("http://localhost:5341")
    .CreateLogger();
configure(
  new LoggerConfiguration()
    .minimumLevel.debug()
    .minimumLevel.override('next', 'warn')
    .enrich.fromLogContext()
    .enrich.withProperty('Application', 'shop')
    .writeTo.console({ format: 'text', outputTemplate: '{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}' })
    .writeTo.seq({ serverUrl: 'http://localhost:5341' }),
);