witness

Types

pub opaque type Config

A unit of structured data

Use these to create them:

witness.string
witness.int
witness.float
witness.bool
pub opaque type Field

The currently available formats

pub type Format {
  Json
  Text
}

Constructors

  • Json

    (Pretty printed for reading convenience)

    {
      "timestamp": "2026-05-28T18:30:40.020522296Z",
      "level": "info",
      "message": "Some log message",
      "example": "you can add structured data",
      "also_bools": true,
      "and_floats": 1.23,
      "ints_as_well": 3
    }
    
  • Text
    [INFO] 20:30:40 Some log message
      example: you can add structured data
      also_bools: True
      and_floats: 1.23
      ints_as_well: 3
    
pub type Sink =
  fn(logging.LogLevel, String) -> Nil

Values

pub fn bool(name name: String, value value: Bool) -> Field

Create a bool field

Example

witness.this(logging.Info, "Things happened", [witness.bool("really", True)])
pub fn default_config() -> Config

The default config logging to io.println with format witness.Text

pub fn empty_config() -> Config

Get a new empty config

Example

witness.empty_config()
|> with_sink(witness.Json, logging.log)
|> witness.set_config
pub fn float(name name: String, value value: Float) -> Field

Create a float field

Example

witness.this(logging.Info, "Things happened", [witness.float("how_many", 3.14159)])
pub fn int(name name: String, value value: Int) -> Field

Create an int field

Example

witness.this(logging.Info, "Things happened", [witness.int("how_many", 21)])
pub fn level_to_short_string(level: logging.LogLevel) -> String

Turn a log level into a “[LEVEL]” string

pub fn set_config(config: Config) -> Nil

Set your config globally

Example

witness.empty_config()
|> with_sink(witness.Json, logging.log)
|> witness.set_config
pub fn set_process_fields(fields: List(Field)) -> Nil

Put per process fields into the process dictionary.

These fields will be prepended to every message that process logs.

Example

witness.set_process_fields([
  witness.string("process_name", "my special process"),
])
pub fn string(name name: String, value value: String) -> Field

Create a string field

Example

witness.this(logging.Info, "Something happened", [witness.string("what", "i dont know, honestly")])
pub fn this(
  level level: logging.LogLevel,
  message message: String,
  fields fields: List(Field),
) -> Nil
pub fn with_global_fields(
  config: Config,
  fields: List(Field),
) -> Config

These fields will be prepended to every message logged through witness. Does not replace fields from previous calls.

Example


witness.empty_config()
|> with_sink(witness.Json, logging.log)
|> witness.with_global_fields([
  witness.string("application", "my_application"),
  witness.string("version", "1.0.0"),
])
|> witness.set_config
pub fn with_sink(
  config: Config,
  format: Format,
  sink: fn(logging.LogLevel, String) -> Nil,
) -> Config

Add a sink with a specific formatting to a config

Example

witness.empty_config()
|> with_sink(witness.Json, logging.log)
|> witness.set_config
Search Document