SENDLINE open source
open source / logging4s / overview
No. 02now.sendline · Apache-2.0

logging4s

Logging for Scala 3

Structured logging for Scala 3. You describe how your types render with a Loggable[A] type class, get a Logging[F[_]] for your effect type, and log values directly. Each value is rendered to JSON and attached to the log event as structured data; the message string carries a human-readable rendering of the same values.

Install

libraryDependencies ++= Seq(
  "now.sendline" %% "logging4s-cats"    % "7.0.0",
  "now.sendline" %% "logging4s-logback" % "7.0.0",
)

import logging4s.cats.CatsInstances.given

The runtime you pick here is remembered on every page and in every library. A backend goes next to the runtime: logback here, or log4j2, slf4j or the console. logging4s-kyo needs JDK 25; every other module runs on JDK 17.

At a glance

The library is a backend-agnostic core plus thin integration modules. The logging backend (logback / log4j2 / slf4j / console), the effect type (cats-effect / ZIO / Kyo / rapid), and the JSON codec (circe / jsoniter / …) are independent modules, each wired in through a given import. core has no dependency on any of them.

flowchart LR
  app["your application"] --> core

  subgraph core["logging4s-core"]
    L["Loggable · JsonEncoder · PlainEncoder"]
    G["Logging[F]"]
  end

  B["backend<br/>logback · log4j2 · slf4j · console"] -->|given LoggingFactory| G
  R["runtime<br/>cats-effect · ZIO · Kyo · rapid"] -->|given Delay, PlainEncoder| L
  J["json<br/>circe · jsoniter · …"] -->|given JsonEncoder| L

Quick start

Pick a backend and an effect runtime (a JSON library is optional — derives Loggable needs none):

scala
libraryDependencies ++= Seq(
  "now.sendline" %% "logging4s-cats"    % "7.0.0",
  "now.sendline" %% "logging4s-logback" % "7.0.0"
)
scala
import cats.effect.{IO, IOApp}

import logging4s.core.{Loggable, Logging}
import logging4s.cats.CatsInstances.given        // Delay[IO], cats.Show/data bridges
import logging4s.logback.LogbackInstances.given  // the LoggingFactory

final case class User(id: Int, name: String) derives Loggable

object Main extends IOApp.Simple:
  def run: IO[Unit] =
    for
      log <- Logging.create[IO]("Main")
      _   <- log.info("user created", User(1, "John"))
    yield ()

The logback backend attaches User as a nested JSON object; the message string gets its plain rendering (abridged — LogstashEncoder also writes @timestamp, logger_name, thread_name and the rest of its envelope):

json
{"message":"user created: user -> (id -> (1), name -> (John))","user":{"id":1,"name":"John"},"source":"Main.scala:13","level":"INFO"}

Adopters

Betby

Using logging4s? Open a PR to add your logo.

Where next

built from README.md at v7.0.0 edit this page on GitHub