blob: 226b60c22534ec3250814750103178098fa1b717 [file] [log] [blame]
divyadesai81bb7ba2020-03-11 11:45:23 +00001// Copyright 2015 CoreOS, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package capnslog
16
17import (
18 "errors"
19 "strings"
20 "sync"
21)
22
23// LogLevel is the set of all log levels.
24type LogLevel int8
25
26const (
27 // CRITICAL is the lowest log level; only errors which will end the program will be propagated.
28 CRITICAL LogLevel = iota - 1
29 // ERROR is for errors that are not fatal but lead to troubling behavior.
30 ERROR
31 // WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
32 WARNING
33 // NOTICE is for normal but significant conditions.
34 NOTICE
35 // INFO is a log level for common, everyday log updates.
36 INFO
37 // DEBUG is the default hidden level for more verbose updates about internal processes.
38 DEBUG
39 // TRACE is for (potentially) call by call tracing of programs.
40 TRACE
41)
42
43// Char returns a single-character representation of the log level.
44func (l LogLevel) Char() string {
45 switch l {
46 case CRITICAL:
47 return "C"
48 case ERROR:
49 return "E"
50 case WARNING:
51 return "W"
52 case NOTICE:
53 return "N"
54 case INFO:
55 return "I"
56 case DEBUG:
57 return "D"
58 case TRACE:
59 return "T"
60 default:
61 panic("Unhandled loglevel")
62 }
63}
64
65// String returns a multi-character representation of the log level.
66func (l LogLevel) String() string {
67 switch l {
68 case CRITICAL:
69 return "CRITICAL"
70 case ERROR:
71 return "ERROR"
72 case WARNING:
73 return "WARNING"
74 case NOTICE:
75 return "NOTICE"
76 case INFO:
77 return "INFO"
78 case DEBUG:
79 return "DEBUG"
80 case TRACE:
81 return "TRACE"
82 default:
83 panic("Unhandled loglevel")
84 }
85}
86
87// Update using the given string value. Fulfills the flag.Value interface.
88func (l *LogLevel) Set(s string) error {
89 value, err := ParseLevel(s)
90 if err != nil {
91 return err
92 }
93
94 *l = value
95 return nil
96}
97
98// Returns an empty string, only here to fulfill the pflag.Value interface.
99func (l *LogLevel) Type() string {
100 return ""
101}
102
103// ParseLevel translates some potential loglevel strings into their corresponding levels.
104func ParseLevel(s string) (LogLevel, error) {
105 switch s {
106 case "CRITICAL", "C":
107 return CRITICAL, nil
108 case "ERROR", "0", "E":
109 return ERROR, nil
110 case "WARNING", "1", "W":
111 return WARNING, nil
112 case "NOTICE", "2", "N":
113 return NOTICE, nil
114 case "INFO", "3", "I":
115 return INFO, nil
116 case "DEBUG", "4", "D":
117 return DEBUG, nil
118 case "TRACE", "5", "T":
119 return TRACE, nil
120 }
121 return CRITICAL, errors.New("couldn't parse log level " + s)
122}
123
124type RepoLogger map[string]*PackageLogger
125
126type loggerStruct struct {
127 sync.Mutex
128 repoMap map[string]RepoLogger
129 formatter Formatter
130}
131
132// logger is the global logger
133var logger = new(loggerStruct)
134
135// SetGlobalLogLevel sets the log level for all packages in all repositories
136// registered with capnslog.
137func SetGlobalLogLevel(l LogLevel) {
138 logger.Lock()
139 defer logger.Unlock()
140 for _, r := range logger.repoMap {
141 r.setRepoLogLevelInternal(l)
142 }
143}
144
145// GetRepoLogger may return the handle to the repository's set of packages' loggers.
146func GetRepoLogger(repo string) (RepoLogger, error) {
147 logger.Lock()
148 defer logger.Unlock()
149 r, ok := logger.repoMap[repo]
150 if !ok {
151 return nil, errors.New("no packages registered for repo " + repo)
152 }
153 return r, nil
154}
155
156// MustRepoLogger returns the handle to the repository's packages' loggers.
157func MustRepoLogger(repo string) RepoLogger {
158 r, err := GetRepoLogger(repo)
159 if err != nil {
160 panic(err)
161 }
162 return r
163}
164
165// SetRepoLogLevel sets the log level for all packages in the repository.
166func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
167 logger.Lock()
168 defer logger.Unlock()
169 r.setRepoLogLevelInternal(l)
170}
171
172func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
173 for _, v := range r {
174 v.level = l
175 }
176}
177
178// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
179// order, and returns a map of the results, for use in SetLogLevel.
180func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
181 setlist := strings.Split(conf, ",")
182 out := make(map[string]LogLevel)
183 for _, setstring := range setlist {
184 setting := strings.Split(setstring, "=")
185 if len(setting) != 2 {
186 return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
187 }
188 l, err := ParseLevel(setting[1])
189 if err != nil {
190 return nil, err
191 }
192 out[setting[0]] = l
193 }
194 return out, nil
195}
196
197// SetLogLevel takes a map of package names within a repository to their desired
198// loglevel, and sets the levels appropriately. Unknown packages are ignored.
199// "*" is a special package name that corresponds to all packages, and will be
200// processed first.
201func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
202 logger.Lock()
203 defer logger.Unlock()
204 if l, ok := m["*"]; ok {
205 r.setRepoLogLevelInternal(l)
206 }
207 for k, v := range m {
208 l, ok := r[k]
209 if !ok {
210 continue
211 }
212 l.level = v
213 }
214}
215
216// SetFormatter sets the formatting function for all logs.
217func SetFormatter(f Formatter) {
218 logger.Lock()
219 defer logger.Unlock()
220 logger.formatter = f
221}
222
223// NewPackageLogger creates a package logger object.
224// This should be defined as a global var in your package, referencing your repo.
225func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
226 logger.Lock()
227 defer logger.Unlock()
228 if logger.repoMap == nil {
229 logger.repoMap = make(map[string]RepoLogger)
230 }
231 r, rok := logger.repoMap[repo]
232 if !rok {
233 logger.repoMap[repo] = make(RepoLogger)
234 r = logger.repoMap[repo]
235 }
236 p, pok := r[pkg]
237 if !pok {
238 r[pkg] = &PackageLogger{
239 pkg: pkg,
240 level: INFO,
241 }
242 p = r[pkg]
243 }
244 return
245}