simple-log
1 Quick start
2 Defining a logger
sl-def-log
3 Log destinations
sl-log-to
sl-log-to-display
sl-log-to-file
sl-log-to-file&display
sl-log-to-store
4 Log level
sl-set-log-level
sl-log-level
5 Synchronization
sl-sync
6 In-memory stores
make-sl-store
sl-store-enqueue!
sl-store-length
sl-store-max-length
sl-store->list
sl-store->display
sl-store-grep-message
sl-store-grep-topic
sl-store-grep-level
sl-store-grep
sl-store-tail
sl-store-head
7 Store examples
8 Generated procedures
9.3

simple-log🔗ℹ

Hans Dijkema <hans@dijkewijk.nl>

 (require simple-log) package: simple-log

A small logging layer on top of Racket’s logger system. A log definition creates a logger and convenience procedures for the standard log levels. Messages are formatted with format, timestamped, and dispatched asynchronously to one or more registered destinations.

1 Quick start🔗ℹ

(require simple-log)
 
(sl-def-log example)
(sl-log-to-display)
 
(info-example "Started with ~a items" 3)
(warn-example "This is only an example")

A log line has the following format:

example:info:2026-08-03T10:30:00:Started with 3 items

2 Defining a logger🔗ℹ

syntax

(sl-def-log id)

(sl-def-log id prefix)
(sl-def-log id prefix parent)
Defines a logger with topic 'id. The generated procedure names use id as their prefix unless an explicit prefix is supplied.

For example:

(sl-def-log player)

creates dbg-player, info-player, warn-player, err-player, fatal-player, and sync-log-player.

With an explicit prefix:

(sl-def-log media-renderer renderer)

creates the same procedures with renderer in their names, while the log topic remains 'media-renderer.

If parent is omitted, #f is used as the parent logger.

3 Log destinations🔗ℹ

syntax

(sl-log-to name callback)

Registers callback under the symbolic name derived from name. The name is an identifier, not a runtime value. Registering another callback with the same name replaces the previous callback.

The callback is invoked as:

(callback topic level timestamp message)

The arguments are a topic symbol, a level symbol, an ISO-like timestamp in YYYY-MM-DDTHH:MM:SS form, and the formatted message string.

For example:

