lamina.trace documentation

aggregating-trace-router

fn

[endpoint-router]

A trace-router which splits the query into distributable and non-distributable portions, and forwards the distributable portion to endpoint-router.

analyze-timings

fn

[{:keys [period window], :or {window (time/hours 1), period (time/period)}, :as options} ch]

Aggregates timings, and periodically emits statistical information about them.

canonical-probe-name

fn

[x]

Returns the canonical representation of a probe name.

:foo:bar => "foo:bar" `[:foo "bar:baz"] => "foo:bar:baz"

defn-instrumented

macro

[fn-name & body]

A def form of (instrumented-fn...). Options can be defined in the function metadata:

(defn-instrumented foo {:implicit? false :timeout (constantly 1000)} "Here's a doc-string" [x] ...)

The :name can be explicitly defined, but will default to

the:function:namespace:the-function-name

distill-timing

fn

[timing]

Returns a distillation of the timing object, containing only :task, :durations, :context, and :sub-tasks. This is an idempotent operation.

This data structure can be merged using merge-distilled-timings.

error-probe-channel

var

Like probe-channel, but if the probe is not active every message enqueued into the channe will be logged as an error.

instrument

fn

[f {:keys [executor capture timeout probes implicit? with-bindings?], :as options, :or {implicit? true, capture :none, with-bindings? false}}]

A general purpose transform for functions, allowing for tracing their execution, defining timeouts, and deferring their execution onto a thread pool.

Instrumenting a function does not change its behavior in any way (unless an :executor is defined, see below). This can be a powerful tool for both understanding complex tasks during development, and monitoring their behavior in production.


OVERHEAD

Instrumenting adds some overhead to a function, equivalent to the performance difference between calling

(+ 1 2 3)

and

(apply + [1 2 3])

If you'd happily call apply on the function being instrumented, chances are you won't notice the difference.


PROBES

Instrumenting a function creates enter, return, and error probes. A :name must be specified, and probe names will be of the structure name:enter, name:return, etc. Data emitted by these probes may be captured by other functions if :implicit? is set to true, which is the default.

When the function is invoked, the enter probe emits a hash of the form

:name - the :name specified in the options :timestamp - time of invocation in milliseconds since the epoch :args - a list of arguments passed to the function

When the function completes and the value is realized, the return probe will emit the data above, and also:

:duration - the time elapsed since the invocation, in nanoseconds :result - the value returned by the function :sub-tasks - return probe data, less :result, for all implicit instrumented sub-functions

If an error is thrown, or the value is realized as an error, :result is replaced by

:error - the exception thrown or realized error

A :probes option may be defined, giving a hash of probe names onto channels that will consume their data:

{:error (sink->> #(println "ERROR:" %)) :return (sink->> #(println "Given" (:args %) ", returned" (:result %)))}

TIMEOUTS

A :timeout option may be specified, which should be a function that takes the arguments passed to the function, and returns the timeout in milliseconds or nil for no timeout. If the timeout elapses without any value, the returned result will be realized as an error of type 'lamina/timeout!'.

EXECUTORS

If an :executor is specified, the function will be executed on that thread pool, and return an unrealized result representing its eventual value.

In this case, :timeout will also interrupt the thread if it is still actively computing the value, and the return probe will include an :enqueued-duration parameter that describes the time, in nanoseconds, spent waiting to be executed.

instrumented-fn

macro

[fn-name {:keys [executor capture with-bindings? implicit? timeout probes], :or {capture :none, with-bindings? false, implicit? true}, :as options} & fn-tail]

A compile-time version of (instrument ...).

(instrumented-fn foo {:implicit? false} [x y] (+ x y))

The

local-trace-router

var

A trace-router which can be used to consume and analyze probe-channels.

merge-distilled-timings

fn

[& distilled-timings]

Returns a list of one or distilled timings, where :durations have been concatenated together for identical tasks.

probe-channel

var

Returns a probe channel for the given name. Keywords will be converted to simple strings, and sequences will be joined with ':' characters.

[:a "b" [1 2 3]] => "a:b:1:2:3"

probe-enabled?

fn

[_]

Returns true if the probe has downstream channels.

probe-names

fn

[]

Returns a list of all existing probes.

register-context-builder

fn

[f]

Defines a function which is given a default context map, and returns a modified context map.

select-probes

fn

[& wildcard-strings-or-regexes]

Activates all probes that match the given strings or regexes, and returns a channel that will emit all messages from these probes.

subscribe

fn

[router topic options]

Returns a channel corresponding to topic. options are router-specific, and may be nil.

time*

macro

[& body]

A somewhat more useful variant of (time ...), which captures the sub-timings of all instrumented functions called within the scope. If the body returns an unrealized value, time* will wait for it to become realized.

trace

macro

[probe & body]

Enqueues a value into the probe-channel described by probe. The body is executed only if there is a consumer for the probe channel; this is essentially a log statement that is only active if someone is paying attention.

For performance reasons, the probe name must be something that can be resolved at compile-time.

trace*

macro

[probe & body]

A variant of trace that allows the probe name to be resolved at runtime.

trace-router

fn

[{:keys [generator topic->id on-subscribe on-unsubscribe timestamp payload task-queue auto-advance?], :or {payload identity, task-queue (time/task-queue), auto-advance? false}, :as options}]

Creates a router which can be queried via the default syntax and allows multiple queries to share sub-streams.

Like lamina.cache/router, with the addition of:

:timestamp - a function which returns the logical time of each message, defaults to wall time.

:payload - a function which describes the content of each message, defaults to identity.

tracing?

fn

[]

Returns true when called inside an active function trace scope, false otherwise.

with-instrumentation

macro

[& body]

Returns the full timing data for all code called within the scope.