blob: ab3c537dd214117ec6124103e9ef1c849ddaacf3 [file] [log] [blame]
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17/*
18Package 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 +010019glog-like API and io.Writer API, with loggers placed in a hierarchical structure defined by a dot-delimited path
20(called a DN, short for Distinguished Name).
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020021
22 tree.MustLeveledFor("foo.bar.baz").Warningf("Houston, we have a problem: %v", err)
Serge Bazanski12971d62020-11-17 12:12:58 +010023 fmt.Fprintf(tree.MustRawFor("foo.bar.baz"), "some\nunstructured\ndata\n")
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020024
25Logs in this context are unstructured, operational and developer-centric human readable text messages presented as lines
26of text to consumers, with some attached metadata. Logtree does not deal with 'structured' logs as some parts of the
27industry do, and instead defers any machine-readable logs to either be handled by metrics systems like Prometheus or
28event sourcing systems like Kafka.
29
30Tree Structure
31
Serge Bazanski06d65bc2020-09-24 10:51:59 +020032As an example, consider an application that produces logs with the following DNs:
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020033
34 listener.http
Serge Bazanski06d65bc2020-09-24 10:51:59 +020035 listener.grpc
36 svc
37 svc.cache
38 svc.cache.gc
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020039
40This would correspond to a tree as follows:
41
42 .------.
43 | "" |
44 | (root) |
45 '------'
46 .----------------' '------.
47 .--------------. .---------------.
48 | svc | | listener |
49 '--------------' '---------------'
50 | .----' '----.
51 .--------------. .---------------. .---------------.
52 | svc.cache | | listener.http | | listener.grpc |
53 '--------------' '---------------' '---------------'
54 |
55 .--------------.
56 | svc.cache.gc |
57 '--------------'
58
59In this setup, every DN acts as a separate logging target, each with its own retention policy and quota. Logging to a DN
60under foo.bar does NOT automatically log to foo - all tree mechanisms are applied on log access by consumers. Loggers
61are automatically created on first use, and importantly, can be created at any time, and will automatically be created
62if a sub-DN is created that requires a parent DN to exist first. Note, for instance, that a `listener` logging node was
63created even though the example application only logged to `listener.http` and `listener.grpc`.
64
65An implicit root node is always present in the tree, accessed by DN "" (an empty string). All other logger nodes are
66children (or transitive children) of the root node.
67
68Log consumers (application code that reads the log and passes them on to operators, or ships them off for aggregation in
69other systems) to select subtrees of logs for readout. In the example tree, a consumer could select to either read all
70logs of the entire tree, just a single DN (like svc), or a subtree (like everything under listener, ie. messages emitted
71to listener.http and listener.grpc).
72
Serge Bazanski12971d62020-11-17 12:12:58 +010073Leveled Log Producer API
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020074
75As part of the glog-like logging API available to producers, the following metadata is attached to emitted logs in
76addition to the DN of the logger to which the log entry was emitted:
77
78 - timestamp at which the entry was emitted
79 - a severity level (one of FATAL, ERROR, WARN or INFO)
80 - a source of the message (file name and line number)
81
82In addition, the logger mechanism supports a variable verbosity level (so-called 'V-logging') that can be set at every
83node of the tree. For more information about the producer-facing logging API, see the documentation of the LeveledLogger
84interface, which is the main interface exposed to log producers.
85
Serge Bazanski12971d62020-11-17 12:12:58 +010086If the submitted message contains newlines, it will be split accordingly into a single log entry that contains multiple
87string lines. This allows for log producers to submit long, multi-line messages that are guaranteed to be non-interleaved
88with other entries, and allows for access API consumers to maintain semantic linking between multiple lines being emitted
89as a single atomic entry.
90
91Raw Log Producer API
92
93In addition to leveled, glog-like logging, LogTree supports 'raw logging'. This is implemented as an io.Writer that will
94split incoming bytes into newline-delimited lines, and log them into that logtree's DN. This mechanism is primarily
95intended to support storage of unstructured log data from external processes - for example binaries running with redirected
96stdout/stderr.
97
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020098Log Access API
99
100The Log Access API is mostly exposed via a single function on the LogTree struct: Read. It allows access to log entries
101that have been already buffered inside LogTree and to subscribe to receive future entries over a channel. As outlined
102earlier, any access can specify whether it is just interested in a single logger (addressed by DN), or a subtree of
103loggers.
104
105Due to the current implementation of the logtree, subtree accesses of backlogged data is significantly slower than
106accessing data of just one DN, or the whole tree (as every subtree backlog access performs a scan on all logged data).
107Thus, log consumers should be aware that it is much better to stream and buffer logs specific to some long-standing
108logging request on their own, rather than repeatedly perform reads of a subtree backlog.
109
Serge Bazanski12971d62020-11-17 12:12:58 +0100110The data returned from the log access API is a LogEntry, which itself can contain either a raw logging entry, or a leveled
111logging entry. Helper functions are available on LogEntry that allow canonical string representations to be returned, for
112easy use in consuming tools/interfaces. Alternatively, the consumer can itself access the internal raw/leveled entries and
113print them according to their own preferred format.
114
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200115*/
116package logtree