(sl-log-to collect-errors
  (lambda (topic level timestamp message)
    (when (memq level '(error fatal))
      (displayln (list timestamp topic message)))))

procedure

(sl-log-to-display)  void?

Registers a destination that writes log lines to the current output port with displayln. Calling the procedure again replaces the existing display destination.

procedure

(sl-log-to-file filename)  void?

  filename : path-string?
Registers a destination that writes log lines to filename. The file is opened with 'replace, and the output is flushed after every line. Calling the procedure again replaces the existing file destination.

procedure

(sl-log-to-file&display filename)  void?

  filename : path-string?
Enables both the display and file destinations.

procedure

(sl-log-to-store [max-length])  any/c

  max-length : exact-nonnegative-integer? = 1000
Creates an in-memory log store, registers it as the current store destination, and returns it. At most max-length entries are retained; older entries are removed when the limit is exceeded.

Only one destination named store is active. A later call to sl-log-to-store replaces the previous store destination, but does not modify the previously returned store.

Logging is asynchronous. Use the generated synchronization procedure before reading the store when all previously submitted messages must be present.

(sl-def-log worker)
(define logs (sl-log-to-store 200))
 
(info-worker "Starting job ~a" 42)
(warn-worker "Job ~a is slow" 42)
(sync-log-worker)
 
(sl-store->display logs)

4 Log level🔗ℹ

procedure

(sl-set-log-level level)  symbol?

  level : 
(or/c 'debug 'dbg
      'info
      'warning 'warn
      'error 'err
      'fatal)
Sets the module-wide minimum log level and returns its normalized symbol. The aliases 'dbg, 'warn, and 'err are normalized to 'debug, 'warning, and 'error. Other values raise an exception.

procedure

(sl-log-level)  symbol?

Returns the current module-wide minimum log level. The default is 'debug.

5 Synchronization🔗ℹ

procedure

(sl-sync logger topic)  void?

  logger : logger?
  topic : symbol?
Submits a synchronization event to logger and waits until the simple-log receiver has processed it. Normally this lower-level procedure is not needed directly; use the sync-log-prefix procedure generated by sl-def-log.

Synchronization is useful before inspecting an in-memory store or before a program exits immediately after its final log message.

6 In-memory stores🔗ℹ

A store contains log entries in chronological order. Every entry consists of:

(list topic level timestamp message)

The filtering procedures return new stores and leave the original store unchanged. The new store has the same maximum length as the original.

procedure

(make-sl-store [max-length])  any/c

  max-length : exact-nonnegative-integer? = 1000
Creates an empty store without registering it as a log destination. Most code uses sl-log-to-store instead.

procedure

(sl-store-enqueue! store    
  topic    
  level    
  timestamp    
  message)  void?
  store : any/c
  topic : symbol?
  level : 
(or/c 'debug 'dbg
      'info
      'warning 'warn
      'error 'err
      'fatal)
  timestamp : string?
  message : string?
Adds an entry to store. When the maximum length is exceeded, entries are removed from the beginning of the store.

procedure

(sl-store-length store)  exact-nonnegative-integer?

  store : any/c
Returns the current number of entries in store.

procedure

(sl-store-max-length store)  exact-nonnegative-integer?

  store : any/c
Returns the configured maximum number of entries in store.

procedure

(sl-store->list store)  list?

  store : any/c
Returns the entries as a list in chronological order. Each entry is a list of topic, level, timestamp, and message.

procedure

(sl-store->display store)  void?

  store : any/c
Writes all entries to the current output port in the same colon-separated format used by the display and file destinations.

procedure

(sl-store-grep-message store    
  regexp    
  [#:invert? invert?])  any/c
  store : any/c
  regexp : regexp?
  invert? : boolean? = #f
Returns a store containing entries whose message matches regexp. When invert? is true, entries matching the filter are excluded instead.

procedure

(sl-store-grep-topic store    
  topic-filter    
  [#:invert? invert?])  any/c
  store : any/c
  topic-filter : 
(or/c symbol?
      regexp?
      (listof (or/c symbol? regexp?)))
  invert? : boolean? = #f
With a symbol, returns entries whose topic is exactly that symbol. With a regular expression, matches against the string form of the topic. A list selects entries matching any listed symbol or regular expression. When invert? is true, entries matching the filter are excluded instead.

(sl-store-grep-topic logs '(webview webview-backend))
(sl-store-grep-topic logs 'webview-backend #:invert? #t)

procedure

(sl-store-grep-level store level-or-regexp)  any/c

  store : any/c
  level-or-regexp : 
(or/c regexp?
      'debug 'dbg
      'info
      'warning 'warn
      'error 'err
      'fatal)
Returns entries at the selected level or a more severe level. A regular expression is matched against the available level names and selects the first matching level.

(sl-store-grep-level logs 'warning)
(sl-store-grep-level logs #rx"err(or)?")

procedure

(sl-store-grep store    
  value    
  [#:invert? invert?])  any/c
  store : any/c
  value : (or/c symbol? regexp?)
  invert? : boolean? = #f
Performs a combined search.

When value is a log-level symbol, the result contains entries at that level or a more severe level. Any other symbol is treated as an exact topic. A regular expression is matched against both topic and message. If that same regular expression also identifies a log level, entries below that level are excluded. When invert? is true, matching entries are excluded, similar to grep -v.

procedure

(sl-store-tail store count)  any/c

  store : any/c
  count : exact-nonnegative-integer?
Returns a store containing the last count entries. If count is greater than the store length, all entries are returned.

procedure

(sl-store-head store count)  any/c

  store : any/c
  count : exact-nonnegative-integer?
Returns a store containing the first count entries. If count is greater than the store length, all entries are returned.

7 Store examples🔗ℹ

(sl-def-log player)
(define logs (sl-log-to-store 500))
 
(info-player "Opening ~a" "album.flac")
(warn-player "No duration available")
(err-player "Renderer returned status ~a" 701)
(sync-log-player)
 
(define warnings-and-errors
  (sl-store-grep-level logs 'warning))
 
(define renderer-errors
  (sl-store-grep
   (sl-store-grep-topic logs 'player)
   #rx"renderer|status"))
 
(sl-store->display (sl-store-tail warnings-and-errors 20))

8 Generated procedures🔗ℹ

For a definition such as:

(sl-def-log my-module)

simple-log creates the following procedures:

procedure

(dbg-my-module message argument ...)  void?

  message : string?
  argument : any/c
Emits a debug message.

procedure

(info-my-module message argument ...)  void?

  message : string?
  argument : any/c
Emits an informational message.

procedure

(warn-my-module message argument ...)  void?

  message : string?
  argument : any/c
Emits a warning message.

procedure

(err-my-module message argument ...)  void?

  message : string?
  argument : any/c
Emits an error message.

procedure

(fatal-my-module message argument ...)  void?

  message : string?
  argument : any/c
Emits a fatal message.

procedure

(sync-log-my-module)  void?

Waits until the simple-log receiver has processed all earlier messages from this logger.

The message procedures pass message and argument values to format. Log delivery to the registered destinations is asynchronous.