Skip to content

Breaking changes

Mykhailo Shevchuk edited this page Jun 6, 2026 · 20 revisions

v9 is a ground-up rewrite of the sink in F#, with a public API that stays idiomatic from C#. See Upgrading from v8 to v9 for a step-by-step guide.

Platform

  • Targets net8.0, net9.0, net10.0. Dropped netstandard2.0, net5.0, net6.0, net7.0 (all EOL)
  • Requires Serilog 4.3.1+ (uses Serilog's native batching)
  • FSharp.Core is now a transitive dependency

Configuration / appsettings.json

  • batchPostingLimitbatchSizeLimit
  • The level label is now controlled by handleLogLevelAsLabel (default true); in v8 the label was added automatically and could not be turned off
  • Removed reservedPropertyRenamingStrategy (and IReservedPropertyRenamingStrategy)
  • Removed useInternalTimestamp
  • Removed leavePropertiesIntact
  • queueLimit is now bounded by default (50 000) instead of unbounded

Behaviour

  • Fatal is now rendered as fatal (was critical) - update dashboards/alerts
  • Properties promoted via propertiesAsLabels are now kept in the log body as well
  • Reserved body keys (Message, MessageTemplate, Exception, TraceId, SpanId) are collision-prefixed with _
  • Delivery uses Serilog native batching: failed batches are retried with exponential backoff up to retryTimeLimit (default 10 min), then dropped; emission is fully async (no dispose deadlock)
  • The uri is validated at logger-configuration time (throws ArgumentException)
  • The tenant is also validated at logger-configuration time, now against Loki's documented tenant ID rules: alphanumerics plus !-_.*'(), at most 150 bytes, and not . or .. (throws ArgumentException). v8 used a slightly different rule set - it rejected .. anywhere in the value (now only the exact values ./.. are rejected, per the docs) and had no length cap

HTTP client

  • Removed ILokiHttpClient / BaseLokiHttpClient / LokiHttpClient / LokiGzipHttpClient
  • Inject a standard HttpClient (httpClient) or HttpMessageHandler (httpMessageHandler) instead; gzip is now a DelegatingHandler - see Custom HttpClient

New features

  • LokiJsonTextFormatter is used as a default text formatter. Removed property outputTemplate
  • Removed interface ILabelAwareTextFormatter
  • Redesigned approach of mapping properties to labels: removed filtrationMode and filtrationLabels, added propertiesAsLabels
  • Properties, mapped to the labels won't be passed to the text formatter
  • Redesigned behavior of log level data (level label): removed createLevelLabel
  • Added IReservedPropertyRenamingStrategy and reservedPropertyRenamingStrategy configuration property

More detailed description and reasons behind this changes are described here

  • Now level label is not created by default

To keep previous behavior set createLevelLabel to true at sink configuration

 .WriteTo.GrafanaLoki(
                    "http://localhost:3100",
                    createLevelLabel: true)
      {
        "Name": "GrafanaLoki",
        "Args": {
          "uri": "http://localhost:3100",
          "createLevelLabel": true
        }
      }
  • Deprecated dependency Serilog.Sinks.Http
  • Replaced IHttpClient with ILokiHttpClient Migration guide: You will have to migrate your code if you've implemented your own version of ILokiHttpClient. The signature of method ILokiHttpClient.PostAsync (before upgrade IHttpClient.PostAsync has changed from Task PostAsync(string, HttpContent) to Task PostAsync(string, Stream).

Before upgrade:

public class CustomLokiHttpClient : IHttpClient
{
  // Code removed for brevity...

  public async Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
  {
    // Here you probably have some code updating the content,
    // and then you send the request
    return await httpClient.PostAsync(requestUri, content)
  }
}

After update:

public class CustomLokiHttpClient : ILokiHttpClient
{
  // Code removed for brevity...

  public async Task<HttpResponseMessage> PostAsync(string requestUri, Stream contentStream)
  {
      using var content = new StreamContent(contentStream);

      // Here you probably have some code updating the content,
      // and then you send the request
      return await httpClient.PostAsync(requestUri, content)
  }
}
  • Removed DurableGrafanaLokiUsingTimeRolledBuffers and DurableGrafanaLokiUsingFileSizeRolledBuffers
  • Replaced excludedLabels with filtrationMode and filtrationLabels

Clone this wiki locally