jlogger  Table of Contents

  1. A logging implementation for Janet - jlogger
  2. Purpose
  3. Installation
  4. How it works
    1. Heirarchy:
    2. log-manager
    3. log-sink-combo (special)
    4. log-sink
    5. log-formatter
    6. log-namer
  5. Usage
  6. This should be flexible!
  7. What’s left to do
    1. Proxy objects for all log types (hide their internals)
    2. Documentation

A logging implementation for Janet - jlogger


Provide a decent abstraction on logging along with a nice interface for one to be able to design their own logging backends.


To Be Done

How it works


log-manager -> (log-sink-combo) -> log-sink… log-formatter log-namer


This manages all the logs for the application, it’s purpose is to open loggers and return a logging object (logger).

log-sink-combo (special)

A logger is usually just a log-sink except when a logger actually combines multiple log-sink’s into one logger object.


Any object conforming to the following interface (TBD).


Something that can format a log, supporting all that string/format does and outputting things in it’s unique manner.


Something to format the name of a log.


(import logging)

# get a log manager
(def manager (:new logging/manager))
# optional parameters, with defaults -
#  :rotation        {:bytes :never :lines :never
#                    :interval :never :boundary :daily}
#  :flush-frequency {:bytes 0 :lines 0 :interval 0}
#  :formatter       formatters/default
#  :namer           namers/default
#  :levels          [:trace :debug :info :warn :error :fatal]

# open a log sink (it's a combo sink, so will need to open sub-sinks)
(def combo (:open m :type :combo :name "arbitray"))

# open a file sink, could do this direct on a log manager, same way
# just one would instead assign the result to a variable to be able
# to use it.
(:open combo :type :file :name "arbitrary-file")
# optional:
#  :initializer {:split false :directory "." :levels nil}
# NOTE: levels should match one of the levels defined when opening
# a log manager, such as :trace from default, or it'll default to all
# levels. When defined, it'll log only at that level and levels above it.
# When split, each level will be logged to its own file.

# open a console sink
(:open combo :type :console :name "arbitrary-console")
# optional
#  :initializer {:levels nil :errors nil}
# NOTE: levels should be same as described for files, yet must be a
# level below what :errors is set to. With :errors, nil is same as
# the last level + 1. levels defaults to first level. When levels is
# defined, any level at the level specified, and the ones above it'll
# will be logged to stdout, up to the level defined by :errors. Levels
# at the same as :errors, and above, will be logged to stderr.

(:error combo "Error")
(:error combo "Formatted %q" '[a b c d])
(:trace combo "Trace")
(:write combo :trace "Trace")
(:write combo :trace "Formatted %q" '[a b c d])

# can optionally close logs individually, but should only do it from
# the manager like (:close manager "arbitrary-name-from-before")

# close all logs
(:close manager)

This should be flexible!

The way it works should support the addition of any logger type from networked, to threaded, or sql, or whatever the imagination gives.

For instance, for JSON formatting either you can open the logging/manager with a formatter that emits JSON or you could, when opening a log with the log manager, instruct the individual log (per it’s :new method) to output using a certain formatter.

Each log-sink should be responsible for it’s own rotation (you can’t rotate a console for instance like you could files).

The above example seems a bit verbose, maybe there should be an easy default way?

Things may change as this project evolves, this gives an idea as to what is envisioned however.

What’s left to do

TODO Proxy objects for all log types (hide their internals)

TODO Documentation

The idea is to take the root log-manager proxy, and append each logging type’s documentation to it where sensible.