The natchez-log module provides a back end that logs traces locally (as JSON) to a log4cats Logger, with no distributed functionality. To use it, add the following dependency.

libraryDependencies += "org.tpolecat" %% "natchez-log" % "0.1.5"
dependencies {
  implementation "org.tpolecat:natchez-log_2.13:0.1.5"

Example Trace

The log handler constructs a JSON object for each root span, with a children collection of child spans. On completion of the root span the entire JSON blob is logged as a single message. Here is an example with a root span containing two custom fields and two child spans.

  "name" : "root span",
  "service" : "example-service",
  "timestamp" : "2021-05-26T16:41:09.221325Z",
  "duration_ms" : 108,
  "trace.span_id" : "dd817430-c00a-423e-9db4-0a24897b9cb9",
  "trace.parent_id" : null,
  "trace.trace_id" : "ef7758f0-7b33-4358-81f5-4f2070f7196d",
  "exit.case" : "succeeded",
  "person.name" : "bob",
  "person.age" : 42,
  "children" : [
      "name" : "thing one",
      "service" : "example-service",
      "timestamp" : "2021-05-26T16:41:09.301796Z",
      "duration_ms" : 3,
      "trace.span_id" : "93f6aab7-b89d-4c11-b21a-2272ae6f5697",
      "trace.parent_id" : "ef7758f0-7b33-4358-81f5-4f2070f7196d",
      "trace.trace_id" : "ef7758f0-7b33-4358-81f5-4f2070f7196d",
      "exit.case" : "succeeded",
      "children" : [
      "name" : "thing two",
      "service" : "example-service",
      "timestamp" : "2021-05-26T16:41:09.328357Z",
      "duration_ms" : 1,
      "trace.span_id" : "f94c8eed-8c1a-4c02-b40e-36266e40b4d5",
      "trace.parent_id" : "ef7758f0-7b33-4358-81f5-4f2070f7196d",
      "trace.trace_id" : "ef7758f0-7b33-4358-81f5-4f2070f7196d",
      "exit.case" : "succeeded",
      "children" : [

Constructing an EntryPoint

You can construct a log EntryPoint given an implicit Logger[F].

import cats.effect.IO
import natchez.EntryPoint
import natchez.log.Log
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.slf4j.Slf4jLogger

implicit val log: Logger[IO] =

val ep: EntryPoint[IO] =

Log.entryPoint takes an optional second parameter format: io.circe.Json => String that can be used to manipulate the structure and content of the trace and customize the way it is formatted. The default value is _.spaces2.

Log Fields

Log spans include the following fields, in addition to any user-specified fields:

Field Json Type Meaning
service String User-supplied service name.
timestamp String ISO-8601 date and time of span start.
name String User-supplied span name.
trace.trace_id String (UUID) Id shared by all spans in a trace.
trace.span_id String (UUID) Id of the parent span, if any.
trace.parent_id String (UUID, Optional) Id of the parent span, if any.
duration_ms Number Span duration, in milliseconds.
exit.case String completed, canceled, or error
exit.error.class String (Optional) Exception classname, if any.
exit.error.message String (Optional) Exception message, if any.
exit.error.stackTrace List of String (Optional) Exception stack trace, if any.

If you don’t want to see the trace/span IDs or other fields, you can filter them out by supplying a format argument to entryPoint that manipulates the JSON however you like.

The source code for this page can be found here.