blob: 6106b1953b067cb198588b44b4069980f8ae3091 [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 Bazanski216fe7b2021-05-21 18:36:16 +020029// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error
30// may be returned if the DN is malformed.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020031func (l *LogTree) LeveledFor(dn DN) (LeveledLogger, error) {
32 return l.nodeByDN(dn)
33}
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 Bazanski5faa2fc2020-09-07 14:09:30 +020045func (l *LogTree) MustLeveledFor(dn DN) LeveledLogger {
46 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 Bazanski5faa2fc2020-09-07 14:09:30 +020063func (l *LogTree) SetVerbosity(dn DN, level VerbosityLevel) error {
64 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 Bazanski216fe7b2021-05-21 18:36:16 +020084// log builds a LeveledPayload and entry for a given message, including all related
85// metadata. It will create a new entry append it to the journal, and notify all
86// pertinent subscribers.
Serge Bazanskif68153c2020-10-26 13:54:37 +010087func (n *node) logLeveled(depth int, severity Severity, msg string) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020088 _, file, line, ok := runtime.Caller(2 + depth)
89 if !ok {
90 file = "???"
91 line = 1
92 } else {
93 slash := strings.LastIndex(file, "/")
94 if slash >= 0 {
95 file = file[slash+1:]
96 }
97 }
Serge Bazanskib0272182020-11-02 18:39:44 +010098
Serge Bazanski12971d62020-11-17 12:12:58 +010099 // Remove leading/trailing newlines and split.
100 messages := strings.Split(strings.Trim(msg, "\n"), "\n")
101
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200102 p := &LeveledPayload{
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200103 timestamp: time.Now(),
104 severity: severity,
Serge Bazanski12971d62020-11-17 12:12:58 +0100105 messages: messages,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200106 file: file,
107 line: line,
108 }
109 e := &entry{
110 origin: n.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200111 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200112 }
113 n.tree.journal.append(e)
114 n.tree.journal.notify(e)
115}
116
117// Info implements the LeveledLogger interface.
118func (n *node) Info(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100119 n.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200120}
121
122// Infof implements the LeveledLogger interface.
123func (n *node) Infof(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100124 n.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200125}
126
127// Warning implements the LeveledLogger interface.
128func (n *node) Warning(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100129 n.logLeveled(0, WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200130}
131
132// Warningf implements the LeveledLogger interface.
133func (n *node) Warningf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100134 n.logLeveled(0, WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200135}
136
137// Error implements the LeveledLogger interface.
138func (n *node) Error(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100139 n.logLeveled(0, ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200140}
141
142// Errorf implements the LeveledLogger interface.
143func (n *node) Errorf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100144 n.logLeveled(0, ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200145}
146
147// Fatal implements the LeveledLogger interface.
148func (n *node) Fatal(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100149 n.logLeveled(0, FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200150}
151
152// Fatalf implements the LeveledLogger interface.
153func (n *node) Fatalf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100154 n.logLeveled(0, FATAL, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200155}
156
157// V implements the LeveledLogger interface.
158func (n *node) V(v VerbosityLevel) VerboseLeveledLogger {
159 return &verbose{
160 node: n,
161 enabled: n.verbosity >= v,
162 }
163}
164
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200165// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
166// around node, with an 'enabled' bool. This means that V(n)-returned
167// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
168// affect all already existing VerboseLeveledLoggers.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200169type verbose struct {
170 node *node
171 enabled bool
172}
173
174func (v *verbose) Enabled() bool {
175 return v.enabled
176}
177
178func (v *verbose) Info(args ...interface{}) {
179 if !v.enabled {
180 return
181 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100182 v.node.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200183}
184
185func (v *verbose) Infof(format string, args ...interface{}) {
186 if !v.enabled {
187 return
188 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100189 v.node.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200190}