blob: 6e44ef44c466145dc3fb4b97cb3d8d6fce67b9a1 [file] [log] [blame]
Serge Bazanski5ade7322020-08-27 13:27:51 +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
Serge Bazanskib0272182020-11-02 18:39:44 +010019import (
20 "fmt"
21
Serge Bazanski31370b02021-01-07 16:31:14 +010022 apb "source.monogon.dev/metropolis/proto/api"
Serge Bazanskib0272182020-11-02 18:39:44 +010023)
24
Serge Bazanski216fe7b2021-05-21 18:36:16 +020025// LeveledLogger is a generic interface for glog-style logging. There are four
26// hardcoded log severities, in increasing order: INFO, WARNING, ERROR, FATAL.
27// Logging at a certain severity level logs not only to consumers expecting data at
28// that severity level, but also all lower severity levels. For example, an ERROR
29// log will also be passed to consumers looking at INFO or WARNING logs.
Serge Bazanski5ade7322020-08-27 13:27:51 +020030type LeveledLogger interface {
Serge Bazanski216fe7b2021-05-21 18:36:16 +020031 // Info logs at the INFO severity. Arguments are handled in the manner of
32 // fmt.Print, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020033 Info(args ...interface{})
Serge Bazanski216fe7b2021-05-21 18:36:16 +020034 // Infof logs at the INFO severity. Arguments are handled in the manner of
35 // fmt.Printf, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020036 Infof(format string, args ...interface{})
37
Serge Bazanski216fe7b2021-05-21 18:36:16 +020038 // Warning logs at the WARNING severity. Arguments are handled in the manner of
39 // fmt.Print, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020040 Warning(args ...interface{})
Serge Bazanski216fe7b2021-05-21 18:36:16 +020041 // Warningf logs at the WARNING severity. Arguments are handled in the manner of
42 // fmt.Printf, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020043 Warningf(format string, args ...interface{})
44
Serge Bazanski216fe7b2021-05-21 18:36:16 +020045 // Error logs at the ERROR severity. Arguments are handled in the manner of
46 // fmt.Print, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020047 Error(args ...interface{})
Serge Bazanski216fe7b2021-05-21 18:36:16 +020048 // Errorf logs at the ERROR severity. Arguments are handled in the manner of
49 // fmt.Printf, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020050 Errorf(format string, args ...interface{})
51
Serge Bazanski216fe7b2021-05-21 18:36:16 +020052 // Fatal logs at the FATAL severity and aborts the current program. Arguments are
53 // handled in the manner of fmt.Print, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020054 Fatal(args ...interface{})
Serge Bazanski216fe7b2021-05-21 18:36:16 +020055 // Fatalf logs at the FATAL severity and aborts the current program. Arguments are
56 // handled in the manner of fmt.Printf, a terminating newline is added if missing.
Serge Bazanski5ade7322020-08-27 13:27:51 +020057 Fatalf(format string, args ...interface{})
58
Serge Bazanski216fe7b2021-05-21 18:36:16 +020059 // V returns a VerboseLeveledLogger at a given verbosity level. These verbosity
60 // levels can be dynamically set and unset on a package-granular level by consumers
61 // of the LeveledLogger logs. The returned value represents whether logging at the
62 // given verbosity level was active at that time, and as such should not be a long-
63 // lived object in programs. This construct is further refered to as 'V-logs'.
Serge Bazanski5ade7322020-08-27 13:27:51 +020064 V(level VerbosityLevel) VerboseLeveledLogger
Serge Bazanskid9775a62022-02-15 13:28:55 +010065
66 // WithAddedStackDepth returns the same LeveledLogger, but adjusted with an
67 // additional 'extra stack depth' which will be used to skip a given number of
68 // stack/call frames when determining the location where the error originated.
69 // For example, WithStackDepth(1) will return a logger that will skip one
70 // stack/call frame. Then, with function foo() calling function helper() which
71 // in turns call l.Infof(), the log line will be emitted with the call site of
72 // helper() within foo(), instead of the default behaviour of logging the
73 // call site of Infof() within helper().
74 //
75 // This is useful for functions which somehow wrap loggers in helper functions,
76 // for example to expose a slightly different API.
77 WithAddedStackDepth(depth int) LeveledLogger
Serge Bazanski5ade7322020-08-27 13:27:51 +020078}
79
Serge Bazanski216fe7b2021-05-21 18:36:16 +020080// VerbosityLevel is a verbosity level defined for V-logs. This can be changed
81// programmatically per Go package. When logging at a given VerbosityLevel V, the
82// current level must be equal or higher to V for the logs to be recorded.
83// Conversely, enabling a V-logging at a VerbosityLevel V also enables all logging
84// at lower levels [Int32Min .. (V-1)].
Serge Bazanski5ade7322020-08-27 13:27:51 +020085type VerbosityLevel int32
86
87type VerboseLeveledLogger interface {
Serge Bazanski216fe7b2021-05-21 18:36:16 +020088 // Enabled returns if this level was enabled. If not enabled, all logging into this
89 // logger will be discarded immediately. Thus, Enabled() can be used to check the
90 // verbosity level before performing any logging:
Serge Bazanski5ade7322020-08-27 13:27:51 +020091 // if l.V(3).Enabled() { l.Info("V3 is enabled") }
92 // or, in simple cases, the convenience function .Info can be used:
93 // l.V(3).Info("V3 is enabled")
Serge Bazanski216fe7b2021-05-21 18:36:16 +020094 // The second form is shorter and more convenient, but more expensive, as its
95 // arguments are always evaluated.
Serge Bazanski5ade7322020-08-27 13:27:51 +020096 Enabled() bool
Serge Bazanski216fe7b2021-05-21 18:36:16 +020097 // Info is the equivalent of a LeveledLogger's Info call, guarded by whether this
98 // VerboseLeveledLogger is enabled.
Serge Bazanski5ade7322020-08-27 13:27:51 +020099 Info(args ...interface{})
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200100 // Infof is the equivalent of a LeveledLogger's Infof call, guarded by whether this
101 // VerboseLeveledLogger is enabled.
Serge Bazanski5ade7322020-08-27 13:27:51 +0200102 Infof(format string, args ...interface{})
103}
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200104
105// Severity is one of the severities as described in LeveledLogger.
106type Severity string
107
108const (
109 INFO Severity = "I"
110 WARNING Severity = "W"
111 ERROR Severity = "E"
112 FATAL Severity = "F"
113)
114
115var (
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200116 // SeverityAtLeast maps a given severity to a list of severities that at that
117 // severity or higher. In other words, SeverityAtLeast[X] returns a list of
118 // severities that might be seen in a log at severity X.
Serge Bazanski5faa2fc2020-09-07 14:09:30 +0200119 SeverityAtLeast = map[Severity][]Severity{
120 INFO: {INFO, WARNING, ERROR, FATAL},
121 WARNING: {WARNING, ERROR, FATAL},
122 ERROR: {ERROR, FATAL},
123 FATAL: {FATAL},
124 }
125)
126
127func (s Severity) AtLeast(other Severity) bool {
128 for _, el := range SeverityAtLeast[other] {
129 if el == s {
130 return true
131 }
132 }
133 return false
134}
Serge Bazanskib0272182020-11-02 18:39:44 +0100135
Serge Bazanski020b7c52021-07-07 14:22:28 +0200136// Valid returns whether true if this severity is one of the known levels
137// (INFO, WARNING, ERROR or FATAL), false otherwise.
138func (s Severity) Valid() bool {
139 switch s {
140 case INFO, WARNING, ERROR, FATAL:
141 return true
142 default:
143 return false
144 }
145}
146
Serge Bazanskib0272182020-11-02 18:39:44 +0100147func SeverityFromProto(s apb.LeveledLogSeverity) (Severity, error) {
148 switch s {
149 case apb.LeveledLogSeverity_INFO:
150 return INFO, nil
151 case apb.LeveledLogSeverity_WARNING:
152 return WARNING, nil
153 case apb.LeveledLogSeverity_ERROR:
154 return ERROR, nil
155 case apb.LeveledLogSeverity_FATAL:
156 return FATAL, nil
157 default:
158 return "", fmt.Errorf("unknown severity value %d", s)
159 }
160}
161
162func (s Severity) ToProto() apb.LeveledLogSeverity {
163 switch s {
164 case INFO:
165 return apb.LeveledLogSeverity_INFO
166 case WARNING:
167 return apb.LeveledLogSeverity_WARNING
168 case ERROR:
169 return apb.LeveledLogSeverity_ERROR
170 case FATAL:
171 return apb.LeveledLogSeverity_FATAL
172 default:
173 return apb.LeveledLogSeverity_INVALID
174 }
175}