1// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// 1. Redistributions of source code must retain the above copyright notice, this
9//    list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright notice,
11//    this list of conditions and the following disclaimer in the documentation
12//    and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
25package seelog
26
27import (
28	"errors"
29	"fmt"
30	"sync"
31	"time"
32)
33
34const (
35	staticFuncCallDepth = 3 // See 'commonLogger.log' method comments
36	loggerFuncCallDepth = 3
37)
38
39// Current is the logger used in all package level convenience funcs like 'Trace', 'Debug', 'Flush', etc.
40var Current LoggerInterface
41
42// Default logger that is created from an empty config: "<seelog/>". It is not closed by a ReplaceLogger call.
43var Default LoggerInterface
44
45// Disabled logger that doesn't produce any output in any circumstances. It is neither closed nor flushed by a ReplaceLogger call.
46var Disabled LoggerInterface
47
48var pkgOperationsMutex *sync.Mutex
49
50func init() {
51	pkgOperationsMutex = new(sync.Mutex)
52	var err error
53
54	if Default == nil {
55		Default, err = LoggerFromConfigAsBytes([]byte("<seelog />"))
56	}
57
58	if Disabled == nil {
59		Disabled, err = LoggerFromConfigAsBytes([]byte("<seelog levels=\"off\"/>"))
60	}
61
62	if err != nil {
63		panic(fmt.Sprintf("Seelog couldn't start. Error: %s", err.Error()))
64	}
65
66	Current = Default
67}
68
69func createLoggerFromFullConfig(config *configForParsing) (LoggerInterface, error) {
70	if config.LogType == syncloggerTypeFromString {
71		return NewSyncLogger(&config.logConfig), nil
72	} else if config.LogType == asyncLooploggerTypeFromString {
73		return NewAsyncLoopLogger(&config.logConfig), nil
74	} else if config.LogType == asyncTimerloggerTypeFromString {
75		logData := config.LoggerData
76		if logData == nil {
77			return nil, errors.New("async timer data not set")
78		}
79
80		asyncInt, ok := logData.(asyncTimerLoggerData)
81		if !ok {
82			return nil, errors.New("invalid async timer data")
83		}
84
85		logger, err := NewAsyncTimerLogger(&config.logConfig, time.Duration(asyncInt.AsyncInterval))
86		if !ok {
87			return nil, err
88		}
89
90		return logger, nil
91	} else if config.LogType == adaptiveLoggerTypeFromString {
92		logData := config.LoggerData
93		if logData == nil {
94			return nil, errors.New("adaptive logger parameters not set")
95		}
96
97		adaptData, ok := logData.(adaptiveLoggerData)
98		if !ok {
99			return nil, errors.New("invalid adaptive logger parameters")
100		}
101
102		logger, err := NewAsyncAdaptiveLogger(
103			&config.logConfig,
104			time.Duration(adaptData.MinInterval),
105			time.Duration(adaptData.MaxInterval),
106			adaptData.CriticalMsgCount,
107		)
108		if err != nil {
109			return nil, err
110		}
111
112		return logger, nil
113	}
114	return nil, errors.New("invalid config log type/data")
115}
116
117// UseLogger sets the 'Current' package level logger variable to the specified value.
118// This variable is used in all Trace/Debug/... package level convenience funcs.
119//
120// Example:
121//
122// after calling
123//     seelog.UseLogger(somelogger)
124// the following:
125//     seelog.Debug("abc")
126// will be equal to
127//     somelogger.Debug("abc")
128//
129// IMPORTANT: UseLogger do NOT close the previous logger (only flushes it). So if
130// you constantly use it to replace loggers and don't close them in other code, you'll
131// end up having memory leaks.
132//
133// To safely replace loggers, use ReplaceLogger.
134func UseLogger(logger LoggerInterface) error {
135	if logger == nil {
136		return errors.New("logger can not be nil")
137	}
138
139	pkgOperationsMutex.Lock()
140	defer pkgOperationsMutex.Unlock()
141
142	oldLogger := Current
143	Current = logger
144
145	if oldLogger != nil {
146		oldLogger.Flush()
147	}
148
149	return nil
150}
151
152// ReplaceLogger acts as UseLogger but the logger that was previously
153// used is disposed (except Default and Disabled loggers).
154//
155// Example:
156//     import log "github.com/cihub/seelog"
157//
158//     func main() {
159//         logger, err := log.LoggerFromConfigAsFile("seelog.xml")
160//
161//         if err != nil {
162//             panic(err)
163//         }
164//
165//         log.ReplaceLogger(logger)
166//         defer log.Flush()
167//
168//         log.Trace("test")
169//         log.Debugf("var = %s", "abc")
170//     }
171func ReplaceLogger(logger LoggerInterface) error {
172	if logger == nil {
173		return errors.New("logger can not be nil")
174	}
175
176	pkgOperationsMutex.Lock()
177	defer pkgOperationsMutex.Unlock()
178
179	defer func() {
180		if err := recover(); err != nil {
181			reportInternalError(fmt.Errorf("recovered from panic during ReplaceLogger: %s", err))
182		}
183	}()
184
185	if Current == Default {
186		Current.Flush()
187	} else if Current != nil && !Current.Closed() && Current != Disabled {
188		Current.Flush()
189		Current.Close()
190	}
191
192	Current = logger
193
194	return nil
195}
196
197// Tracef formats message according to format specifier
198// and writes to default logger with log level = Trace.
199func Tracef(format string, params ...interface{}) {
200	pkgOperationsMutex.Lock()
201	defer pkgOperationsMutex.Unlock()
202	Current.traceWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
203}
204
205// Debugf formats message according to format specifier
206// and writes to default logger with log level = Debug.
207func Debugf(format string, params ...interface{}) {
208	pkgOperationsMutex.Lock()
209	defer pkgOperationsMutex.Unlock()
210	Current.debugWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
211}
212
213// Infof formats message according to format specifier
214// and writes to default logger with log level = Info.
215func Infof(format string, params ...interface{}) {
216	pkgOperationsMutex.Lock()
217	defer pkgOperationsMutex.Unlock()
218	Current.infoWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
219}
220
221// Warnf formats message according to format specifier and writes to default logger with log level = Warn
222func Warnf(format string, params ...interface{}) error {
223	pkgOperationsMutex.Lock()
224	defer pkgOperationsMutex.Unlock()
225	message := newLogFormattedMessage(format, params)
226	Current.warnWithCallDepth(staticFuncCallDepth, message)
227	return errors.New(message.String())
228}
229
230// Errorf formats message according to format specifier and writes to default logger with log level = Error
231func Errorf(format string, params ...interface{}) error {
232	pkgOperationsMutex.Lock()
233	defer pkgOperationsMutex.Unlock()
234	message := newLogFormattedMessage(format, params)
235	Current.errorWithCallDepth(staticFuncCallDepth, message)
236	return errors.New(message.String())
237}
238
239// Criticalf formats message according to format specifier and writes to default logger with log level = Critical
240func Criticalf(format string, params ...interface{}) error {
241	pkgOperationsMutex.Lock()
242	defer pkgOperationsMutex.Unlock()
243	message := newLogFormattedMessage(format, params)
244	Current.criticalWithCallDepth(staticFuncCallDepth, message)
245	return errors.New(message.String())
246}
247
248// Trace formats message using the default formats for its operands and writes to default logger with log level = Trace
249func Trace(v ...interface{}) {
250	pkgOperationsMutex.Lock()
251	defer pkgOperationsMutex.Unlock()
252	Current.traceWithCallDepth(staticFuncCallDepth, newLogMessage(v))
253}
254
255// Debug formats message using the default formats for its operands and writes to default logger with log level = Debug
256func Debug(v ...interface{}) {
257	pkgOperationsMutex.Lock()
258	defer pkgOperationsMutex.Unlock()
259	Current.debugWithCallDepth(staticFuncCallDepth, newLogMessage(v))
260}
261
262// Info formats message using the default formats for its operands and writes to default logger with log level = Info
263func Info(v ...interface{}) {
264	pkgOperationsMutex.Lock()
265	defer pkgOperationsMutex.Unlock()
266	Current.infoWithCallDepth(staticFuncCallDepth, newLogMessage(v))
267}
268
269// Warn formats message using the default formats for its operands and writes to default logger with log level = Warn
270func Warn(v ...interface{}) error {
271	pkgOperationsMutex.Lock()
272	defer pkgOperationsMutex.Unlock()
273	message := newLogMessage(v)
274	Current.warnWithCallDepth(staticFuncCallDepth, message)
275	return errors.New(message.String())
276}
277
278// Error formats message using the default formats for its operands and writes to default logger with log level = Error
279func Error(v ...interface{}) error {
280	pkgOperationsMutex.Lock()
281	defer pkgOperationsMutex.Unlock()
282	message := newLogMessage(v)
283	Current.errorWithCallDepth(staticFuncCallDepth, message)
284	return errors.New(message.String())
285}
286
287// Critical formats message using the default formats for its operands and writes to default logger with log level = Critical
288func Critical(v ...interface{}) error {
289	pkgOperationsMutex.Lock()
290	defer pkgOperationsMutex.Unlock()
291	message := newLogMessage(v)
292	Current.criticalWithCallDepth(staticFuncCallDepth, message)
293	return errors.New(message.String())
294}
295
296// Flush immediately processes all currently queued messages and all currently buffered messages.
297// It is a blocking call which returns only after the queue is empty and all the buffers are empty.
298//
299// If Flush is called for a synchronous logger (type='sync'), it only flushes buffers (e.g. '<buffered>' receivers)
300// , because there is no queue.
301//
302// Call this method when your app is going to shut down not to lose any log messages.
303func Flush() {
304	pkgOperationsMutex.Lock()
305	defer pkgOperationsMutex.Unlock()
306	Current.Flush()
307}
308