blob: 0b945e35709169a0cb81d5d28f762bd1754ff682 [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 Bazanski31370b02021-01-07 16:31:14 +010026 "source.monogon.dev/metropolis/pkg/logbuffer"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020027)
28
Serge Bazanskid9775a62022-02-15 13:28:55 +010029type leveledPublisher struct {
30 node *node
31 depth int
32}
33
Serge Bazanski216fe7b2021-05-21 18:36:16 +020034// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error
35// may be returned if the DN is malformed.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020036func (l *LogTree) LeveledFor(dn DN) (LeveledLogger, error) {
Serge Bazanskid9775a62022-02-15 13:28:55 +010037 node, err := l.nodeByDN(dn)
38 if err != nil {
39 return nil, err
40 }
41 return &leveledPublisher{
42 node: node,
43 depth: 0,
44 }, nil
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020045}
46
Serge Bazanskif68153c2020-10-26 13:54:37 +010047func (l *LogTree) RawFor(dn DN) (io.Writer, error) {
48 node, err := l.nodeByDN(dn)
49 if err != nil {
50 return nil, fmt.Errorf("could not retrieve raw logger: %w", err)
51 }
52 return node.rawLineBuffer, nil
53}
54
Serge Bazanski216fe7b2021-05-21 18:36:16 +020055// MustLeveledFor returns a LeveledLogger publishing interface for a given DN, or
56// panics if the given DN is invalid.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020057func (l *LogTree) MustLeveledFor(dn DN) LeveledLogger {
58 leveled, err := l.LeveledFor(dn)
59 if err != nil {
60 panic(fmt.Errorf("LeveledFor returned: %w", err))
61 }
62 return leveled
63}
64
Serge Bazanskif68153c2020-10-26 13:54:37 +010065func (l *LogTree) MustRawFor(dn DN) io.Writer {
66 raw, err := l.RawFor(dn)
67 if err != nil {
68 panic(fmt.Errorf("RawFor returned: %w", err))
69 }
70 return raw
71}
72
Serge Bazanski216fe7b2021-05-21 18:36:16 +020073// SetVerbosity sets the verbosity for a given DN (non-recursively, ie. for that DN
74// only, not its children).
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020075func (l *LogTree) SetVerbosity(dn DN, level VerbosityLevel) error {
76 node, err := l.nodeByDN(dn)
77 if err != nil {
78 return err
79 }
80 node.verbosity = level
81 return nil
82}
83
Serge Bazanski216fe7b2021-05-21 18:36:16 +020084// logRaw is called by this node's LineBuffer any time a raw log line is completed.
85// It will create a new entry, append it to the journal, and notify all pertinent
86// subscribers.
Serge Bazanskif68153c2020-10-26 13:54:37 +010087func (n *node) logRaw(line *logbuffer.Line) {
88 e := &entry{
89 origin: n.dn,
90 raw: line,
91 }
92 n.tree.journal.append(e)
93 n.tree.journal.notify(e)
94}
95
Serge Bazanski020b7c52021-07-07 14:22:28 +020096// LogExternalLeveled injects a ExternalLeveledPayload into a given
97// LeveledLogger. This should only be used by systems which translate external
98// data sources into leveled logging - see ExternelLeveledPayload for more
99// information.
100func LogExternalLeveled(l LeveledLogger, e *ExternalLeveledPayload) error {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100101 publisher, ok := l.(*leveledPublisher)
Serge Bazanski020b7c52021-07-07 14:22:28 +0200102 if !ok {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100103 return fmt.Errorf("the given LeveledLogger is not a *leveledPublisher")
Serge Bazanski020b7c52021-07-07 14:22:28 +0200104 }
105 p := e.sanitize()
106 entry := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100107 origin: publisher.node.dn,
Serge Bazanski020b7c52021-07-07 14:22:28 +0200108 leveled: p,
109 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100110 publisher.node.tree.journal.append(entry)
111 publisher.node.tree.journal.notify(entry)
Serge Bazanski020b7c52021-07-07 14:22:28 +0200112 return nil
113}
114
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200115// log builds a LeveledPayload and entry for a given message, including all related
116// metadata. It will create a new entry append it to the journal, and notify all
117// pertinent subscribers.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100118func (l *leveledPublisher) logLeveled(depth int, severity Severity, msg string) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200119 _, file, line, ok := runtime.Caller(2 + depth)
120 if !ok {
121 file = "???"
122 line = 1
123 } else {
124 slash := strings.LastIndex(file, "/")
125 if slash >= 0 {
126 file = file[slash+1:]
127 }
128 }
Serge Bazanskib0272182020-11-02 18:39:44 +0100129
Serge Bazanski12971d62020-11-17 12:12:58 +0100130 // Remove leading/trailing newlines and split.
131 messages := strings.Split(strings.Trim(msg, "\n"), "\n")
132
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200133 p := &LeveledPayload{
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200134 timestamp: time.Now(),
135 severity: severity,
Serge Bazanski12971d62020-11-17 12:12:58 +0100136 messages: messages,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200137 file: file,
138 line: line,
139 }
140 e := &entry{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100141 origin: l.node.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200142 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200143 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100144 l.node.tree.journal.append(e)
145 l.node.tree.journal.notify(e)
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200146}
147
148// Info implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100149func (l *leveledPublisher) Info(args ...interface{}) {
150 l.logLeveled(l.depth, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200151}
152
153// Infof implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100154func (l *leveledPublisher) Infof(format string, args ...interface{}) {
155 l.logLeveled(l.depth, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200156}
157
158// Warning implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100159func (l *leveledPublisher) Warning(args ...interface{}) {
160 l.logLeveled(l.depth, WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200161}
162
163// Warningf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100164func (l *leveledPublisher) Warningf(format string, args ...interface{}) {
165 l.logLeveled(l.depth, WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200166}
167
168// Error implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100169func (l *leveledPublisher) Error(args ...interface{}) {
170 l.logLeveled(l.depth, ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200171}
172
173// Errorf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100174func (l *leveledPublisher) Errorf(format string, args ...interface{}) {
175 l.logLeveled(l.depth, ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200176}
177
178// Fatal implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100179func (l *leveledPublisher) Fatal(args ...interface{}) {
180 l.logLeveled(l.depth, FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200181}
182
183// Fatalf implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100184func (l *leveledPublisher) Fatalf(format string, args ...interface{}) {
185 l.logLeveled(l.depth, FATAL, fmt.Sprintf(format, args...))
186}
187
188// WithAddedStackDepth impleemnts the LeveledLogger interface.
189func (l *leveledPublisher) WithAddedStackDepth(depth int) LeveledLogger {
190 l2 := *l
191 l2.depth += depth
192 return &l2
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200193}
194
195// V implements the LeveledLogger interface.
Serge Bazanskid9775a62022-02-15 13:28:55 +0100196func (l *leveledPublisher) V(v VerbosityLevel) VerboseLeveledLogger {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200197 return &verbose{
Serge Bazanskid9775a62022-02-15 13:28:55 +0100198 publisher: l,
199 enabled: l.node.verbosity >= v,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200200 }
201}
202
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200203// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
204// around node, with an 'enabled' bool. This means that V(n)-returned
205// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
206// affect all already existing VerboseLeveledLoggers.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200207type verbose struct {
Serge Bazanskid9775a62022-02-15 13:28:55 +0100208 publisher *leveledPublisher
209 node *node
210 enabled bool
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200211}
212
213func (v *verbose) Enabled() bool {
214 return v.enabled
215}
216
217func (v *verbose) Info(args ...interface{}) {
218 if !v.enabled {
219 return
220 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100221 v.publisher.logLeveled(v.publisher.depth, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200222}
223
224func (v *verbose) Infof(format string, args ...interface{}) {
225 if !v.enabled {
226 return
227 }
Serge Bazanskid9775a62022-02-15 13:28:55 +0100228 v.publisher.logLeveled(v.publisher.depth, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200229}