blob: 3e2711adc6e0fbe82987853c1608962f0c3c3767 [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 Bazanski549b72b2021-01-07 14:54:19 +010026 "git.monogon.dev/source/nexantic.git/metropolis/pkg/logbuffer"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020027)
28
29// LeveledFor returns a LeveledLogger publishing interface for a given DN. An error may be returned if the DN is
30// malformed.
31func (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 Bazanski5faa2fc2020-09-07 14:09:30 +020043// MustLeveledFor returns a LeveledLogger publishing interface for a given DN, or panics if the given DN is invalid.
44func (l *LogTree) MustLeveledFor(dn DN) LeveledLogger {
45 leveled, err := l.LeveledFor(dn)
46 if err != nil {
47 panic(fmt.Errorf("LeveledFor returned: %w", err))
48 }
49 return leveled
50}
51
Serge Bazanskif68153c2020-10-26 13:54:37 +010052func (l *LogTree) MustRawFor(dn DN) io.Writer {
53 raw, err := l.RawFor(dn)
54 if err != nil {
55 panic(fmt.Errorf("RawFor returned: %w", err))
56 }
57 return raw
58}
59
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020060// SetVerbosity sets the verbosity for a given DN (non-recursively, ie. for that DN only, not its children).
61func (l *LogTree) SetVerbosity(dn DN, level VerbosityLevel) error {
62 node, err := l.nodeByDN(dn)
63 if err != nil {
64 return err
65 }
66 node.verbosity = level
67 return nil
68}
69
Serge Bazanskif68153c2020-10-26 13:54:37 +010070// logRaw is called by this node's LineBuffer any time a raw log line is completed. It will create a new entry, append
71// it to the journal, and notify all pertinent subscribers.
72func (n *node) logRaw(line *logbuffer.Line) {
73 e := &entry{
74 origin: n.dn,
75 raw: line,
76 }
77 n.tree.journal.append(e)
78 n.tree.journal.notify(e)
79}
80
81// log builds a LeveledPayload and entry for a given message, including all related metadata. It will create a new
82// entry append it to the journal, and notify all pertinent subscribers.
83func (n *node) logLeveled(depth int, severity Severity, msg string) {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020084 _, file, line, ok := runtime.Caller(2 + depth)
85 if !ok {
86 file = "???"
87 line = 1
88 } else {
89 slash := strings.LastIndex(file, "/")
90 if slash >= 0 {
91 file = file[slash+1:]
92 }
93 }
Serge Bazanskib0272182020-11-02 18:39:44 +010094
Serge Bazanski12971d62020-11-17 12:12:58 +010095 // Remove leading/trailing newlines and split.
96 messages := strings.Split(strings.Trim(msg, "\n"), "\n")
97
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020098 p := &LeveledPayload{
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020099 timestamp: time.Now(),
100 severity: severity,
Serge Bazanski12971d62020-11-17 12:12:58 +0100101 messages: messages,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200102 file: file,
103 line: line,
104 }
105 e := &entry{
106 origin: n.dn,
Serge Bazanski1bfa0c22020-10-14 16:45:07 +0200107 leveled: p,
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200108 }
109 n.tree.journal.append(e)
110 n.tree.journal.notify(e)
111}
112
113// Info implements the LeveledLogger interface.
114func (n *node) Info(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100115 n.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200116}
117
118// Infof implements the LeveledLogger interface.
119func (n *node) Infof(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100120 n.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200121}
122
123// Warning implements the LeveledLogger interface.
124func (n *node) Warning(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100125 n.logLeveled(0, WARNING, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200126}
127
128// Warningf implements the LeveledLogger interface.
129func (n *node) Warningf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100130 n.logLeveled(0, WARNING, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200131}
132
133// Error implements the LeveledLogger interface.
134func (n *node) Error(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100135 n.logLeveled(0, ERROR, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200136}
137
138// Errorf implements the LeveledLogger interface.
139func (n *node) Errorf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100140 n.logLeveled(0, ERROR, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200141}
142
143// Fatal implements the LeveledLogger interface.
144func (n *node) Fatal(args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100145 n.logLeveled(0, FATAL, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200146}
147
148// Fatalf implements the LeveledLogger interface.
149func (n *node) Fatalf(format string, args ...interface{}) {
Serge Bazanskif68153c2020-10-26 13:54:37 +0100150 n.logLeveled(0, FATAL, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200151}
152
153// V implements the LeveledLogger interface.
154func (n *node) V(v VerbosityLevel) VerboseLeveledLogger {
155 return &verbose{
156 node: n,
157 enabled: n.verbosity >= v,
158 }
159}
160
161// verbose implements the VerboseLeveledLogger interface. It is a thin wrapper around node, with an 'enabled' bool. This
162// means that V(n)-returned VerboseLeveledLoggers must be short lived, as a changed in verbosity will not affect all
163// already existing VerboseLeveledLoggers.
164type verbose struct {
165 node *node
166 enabled bool
167}
168
169func (v *verbose) Enabled() bool {
170 return v.enabled
171}
172
173func (v *verbose) Info(args ...interface{}) {
174 if !v.enabled {
175 return
176 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100177 v.node.logLeveled(0, INFO, fmt.Sprint(args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200178}
179
180func (v *verbose) Infof(format string, args ...interface{}) {
181 if !v.enabled {
182 return
183 }
Serge Bazanskif68153c2020-10-26 13:54:37 +0100184 v.node.logLeveled(0, INFO, fmt.Sprintf(format, args...))
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200185}