blob: 9d196bcacf7ca41183c6a989ef36a3ca1bdd13e3 [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
4package logtree
5
6import (
7 "fmt"
Serge Bazanskif68153c2020-10-26 13:54:37 +01008 "io"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02009 "runtime"
10 "strings"
11 "time"
Serge Bazanskif68153c2020-10-26 13:54:37 +010012
Serge Bazanski3c5d0632024-09-12 10:49:12 +000013 "source.monogon.dev/go/logging"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020014 "source.monogon.dev/osbase/logbuffer"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020015)
16
Serge Bazanskid9775a62022-02-15 13:28:55 +010017type leveledPublisher struct {
18 node *node
19 depth int
20}
21
Serge Bazanski216fe7b2021-05-21 18:36:16 +020022// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error
23// may be returned if the DN is malformed.
Serge Bazanski3c5d0632024-09-12 10:49:12 +000024func (l *LogTree) LeveledFor(dn DN) (logging.Leveled, error) {
Serge Bazanskid9775a62022-02-15 13:28:55 +010025 node, err := l.nodeByDN(dn)
26 if err != nil {
27 return nil, err
28 }
29 return &leveledPublisher{
30 node: node,
31 depth: 0,
32 }, nil
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020033}
34
Serge Bazanskif68153c2020-10-26 13:54:37 +010035func (l *LogTree) RawFor(dn DN) (io.Writer, error) {
36 node, err := l.nodeByDN(dn)
37 if err != nil {
38 return nil, fmt.Errorf("could not retrieve raw logger: %w", err)
39 }
40 return node.rawLineBuffer, nil
41}
42
Serge Bazanski216fe7b2021-05-21 18:36:16 +020043// MustLeveledFor returns a LeveledLogger publishing interface for a given DN, or
44// panics if the given DN is invalid.
Serge Bazanski3c5d0632024-09-12 10:49:12 +000045func (l *LogTree) MustLeveledFor(dn DN) logging.Leveled {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020046 leveled, err := l.LeveledFor(dn)
47 if err != nil {
48 panic(fmt.Errorf("LeveledFor returned: %w", err))
49 }
50 return leveled
51}
52
Serge Bazanskif68153c2020-10-26 13:54:37 +010053func (l *LogTree) MustRawFor(dn DN) io.Writer {
54 raw, err := l.RawFor(dn)
55 if err != nil {
56 panic(fmt.Errorf("RawFor returned: %w", err))
57 }
58 return raw
59}
60
Serge Bazanski216fe7b2021-05-21 18:36:16 +020061// SetVerbosity sets the verbosity for a given DN (non-recursively, ie. for that DN
62// only, not its children).
Serge Bazanski3c5d0632024-09-12 10:49:12 +000063func (l *LogTree) SetVerbosity(dn DN, level logging.VerbosityLevel) error {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020064 node, err := l.nodeByDN(dn)
65 if err != nil {
66 return err
67 }
68 node.verbosity = level
69 return nil
70}
71
Serge Bazanski216fe7b2021-05-21 18:36:16 +020072// logRaw is called by this node's LineBuffer any time a raw log line is completed.
73// It will create a new entry, append it to the journal, and notify all pertinent
74// subscribers.
Serge Bazanskif68153c2020-10-26 13:54:37 +010075func (n *node) logRaw(line *logbuffer.Line) {
76 e := &entry{
77 origin: n.dn,
78 raw: line,
79 }
80 n.tree.journal.append(e)
81 n.tree.journal.notify(e)
82}
83
Serge Bazanski020b7c52021-07-07 14:22:28 +020084// LogExternalLeveled injects a ExternalLeveledPayload into a given
85// LeveledLogger. This should only be used by systems which translate external
86// data sources into leveled logging - see ExternelLeveledPayload for more
87// information.
Serge Bazanski3c5d0632024-09-12 10:49:12 +000088func LogExternalLeveled(l logging.Leveled, e *ExternalLeveledPayload) error {
Serge Bazanskid9775a62022-02-15 13:28:55 +010089 publisher, ok := l.(*leveledPublisher)
Serge Bazanski020b7c52021-07-07 14:22:28 +020090 if !ok {
Serge Bazanskid9775a62022-02-15 13:28:55 +010091 return fmt.Errorf("the given LeveledLogger is not a *leveledPublisher")
Serge Bazanski020b7c52021-07-07 14:22:28 +020092 }
93 p := e.sanitize()
94 entry := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +010095 origin: publisher.node.dn,
Serge Bazanski020b7c52021-07-07 14:22:28 +020096 leveled: p,
97 }
Serge Bazanskid9775a62022-02-15 13:28:55 +010098 publisher.node.tree.journal.append(entry)
99 publisher.node.tree.journal.notify(entry)
Serge Bazanski020b7c52021-07-07 14:22:28 +0200100 return nil
101}
102
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200103// log builds a LeveledPayload and entry for a given message, including all related
104// metadata. It will create a new entry append it to the journal, and notify all
105// pertinent subscribers.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000106func (l *leveledPublisher) logLeveled(depth int, severity logging.Severity, msg string) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200107 _, file, line, ok := runtime.Caller(2 + depth)
108 if !ok {
109 file = "???"
110 line = 1
111 } else {
112 slash := strings.LastIndex(file, "/")
113 if slash >= 0 {
114 file = file[slash+1:]
115 }
116 }
Serge Bazanskib0272182020-11-02 18:39:44 +0100117
Serge Bazanski12971d62020-11-17 12:12:58 +0100118 // Remove leading/trailing newlines and split.
119 messages := strings.Split(strings.Trim(msg, "\n"), "\n")
120
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200121 p := &LeveledPayload{
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200122 timestamp: time.Now(),
123 severity: severity,
Serge Bazanski12971d62020-11-17 12:12:58 +0100124 messages: messages,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200125 file: file,
126 line: line,
127 }
128 e := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100129 origin: l.node.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200130 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200131 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100132 l.node.tree.journal.append(e)
133 l.node.tree.journal.notify(e)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200134}
135
136// Info implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100137func (l *leveledPublisher) Info(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000138 l.logLeveled(l.depth, logging.INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200139}
140
141// Infof implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100142func (l *leveledPublisher) Infof(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000143 l.logLeveled(l.depth, logging.INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200144}
145
146// Warning implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100147func (l *leveledPublisher) Warning(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000148 l.logLeveled(l.depth, logging.WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200149}
150
151// Warningf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100152func (l *leveledPublisher) Warningf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000153 l.logLeveled(l.depth, logging.WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200154}
155
156// Error implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100157func (l *leveledPublisher) Error(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000158 l.logLeveled(l.depth, logging.ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200159}
160
161// Errorf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100162func (l *leveledPublisher) Errorf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000163 l.logLeveled(l.depth, logging.ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200164}
165
166// Fatal implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100167func (l *leveledPublisher) Fatal(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000168 l.logLeveled(l.depth, logging.FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200169}
170
171// Fatalf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100172func (l *leveledPublisher) Fatalf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000173 l.logLeveled(l.depth, logging.FATAL, fmt.Sprintf(format, args...))
Serge Bazanskid9775a62022-02-15 13:28:55 +0100174}
175
176// WithAddedStackDepth impleemnts the LeveledLogger interface.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000177func (l *leveledPublisher) WithAddedStackDepth(depth int) logging.Leveled {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100178 l2 := *l
179 l2.depth += depth
180 return &l2
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200181}
182
183// V implements the LeveledLogger interface.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000184func (l *leveledPublisher) V(v logging.VerbosityLevel) logging.VerboseLeveled {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200185 return &verbose{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100186 publisher: l,
187 enabled: l.node.verbosity >= v,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200188 }
189}
190
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200191// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
192// around node, with an 'enabled' bool. This means that V(n)-returned
193// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
194// affect all already existing VerboseLeveledLoggers.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200195type verbose struct {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100196 publisher *leveledPublisher
197 node *node
198 enabled bool
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200199}
200
201func (v *verbose) Enabled() bool {
202 return v.enabled
203}
204
205func (v *verbose) Info(args ...interface{}) {
206 if !v.enabled {
207 return
208 }
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000209 v.publisher.logLeveled(v.publisher.depth, logging.INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200210}
211
212func (v *verbose) Infof(format string, args ...interface{}) {
213 if !v.enabled {
214 return
215 }
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000216 v.publisher.logLeveled(v.publisher.depth, logging.INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200217}