blob: 25dfc5a350e446ed194441e3794bad2d4a63dd4d [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 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.
88func LogExternalLeveled(l LeveledLogger, e *ExternalLeveledPayload) error {
89 n, ok := l.(*node)
90 if !ok {
91 return fmt.Errorf("the given LeveledLogger is not a logtree node")
92 }
93 p := e.sanitize()
94 entry := &entry{
95 origin: n.dn,
96 leveled: p,
97 }
98 n.tree.journal.append(entry)
99 n.tree.journal.notify(entry)
100 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 Bazanskif68153c2020-10-26 13:54:37 +0100106func (n *node) logLeveled(depth int, severity 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{
129 origin: n.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200130 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200131 }
132 n.tree.journal.append(e)
133 n.tree.journal.notify(e)
134}
135
136// Info implements the LeveledLogger interface.
137func (n *node) Info(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100138 n.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200139}
140
141// Infof implements the LeveledLogger interface.
142func (n *node) Infof(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100143 n.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200144}
145
146// Warning implements the LeveledLogger interface.
147func (n *node) Warning(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100148 n.logLeveled(0, WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200149}
150
151// Warningf implements the LeveledLogger interface.
152func (n *node) Warningf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100153 n.logLeveled(0, WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200154}
155
156// Error implements the LeveledLogger interface.
157func (n *node) Error(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100158 n.logLeveled(0, ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200159}
160
161// Errorf implements the LeveledLogger interface.
162func (n *node) Errorf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100163 n.logLeveled(0, ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200164}
165
166// Fatal implements the LeveledLogger interface.
167func (n *node) Fatal(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100168 n.logLeveled(0, FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200169}
170
171// Fatalf implements the LeveledLogger interface.
172func (n *node) Fatalf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100173 n.logLeveled(0, FATAL, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200174}
175
176// V implements the LeveledLogger interface.
177func (n *node) V(v VerbosityLevel) VerboseLeveledLogger {
178 return &verbose{
179 node: n,
180 enabled: n.verbosity >= v,
181 }
182}
183
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200184// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper
185// around node, with an 'enabled' bool. This means that V(n)-returned
186// VerboseLeveledLoggers must be short lived, as a changed in verbosity will not
187// affect all already existing VerboseLeveledLoggers.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200188type verbose struct {
189 node *node
190 enabled bool
191}
192
193func (v *verbose) Enabled() bool {
194 return v.enabled
195}
196
197func (v *verbose) Info(args ...interface{}) {
198 if !v.enabled {
199 return
200 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100201 v.node.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200202}
203
204func (v *verbose) Infof(format string, args ...interface{}) {
205 if !v.enabled {
206 return
207 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100208 v.node.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200209}