371 lines
12 KiB
Go
371 lines
12 KiB
Go
|
// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
|
||
|
//
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// Redistribution and use in source and binary forms, with or without
|
||
|
// modification, are permitted provided that the following conditions are met:
|
||
|
//
|
||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||
|
// list of conditions and the following disclaimer.
|
||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
// this list of conditions and the following disclaimer in the documentation
|
||
|
// and/or other materials provided with the distribution.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||
|
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
package seelog
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
func reportInternalError(err error) {
|
||
|
fmt.Fprintf(os.Stderr, "seelog internal error: %s\n", err)
|
||
|
}
|
||
|
|
||
|
// LoggerInterface represents structs capable of logging Seelog messages
|
||
|
type LoggerInterface interface {
|
||
|
|
||
|
// Tracef formats message according to format specifier
|
||
|
// and writes to log with level = Trace.
|
||
|
Tracef(format string, params ...interface{})
|
||
|
|
||
|
// Debugf formats message according to format specifier
|
||
|
// and writes to log with level = Debug.
|
||
|
Debugf(format string, params ...interface{})
|
||
|
|
||
|
// Infof formats message according to format specifier
|
||
|
// and writes to log with level = Info.
|
||
|
Infof(format string, params ...interface{})
|
||
|
|
||
|
// Warnf formats message according to format specifier
|
||
|
// and writes to log with level = Warn.
|
||
|
Warnf(format string, params ...interface{}) error
|
||
|
|
||
|
// Errorf formats message according to format specifier
|
||
|
// and writes to log with level = Error.
|
||
|
Errorf(format string, params ...interface{}) error
|
||
|
|
||
|
// Criticalf formats message according to format specifier
|
||
|
// and writes to log with level = Critical.
|
||
|
Criticalf(format string, params ...interface{}) error
|
||
|
|
||
|
// Trace formats message using the default formats for its operands
|
||
|
// and writes to log with level = Trace
|
||
|
Trace(v ...interface{})
|
||
|
|
||
|
// Debug formats message using the default formats for its operands
|
||
|
// and writes to log with level = Debug
|
||
|
Debug(v ...interface{})
|
||
|
|
||
|
// Info formats message using the default formats for its operands
|
||
|
// and writes to log with level = Info
|
||
|
Info(v ...interface{})
|
||
|
|
||
|
// Warn formats message using the default formats for its operands
|
||
|
// and writes to log with level = Warn
|
||
|
Warn(v ...interface{}) error
|
||
|
|
||
|
// Error formats message using the default formats for its operands
|
||
|
// and writes to log with level = Error
|
||
|
Error(v ...interface{}) error
|
||
|
|
||
|
// Critical formats message using the default formats for its operands
|
||
|
// and writes to log with level = Critical
|
||
|
Critical(v ...interface{}) error
|
||
|
|
||
|
traceWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
debugWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
infoWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
warnWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
errorWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
criticalWithCallDepth(callDepth int, message fmt.Stringer)
|
||
|
|
||
|
// Close flushes all the messages in the logger and closes it. It cannot be used after this operation.
|
||
|
Close()
|
||
|
|
||
|
// Flush flushes all the messages in the logger.
|
||
|
Flush()
|
||
|
|
||
|
// Closed returns true if the logger was previously closed.
|
||
|
Closed() bool
|
||
|
|
||
|
// SetAdditionalStackDepth sets the additional number of frames to skip by runtime.Caller
|
||
|
// when getting function information needed to print seelog format identifiers such as %Func or %File.
|
||
|
//
|
||
|
// This func may be used when you wrap seelog funcs and want to print caller info of you own
|
||
|
// wrappers instead of seelog func callers. In this case you should set depth = 1. If you then
|
||
|
// wrap your wrapper, you should set depth = 2, etc.
|
||
|
//
|
||
|
// NOTE: Incorrect depth value may lead to errors in runtime.Caller evaluation or incorrect
|
||
|
// function/file names in log files. Do not use it if you are not going to wrap seelog funcs.
|
||
|
// You may reset the value to default using a SetAdditionalStackDepth(0) call.
|
||
|
SetAdditionalStackDepth(depth int) error
|
||
|
|
||
|
// Sets logger context that can be used in formatter funcs and custom receivers
|
||
|
SetContext(context interface{})
|
||
|
}
|
||
|
|
||
|
// innerLoggerInterface is an internal logging interface
|
||
|
type innerLoggerInterface interface {
|
||
|
innerLog(level LogLevel, context LogContextInterface, message fmt.Stringer)
|
||
|
Flush()
|
||
|
}
|
||
|
|
||
|
// [file path][func name][level] -> [allowed]
|
||
|
type allowedContextCache map[string]map[string]map[LogLevel]bool
|
||
|
|
||
|
// commonLogger contains all common data needed for logging and contains methods used to log messages.
|
||
|
type commonLogger struct {
|
||
|
config *logConfig // Config used for logging
|
||
|
contextCache allowedContextCache // Caches whether log is enabled for specific "full path-func name-level" sets
|
||
|
closed bool // 'true' when all writers are closed, all data is flushed, logger is unusable. Must be accessed while holding closedM
|
||
|
closedM sync.RWMutex
|
||
|
m sync.Mutex // Mutex for main operations
|
||
|
unusedLevels []bool
|
||
|
innerLogger innerLoggerInterface
|
||
|
addStackDepth int // Additional stack depth needed for correct seelog caller context detection
|
||
|
customContext interface{}
|
||
|
}
|
||
|
|
||
|
func newCommonLogger(config *logConfig, internalLogger innerLoggerInterface) *commonLogger {
|
||
|
cLogger := new(commonLogger)
|
||
|
|
||
|
cLogger.config = config
|
||
|
cLogger.contextCache = make(allowedContextCache)
|
||
|
cLogger.unusedLevels = make([]bool, Off)
|
||
|
cLogger.fillUnusedLevels()
|
||
|
cLogger.innerLogger = internalLogger
|
||
|
|
||
|
return cLogger
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) SetAdditionalStackDepth(depth int) error {
|
||
|
if depth < 0 {
|
||
|
return fmt.Errorf("negative depth: %d", depth)
|
||
|
}
|
||
|
cLogger.m.Lock()
|
||
|
cLogger.addStackDepth = depth
|
||
|
cLogger.m.Unlock()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Tracef(format string, params ...interface{}) {
|
||
|
cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Debugf(format string, params ...interface{}) {
|
||
|
cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Infof(format string, params ...interface{}) {
|
||
|
cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Warnf(format string, params ...interface{}) error {
|
||
|
message := newLogFormattedMessage(format, params)
|
||
|
cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Errorf(format string, params ...interface{}) error {
|
||
|
message := newLogFormattedMessage(format, params)
|
||
|
cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Criticalf(format string, params ...interface{}) error {
|
||
|
message := newLogFormattedMessage(format, params)
|
||
|
cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Trace(v ...interface{}) {
|
||
|
cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Debug(v ...interface{}) {
|
||
|
cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Info(v ...interface{}) {
|
||
|
cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Warn(v ...interface{}) error {
|
||
|
message := newLogMessage(v)
|
||
|
cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Error(v ...interface{}) error {
|
||
|
message := newLogMessage(v)
|
||
|
cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Critical(v ...interface{}) error {
|
||
|
message := newLogMessage(v)
|
||
|
cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
|
||
|
return errors.New(message.String())
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) SetContext(c interface{}) {
|
||
|
cLogger.customContext = c
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) traceWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(TraceLvl, message, callDepth)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) debugWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(DebugLvl, message, callDepth)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) infoWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(InfoLvl, message, callDepth)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) warnWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(WarnLvl, message, callDepth)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) errorWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(ErrorLvl, message, callDepth)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) criticalWithCallDepth(callDepth int, message fmt.Stringer) {
|
||
|
cLogger.log(CriticalLvl, message, callDepth)
|
||
|
cLogger.innerLogger.Flush()
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) Closed() bool {
|
||
|
cLogger.closedM.RLock()
|
||
|
defer cLogger.closedM.RUnlock()
|
||
|
return cLogger.closed
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) fillUnusedLevels() {
|
||
|
for i := 0; i < len(cLogger.unusedLevels); i++ {
|
||
|
cLogger.unusedLevels[i] = true
|
||
|
}
|
||
|
|
||
|
cLogger.fillUnusedLevelsByContraint(cLogger.config.Constraints)
|
||
|
|
||
|
for _, exception := range cLogger.config.Exceptions {
|
||
|
cLogger.fillUnusedLevelsByContraint(exception)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) fillUnusedLevelsByContraint(constraint logLevelConstraints) {
|
||
|
for i := 0; i < len(cLogger.unusedLevels); i++ {
|
||
|
if constraint.IsAllowed(LogLevel(i)) {
|
||
|
cLogger.unusedLevels[i] = false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// stackCallDepth is used to indicate the call depth of 'log' func.
|
||
|
// This depth level is used in the runtime.Caller(...) call. See
|
||
|
// common_context.go -> specifyContext, extractCallerInfo for details.
|
||
|
func (cLogger *commonLogger) log(level LogLevel, message fmt.Stringer, stackCallDepth int) {
|
||
|
if cLogger.unusedLevels[level] {
|
||
|
return
|
||
|
}
|
||
|
cLogger.m.Lock()
|
||
|
defer cLogger.m.Unlock()
|
||
|
|
||
|
if cLogger.Closed() {
|
||
|
return
|
||
|
}
|
||
|
context, _ := specifyContext(stackCallDepth+cLogger.addStackDepth, cLogger.customContext)
|
||
|
// Context errors are not reported because there are situations
|
||
|
// in which context errors are normal Seelog usage cases. For
|
||
|
// example in executables with stripped symbols.
|
||
|
// Error contexts are returned instead. See common_context.go.
|
||
|
/*if err != nil {
|
||
|
reportInternalError(err)
|
||
|
return
|
||
|
}*/
|
||
|
cLogger.innerLogger.innerLog(level, context, message)
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) processLogMsg(level LogLevel, message fmt.Stringer, context LogContextInterface) {
|
||
|
defer func() {
|
||
|
if err := recover(); err != nil {
|
||
|
reportInternalError(fmt.Errorf("recovered from panic during message processing: %s", err))
|
||
|
}
|
||
|
}()
|
||
|
if cLogger.config.IsAllowed(level, context) {
|
||
|
cLogger.config.RootDispatcher.Dispatch(message.String(), level, context, reportInternalError)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (cLogger *commonLogger) isAllowed(level LogLevel, context LogContextInterface) bool {
|
||
|
funcMap, ok := cLogger.contextCache[context.FullPath()]
|
||
|
if !ok {
|
||
|
funcMap = make(map[string]map[LogLevel]bool, 0)
|
||
|
cLogger.contextCache[context.FullPath()] = funcMap
|
||
|
}
|
||
|
|
||
|
levelMap, ok := funcMap[context.Func()]
|
||
|
if !ok {
|
||
|
levelMap = make(map[LogLevel]bool, 0)
|
||
|
funcMap[context.Func()] = levelMap
|
||
|
}
|
||
|
|
||
|
isAllowValue, ok := levelMap[level]
|
||
|
if !ok {
|
||
|
isAllowValue = cLogger.config.IsAllowed(level, context)
|
||
|
levelMap[level] = isAllowValue
|
||
|
}
|
||
|
|
||
|
return isAllowValue
|
||
|
}
|
||
|
|
||
|
type logMessage struct {
|
||
|
params []interface{}
|
||
|
}
|
||
|
|
||
|
type logFormattedMessage struct {
|
||
|
format string
|
||
|
params []interface{}
|
||
|
}
|
||
|
|
||
|
func newLogMessage(params []interface{}) fmt.Stringer {
|
||
|
message := new(logMessage)
|
||
|
|
||
|
message.params = params
|
||
|
|
||
|
return message
|
||
|
}
|
||
|
|
||
|
func newLogFormattedMessage(format string, params []interface{}) *logFormattedMessage {
|
||
|
message := new(logFormattedMessage)
|
||
|
|
||
|
message.params = params
|
||
|
message.format = format
|
||
|
|
||
|
return message
|
||
|
}
|
||
|
|
||
|
func (message *logMessage) String() string {
|
||
|
return fmt.Sprint(message.params...)
|
||
|
}
|
||
|
|
||
|
func (message *logFormattedMessage) String() string {
|
||
|
return fmt.Sprintf(message.format, message.params...)
|
||
|
}
|