Logging4s is small logging library for structured (json) logs build on top of logback and logstash-encoder.
logging4s-core- type classes for abstract encoding, base Logging support work with Try, Either and unsafe variants.logging4s-cats- implementation forcatsandcats-effectlogging4s-cats-core- implementation forPlainEncoderviacats.Showlogging4s-ce-2- implementation forcats-effect 2Synclogging4s-ce-3- implementation forcats-effect 3Sync
logging4s-zio- implementation on top ofzio.Taskfor runtime andzio.prelude.Debugfor plain logs.logging4s-kyo- implementationkyo.IOfor effect andkyo.Renderfor plain logs.logging4s-json- implementation json logs for different libslogging4s-circe- implementation forcirce.Encoderlogging4s-jsoniter- implementation forjsoneter-scala JsonValueCodeclogging4s-argonaut- implementation forargonaut EncodeJsonlogging4s-borer- implementation forborer Encoderlogging4s-play-json- implementation forplay-json Writeslogging4s-json4s- implementation forjson4s Formatslogging4s-spray-json- implementation forspray-json JsonWriterlogging4s-upickle- implementation forupickle Writerlogging4s-weepickle- implementation forweepickle Fromlogging4s-zio-json- implementation forzio-json JsonEncoder
Let's say you are using cats-effect 3 and circe.
Plug the library in for sbt
libraryDependencies ++= Seq(
"org.logging4s" %% "logging4s-ce-3" % version,
"org.logging4s" %% "logging4s-circe" % version
)Create Loggable implementation for your domain objects, create Logging instance and log your objects.
// Your domain
import java.util.UUID
import cats.Show
import io.circe.Encoder
import io.circe.generic.semiauto.deriveEncoder
import logging4s.core.Loggable
import logging4s.cats.instances.given
import logging4s.json.circe.instances.given
final case class User(id: UUID, name: String, age: Int)
object User:
given Show[User] = user => s"id=${user.id}, name=${user.name}, age=${user.age}"
given Encoder[User] = deriveEncoder
given Loggable[User] = Loggable.make("user")
// Your program
import java.util.UUID
import cats.effect.std.UUIDGen
import cats.effect.{ExitCode, IO, IOApp}
import logging4s.cats.Logging
import logging4s.core.syntax.withKey
import logging4s.cats.instances.given
import logging4s.json.circe.instances.given
object CatsEffect3Example extends IOApp:
private def createUser(name: String, age: Int): IO[User] =
for id <- UUIDGen.randomUUID[IO]
yield User(id, name, age)
override def run(args: List[String]): IO[ExitCode] =
for
context <- IO.randomUUID.map(uuid => LoggingContext(uuid.withKey("session_id")))
logging <- Logging.create[IO]("CatsEffect3Example", context)
johnShow <- createUser("John Show", 22)
_ <- logging.info("User created", johnShow)
daenerys <- createUser("Daenerys Targaryen", 22)
_ <- logging.info("User created", daenerys)
_ <- logging.info("All users created", Seq(johnShow, daenerys))
yield ExitCode.Success
This will output:
{"@timestamp":"2023-01-30T13:42:13.249+03:00","message":"User created: session_id -> (9602ed80-e54b-4e0a-8b9c-64762d28d05e), user -> (id=5db8c5e2-6275-437a-bca8-1ad8cd84fbd8, name=John Show, age=22)","name":"CatsEffect3Example","level":"INFO","user":{"id":"5db8c5e2-6275-437a-bca8-1ad8cd84fbd8","name":"John Show","age":22}}
{"@timestamp":"2023-01-30T13:42:13.249+03:00","message":"User created: session_id -> (9602ed80-e54b-4e0a-8b9c-64762d28d05e), user -> (id=c5e4bd53-abd8-4922-bcd2-5e40322e6b9b, name=Daenerys Targaryen, age=22)","name":"CatsEffect3Example","level":"INFO","user":{"id":"c5e4bd53-abd8-4922-bcd2-5e40322e6b9b","name":"Daenerys Targaryen","age":22}}
{"@timestamp":"2023-01-30T13:42:13.249+03:00","message":"All users created: session_id -> (9602ed80-e54b-4e0a-8b9c-64762d28d05e), users -> ([id=5db8c5e2-6275-437a-bca8-1ad8cd84fbd8, name=John Show, age=22,id=c5e4bd53-abd8-4922-bcd2-5e40322e6b9b, name=Daenerys Targaryen, age=22])","name":"CatsEffect3Example","level":"INFO","users":[{"id":"5db8c5e2-6275-437a-bca8-1ad8cd84fbd8","name":"John Show","age":22},{"id":"c5e4bd53-abd8-4922-bcd2-5e40322e6b9b","name":"Daenerys Targaryen","age":22}]}
In the logback.xml file, you can configure the output of logs as you need.
See ./examples for more examples.
Have a library for structured logging that supports Scala 3 and various implementations of effects and json libraries
cause izumi logstage and tofu-logging still not ported for new scala.