blob: 2df70374377af73e59c7108e1e37b91cd9b8cb5f [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
17package logtree
18
19import (
20 "fmt"
Serge Bazanskif68153c2020-10-26 13:54:37 +010021 "io"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020022 "runtime"
23 "strings"
24 "time"
Serge Bazanskif68153c2020-10-26 13:54:37 +010025
Serge Bazanski3c5d0632024-09-12 10:49:12 +000026 "source.monogon.dev/go/logging"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020027 "source.monogon.dev/osbase/logbuffer"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020028)
29
Serge Bazanskid9775a62022-02-15 13:28:55 +010030type leveledPublisher struct {
31 node *node
32 depth int
33}
34
Serge Bazanski216fe7b2021-05-21 18:36:16 +020035// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error
36// may be returned if the DN is malformed.
Serge Bazanski3c5d0632024-09-12 10:49:12 +000037func (l *LogTree) LeveledFor(dn DN) (logging.Leveled, error) {
Serge Bazanskid9775a62022-02-15 13:28:55 +010038 node, err := l.nodeByDN(dn)
39 if err != nil {
40 return nil, err
41 }
42 return &leveledPublisher{
43 node: node,
44 depth: 0,
45 }, nil
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020046}
47
Serge Bazanskif68153c2020-10-26 13:54:37 +010048func (l *LogTree) RawFor(dn DN) (io.Writer, error) {
49 node, err := l.nodeByDN(dn)
50 if err != nil {
51 return nil, fmt.Errorf("could not retrieve raw logger: %w", err)
52 }
53 return node.rawLineBuffer, nil
54}
55
Serge Bazanski216fe7b2021-05-21 18:36:16 +020056// MustLeveledFor returns a LeveledLogger publishing interface for a given DN, or
57// panics if the given DN is invalid.
Serge Bazanski3c5d0632024-09-12 10:49:12 +000058func (l *LogTree) MustLeveledFor(dn DN) logging.Leveled {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020059 leveled, err := l.LeveledFor(dn)
60 if err != nil {
61 panic(fmt.Errorf("LeveledFor returned: %w", err))
62 }
63 return leveled
64}
65
Serge Bazanskif68153c2020-10-26 13:54:37 +010066func (l *LogTree) MustRawFor(dn DN) io.Writer {
67 raw, err := l.RawFor(dn)
68 if err != nil {
69 panic(fmt.Errorf("RawFor returned: %w", err))
70 }
71 return raw
72}
73
Serge Bazanski216fe7b2021-05-21 18:36:16 +020074// SetVerbosity sets the verbosity for a given DN (non-recursively, ie. for that DN
75// only, not its children).
Serge Bazanski3c5d0632024-09-12 10:49:12 +000076func (l *LogTree) SetVerbosity(dn DN, level logging.VerbosityLevel) error {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020077 node, err := l.nodeByDN(dn)
78 if err != nil {
79 return err
80 }
81 node.verbosity = level
82 return nil
83}
84
Serge Bazanski216fe7b2021-05-21 18:36:16 +020085// logRaw is called by this node's LineBuffer any time a raw log line is completed.
86// It will create a new entry, append it to the journal, and notify all pertinent
87// subscribers.
Serge Bazanskif68153c2020-10-26 13:54:37 +010088func (n *node) logRaw(line *logbuffer.Line) {
89 e := &entry{
90 origin: n.dn,
91 raw: line,
92 }
93 n.tree.journal.append(e)
94 n.tree.journal.notify(e)
95}
96
Serge Bazanski020b7c52021-07-07 14:22:28 +020097// LogExternalLeveled injects a ExternalLeveledPayload into a given
98// LeveledLogger. This should only be used by systems which translate external
99// data sources into leveled logging - see ExternelLeveledPayload for more
100// information.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000101func LogExternalLeveled(l logging.Leveled, e *ExternalLeveledPayload) error {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100102 publisher, ok := l.(*leveledPublisher)
Serge Bazanski020b7c52021-07-07 14:22:28 +0200103 if !ok {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100104 return fmt.Errorf("the given LeveledLogger is not a *leveledPublisher")
Serge Bazanski020b7c52021-07-07 14:22:28 +0200105 }
106 p := e.sanitize()
107 entry := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100108 origin: publisher.node.dn,
Serge Bazanski020b7c52021-07-07 14:22:28 +0200109 leveled: p,
110 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100111 publisher.node.tree.journal.append(entry)
112 publisher.node.tree.journal.notify(entry)
Serge Bazanski020b7c52021-07-07 14:22:28 +0200113 return nil
114}
115
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200116// log builds a LeveledPayload and entry for a given message, including all related
117// metadata. It will create a new entry append it to the journal, and notify all
118// pertinent subscribers.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000119func (l *leveledPublisher) logLeveled(depth int, severity logging.Severity, msg string) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200120 _, file, line, ok := runtime.Caller(2 + depth)
121 if !ok {
122 file = "???"
123 line = 1
124 } else {
125 slash := strings.LastIndex(file, "/")
126 if slash >= 0 {
127 file = file[slash+1:]
128 }
129 }
Serge Bazanskib0272182020-11-02 18:39:44 +0100130
Serge Bazanski12971d62020-11-17 12:12:58 +0100131 // Remove leading/trailing newlines and split.
132 messages := strings.Split(strings.Trim(msg, "\n"), "\n")
133
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200134 p := &LeveledPayload{
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200135 timestamp: time.Now(),
136 severity: severity,
Serge Bazanski12971d62020-11-17 12:12:58 +0100137 messages: messages,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200138 file: file,
139 line: line,
140 }
141 e := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100142 origin: l.node.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200143 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200144 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100145 l.node.tree.journal.append(e)
146 l.node.tree.journal.notify(e)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200147}
148
149// Info implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100150func (l *leveledPublisher) Info(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000151 l.logLeveled(l.depth, logging.INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200152}
153
154// Infof implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100155func (l *leveledPublisher) Infof(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000156 l.logLeveled(l.depth, logging.INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200157}
158
159// Warning implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100160func (l *leveledPublisher) Warning(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000161 l.logLeveled(l.depth, logging.WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200162}
163
164// Warningf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100165func (l *leveledPublisher) Warningf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000166 l.logLeveled(l.depth, logging.WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200167}
168
169// Error implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100170func (l *leveledPublisher) Error(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000171 l.logLeveled(l.depth, logging.ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200172}
173
174// Errorf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100175func (l *leveledPublisher) Errorf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000176 l.logLeveled(l.depth, logging.ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200177}
178
179// Fatal implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100180func (l *leveledPublisher) Fatal(args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000181 l.logLeveled(l.depth, logging.FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200182}
183
184// Fatalf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100185func (l *leveledPublisher) Fatalf(format string, args ...interface{}) {
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000186 l.logLeveled(l.depth, logging.FATAL, fmt.Sprintf(format, args...))
Serge Bazanskid9775a62022-02-15 13:28:55 +0100187}
188
189// WithAddedStackDepth impleemnts the LeveledLogger interface.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000190func (l *leveledPublisher) WithAddedStackDepth(depth int) logging.Leveled {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100191 l2 := *l
192 l2.depth += depth
193 return &l2
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200194}
195
196// V implements the LeveledLogger interface.
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000197func (l *leveledPublisher) V(v logging.VerbosityLevel) logging.VerboseLeveled {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200198 return &verbose{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100199 publisher: l,
200 enabled: l.node.verbosity >= v,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200201 }
202}
203
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200204// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
205// around node, with an 'enabled' bool. This means that V(n)-returned
206// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
207// affect all already existing VerboseLeveledLoggers.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200208type verbose struct {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100209 publisher *leveledPublisher
210 node *node
211 enabled bool
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200212}
213
214func (v *verbose) Enabled() bool {
215 return v.enabled
216}
217
218func (v *verbose) Info(args ...interface{}) {
219 if !v.enabled {
220 return
221 }
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000222 v.publisher.logLeveled(v.publisher.depth, logging.INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200223}
224
225func (v *verbose) Infof(format string, args ...interface{}) {
226 if !v.enabled {
227 return
228 }
Serge Bazanski3c5d0632024-09-12 10:49:12 +0000229 v.publisher.logLeveled(v.publisher.depth, logging.INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200230}