1// Copyright 2014 The Gogs Authors. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5package log
6
7import (
8	"fmt"
9	"os"
10	"runtime"
11	"strings"
12	"sync"
13)
14
15type loggerMap struct {
16	sync.Map
17}
18
19func (m *loggerMap) Load(k string) (*MultiChannelledLogger, bool) {
20	v, ok := m.Map.Load(k)
21	if !ok {
22		return nil, false
23	}
24	l, ok := v.(*MultiChannelledLogger)
25	return l, ok
26}
27
28func (m *loggerMap) Store(k string, v *MultiChannelledLogger) {
29	m.Map.Store(k, v)
30}
31
32func (m *loggerMap) Delete(k string) {
33	m.Map.Delete(k)
34}
35
36var (
37	// DEFAULT is the name of the default logger
38	DEFAULT = "default"
39	// NamedLoggers map of named loggers
40	NamedLoggers loggerMap
41	prefix       string
42)
43
44// NewLogger create a logger for the default logger
45func NewLogger(bufLen int64, name, provider, config string) *MultiChannelledLogger {
46	err := NewNamedLogger(DEFAULT, bufLen, name, provider, config)
47	if err != nil {
48		CriticalWithSkip(1, "Unable to create default logger: %v", err)
49		panic(err)
50	}
51	l, _ := NamedLoggers.Load(DEFAULT)
52	return l
53}
54
55// NewNamedLogger creates a new named logger for a given configuration
56func NewNamedLogger(name string, bufLen int64, subname, provider, config string) error {
57	logger, ok := NamedLoggers.Load(name)
58	if !ok {
59		logger = newLogger(name, bufLen)
60		NamedLoggers.Store(name, logger)
61	}
62
63	return logger.SetLogger(subname, provider, config)
64}
65
66// DelNamedLogger closes and deletes the named logger
67func DelNamedLogger(name string) {
68	l, ok := NamedLoggers.Load(name)
69	if ok {
70		NamedLoggers.Delete(name)
71		l.Close()
72	}
73}
74
75// DelLogger removes the named sublogger from the default logger
76func DelLogger(name string) error {
77	logger, _ := NamedLoggers.Load(DEFAULT)
78	found, err := logger.DelLogger(name)
79	if !found {
80		Trace("Log %s not found, no need to delete", name)
81	}
82	return err
83}
84
85// GetLogger returns either a named logger or the default logger
86func GetLogger(name string) *MultiChannelledLogger {
87	logger, ok := NamedLoggers.Load(name)
88	if ok {
89		return logger
90	}
91	logger, _ = NamedLoggers.Load(DEFAULT)
92	return logger
93}
94
95// GetLevel returns the minimum logger level
96func GetLevel() Level {
97	l, _ := NamedLoggers.Load(DEFAULT)
98	return l.GetLevel()
99}
100
101// GetStacktraceLevel returns the minimum logger level
102func GetStacktraceLevel() Level {
103	l, _ := NamedLoggers.Load(DEFAULT)
104	return l.GetStacktraceLevel()
105}
106
107// Trace records trace log
108func Trace(format string, v ...interface{}) {
109	Log(1, TRACE, format, v...)
110}
111
112// IsTrace returns true if at least one logger is TRACE
113func IsTrace() bool {
114	return GetLevel() <= TRACE
115}
116
117// Debug records debug log
118func Debug(format string, v ...interface{}) {
119	Log(1, DEBUG, format, v...)
120}
121
122// IsDebug returns true if at least one logger is DEBUG
123func IsDebug() bool {
124	return GetLevel() <= DEBUG
125}
126
127// Info records info log
128func Info(format string, v ...interface{}) {
129	Log(1, INFO, format, v...)
130}
131
132// IsInfo returns true if at least one logger is INFO
133func IsInfo() bool {
134	return GetLevel() <= INFO
135}
136
137// Warn records warning log
138func Warn(format string, v ...interface{}) {
139	Log(1, WARN, format, v...)
140}
141
142// IsWarn returns true if at least one logger is WARN
143func IsWarn() bool {
144	return GetLevel() <= WARN
145}
146
147// Error records error log
148func Error(format string, v ...interface{}) {
149	Log(1, ERROR, format, v...)
150}
151
152// ErrorWithSkip records error log from "skip" calls back from this function
153func ErrorWithSkip(skip int, format string, v ...interface{}) {
154	Log(skip+1, ERROR, format, v...)
155}
156
157// IsError returns true if at least one logger is ERROR
158func IsError() bool {
159	return GetLevel() <= ERROR
160}
161
162// Critical records critical log
163func Critical(format string, v ...interface{}) {
164	Log(1, CRITICAL, format, v...)
165}
166
167// CriticalWithSkip records critical log from "skip" calls back from this function
168func CriticalWithSkip(skip int, format string, v ...interface{}) {
169	Log(skip+1, CRITICAL, format, v...)
170}
171
172// IsCritical returns true if at least one logger is CRITICAL
173func IsCritical() bool {
174	return GetLevel() <= CRITICAL
175}
176
177// Fatal records fatal log and exit process
178func Fatal(format string, v ...interface{}) {
179	Log(1, FATAL, format, v...)
180	Close()
181	os.Exit(1)
182}
183
184// FatalWithSkip records fatal log from "skip" calls back from this function
185func FatalWithSkip(skip int, format string, v ...interface{}) {
186	Log(skip+1, FATAL, format, v...)
187	Close()
188	os.Exit(1)
189}
190
191// IsFatal returns true if at least one logger is FATAL
192func IsFatal() bool {
193	return GetLevel() <= FATAL
194}
195
196// Pause pauses all the loggers
197func Pause() {
198	NamedLoggers.Range(func(key, value interface{}) bool {
199		logger := value.(*MultiChannelledLogger)
200		logger.Pause()
201		logger.Flush()
202		return true
203	})
204}
205
206// Resume resumes all the loggers
207func Resume() {
208	NamedLoggers.Range(func(key, value interface{}) bool {
209		logger := value.(*MultiChannelledLogger)
210		logger.Resume()
211		return true
212	})
213}
214
215// ReleaseReopen releases and reopens logging files
216func ReleaseReopen() error {
217	var accumulatedErr error
218	NamedLoggers.Range(func(key, value interface{}) bool {
219		logger := value.(*MultiChannelledLogger)
220		if err := logger.ReleaseReopen(); err != nil {
221			if accumulatedErr == nil {
222				accumulatedErr = fmt.Errorf("Error reopening %s: %v", key.(string), err)
223			} else {
224				accumulatedErr = fmt.Errorf("Error reopening %s: %v & %v", key.(string), err, accumulatedErr)
225			}
226		}
227		return true
228	})
229	return accumulatedErr
230}
231
232// Close closes all the loggers
233func Close() {
234	l, ok := NamedLoggers.Load(DEFAULT)
235	if !ok {
236		return
237	}
238	NamedLoggers.Delete(DEFAULT)
239	l.Close()
240}
241
242// Log a message with defined skip and at logging level
243// A skip of 0 refers to the caller of this command
244func Log(skip int, level Level, format string, v ...interface{}) {
245	l, ok := NamedLoggers.Load(DEFAULT)
246	if ok {
247		l.Log(skip+1, level, format, v...)
248	}
249}
250
251// LoggerAsWriter is a io.Writer shim around the gitea log
252type LoggerAsWriter struct {
253	ourLoggers []*MultiChannelledLogger
254	level      Level
255}
256
257// NewLoggerAsWriter creates a Writer representation of the logger with setable log level
258func NewLoggerAsWriter(level string, ourLoggers ...*MultiChannelledLogger) *LoggerAsWriter {
259	if len(ourLoggers) == 0 {
260		l, _ := NamedLoggers.Load(DEFAULT)
261		ourLoggers = []*MultiChannelledLogger{l}
262	}
263	l := &LoggerAsWriter{
264		ourLoggers: ourLoggers,
265		level:      FromString(level),
266	}
267	return l
268}
269
270// Write implements the io.Writer interface to allow spoofing of chi
271func (l *LoggerAsWriter) Write(p []byte) (int, error) {
272	for _, logger := range l.ourLoggers {
273		// Skip = 3 because this presumes that we have been called by log.Println()
274		// If the caller has used log.Output or the like this will be wrong
275		logger.Log(3, l.level, string(p))
276	}
277	return len(p), nil
278}
279
280// Log takes a given string and logs it at the set log-level
281func (l *LoggerAsWriter) Log(msg string) {
282	for _, logger := range l.ourLoggers {
283		// Set the skip to reference the call just above this
284		_ = logger.Log(1, l.level, msg)
285	}
286}
287
288func init() {
289	_, filename, _, _ := runtime.Caller(0)
290	prefix = strings.TrimSuffix(filename, "modules/log/log.go")
291}
292