blob: a532f26e51c91f9d79ba837d78d675f1b18892e0 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02002// SPDX-License-Identifier: Apache-2.0
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02003
4/*
5Package logtree implements a tree-shaped logger for debug events. It provides log publishers (ie. Go code) with a
Serge Bazanski12971d62020-11-17 12:12:58 +01006glog-like API and io.Writer API, with loggers placed in a hierarchical structure defined by a dot-delimited path
7(called a DN, short for Distinguished Name).
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02008
Tim Windelschmidt99e15112025-02-05 17:38:16 +01009 tree.MustLeveledFor("foo.bar.baz").Warningf("Houston, we have a problem: %v", err)
10 fmt.Fprintf(tree.MustRawFor("foo.bar.baz"), "some\nunstructured\ndata\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020011
12Logs in this context are unstructured, operational and developer-centric human readable text messages presented as lines
13of text to consumers, with some attached metadata. Logtree does not deal with 'structured' logs as some parts of the
14industry do, and instead defers any machine-readable logs to either be handled by metrics systems like Prometheus or
15event sourcing systems like Kafka.
16
Tim Windelschmidt99e15112025-02-05 17:38:16 +010017# Tree Structure
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020018
Serge Bazanski06d65bc2020-09-24 10:51:59 +020019As an example, consider an application that produces logs with the following DNs:
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020020
Tim Windelschmidt99e15112025-02-05 17:38:16 +010021 listener.http
22 listener.grpc
23 svc
24 svc.cache
25 svc.cache.gc
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020026
27This would correspond to a tree as follows:
28
Tim Windelschmidt99e15112025-02-05 17:38:16 +010029 .------.
30 | "" |
31 | (root) |
32 '------'
33 .----------------' '------.
34 .--------------. .---------------.
35 | svc | | listener |
36 '--------------' '---------------'
37 | .----' '----.
38 .--------------. .---------------. .---------------.
39 | svc.cache | | listener.http | | listener.grpc |
40 '--------------' '---------------' '---------------'
41 |
42 .--------------.
43 | svc.cache.gc |
44 '--------------'
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020045
46In this setup, every DN acts as a separate logging target, each with its own retention policy and quota. Logging to a DN
47under foo.bar does NOT automatically log to foo - all tree mechanisms are applied on log access by consumers. Loggers
48are automatically created on first use, and importantly, can be created at any time, and will automatically be created
49if a sub-DN is created that requires a parent DN to exist first. Note, for instance, that a `listener` logging node was
50created even though the example application only logged to `listener.http` and `listener.grpc`.
51
52An implicit root node is always present in the tree, accessed by DN "" (an empty string). All other logger nodes are
53children (or transitive children) of the root node.
54
55Log consumers (application code that reads the log and passes them on to operators, or ships them off for aggregation in
56other systems) to select subtrees of logs for readout. In the example tree, a consumer could select to either read all
57logs of the entire tree, just a single DN (like svc), or a subtree (like everything under listener, ie. messages emitted
58to listener.http and listener.grpc).
59
Tim Windelschmidt99e15112025-02-05 17:38:16 +010060# Leveled Log Producer API
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020061
62As part of the glog-like logging API available to producers, the following metadata is attached to emitted logs in
63addition to the DN of the logger to which the log entry was emitted:
64
Tim Windelschmidt99e15112025-02-05 17:38:16 +010065 - timestamp at which the entry was emitted
66 - a severity level (one of FATAL, ERROR, WARN or INFO)
67 - a source of the message (file name and line number)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020068
69In addition, the logger mechanism supports a variable verbosity level (so-called 'V-logging') that can be set at every
70node of the tree. For more information about the producer-facing logging API, see the documentation of the LeveledLogger
71interface, which is the main interface exposed to log producers.
72
Serge Bazanski12971d62020-11-17 12:12:58 +010073If the submitted message contains newlines, it will be split accordingly into a single log entry that contains multiple
74string lines. This allows for log producers to submit long, multi-line messages that are guaranteed to be non-interleaved
75with other entries, and allows for access API consumers to maintain semantic linking between multiple lines being emitted
76as a single atomic entry.
77
Tim Windelschmidt99e15112025-02-05 17:38:16 +010078# Raw Log Producer API
Serge Bazanski12971d62020-11-17 12:12:58 +010079
80In addition to leveled, glog-like logging, LogTree supports 'raw logging'. This is implemented as an io.Writer that will
81split incoming bytes into newline-delimited lines, and log them into that logtree's DN. This mechanism is primarily
82intended to support storage of unstructured log data from external processes - for example binaries running with redirected
83stdout/stderr.
84
Tim Windelschmidt99e15112025-02-05 17:38:16 +010085# Log Access API
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020086
87The Log Access API is mostly exposed via a single function on the LogTree struct: Read. It allows access to log entries
88that have been already buffered inside LogTree and to subscribe to receive future entries over a channel. As outlined
89earlier, any access can specify whether it is just interested in a single logger (addressed by DN), or a subtree of
90loggers.
91
92Due to the current implementation of the logtree, subtree accesses of backlogged data is significantly slower than
93accessing data of just one DN, or the whole tree (as every subtree backlog access performs a scan on all logged data).
94Thus, log consumers should be aware that it is much better to stream and buffer logs specific to some long-standing
95logging request on their own, rather than repeatedly perform reads of a subtree backlog.
96
Serge Bazanski12971d62020-11-17 12:12:58 +010097The data returned from the log access API is a LogEntry, which itself can contain either a raw logging entry, or a leveled
98logging entry. Helper functions are available on LogEntry that allow canonical string representations to be returned, for
99easy use in consuming tools/interfaces. Alternatively, the consumer can itself access the internal raw/leveled entries and
100print them according to their own preferred format.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200101*/
102package logtree