mirror of
https://github.com/kevin-DL/revel-cmd.git
synced 2026-01-11 18:54:31 +00:00
Reformat of code Allow user to use a mix of command line arguments and flags Enhance the import tool to detect missing packages in the modules side Added test cases for all commands
277 lines
8.7 KiB
Go
277 lines
8.7 KiB
Go
package logger
|
|
|
|
import (
|
|
"github.com/revel/config"
|
|
"github.com/revel/log15"
|
|
"gopkg.in/stack.v0"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Utility package to make existing logging backwards compatible
|
|
var (
|
|
// Convert the string to LogLevel
|
|
toLevel = map[string]LogLevel{"debug": LogLevel(log15.LvlDebug),
|
|
"info": LogLevel(log15.LvlInfo), "request": LogLevel(log15.LvlInfo), "warn": LogLevel(log15.LvlWarn),
|
|
"error": LogLevel(log15.LvlError), "crit": LogLevel(log15.LvlCrit),
|
|
"trace": LogLevel(log15.LvlDebug), // TODO trace is deprecated, replaced by debug
|
|
}
|
|
)
|
|
|
|
func GetLogger(name string, logger MultiLogger) (l *log.Logger) {
|
|
switch name {
|
|
case "trace": // TODO trace is deprecated, replaced by debug
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlDebug}, "", 0)
|
|
case "debug":
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlDebug}, "", 0)
|
|
case "info":
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlInfo}, "", 0)
|
|
case "warn":
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlWarn}, "", 0)
|
|
case "error":
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlError}, "", 0)
|
|
case "request":
|
|
l = log.New(loggerRewrite{Logger: logger, Level: log15.LvlInfo}, "", 0)
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
// Get all handlers based on the Config (if available)
|
|
func InitializeFromConfig(basePath string, config *config.Context) (c *CompositeMultiHandler) {
|
|
// If running in test mode suppress anything that is not an error
|
|
if config != nil && config.BoolDefault("testModeFlag", false) {
|
|
config.SetOption("log.info.output", "off")
|
|
config.SetOption("log.debug.output", "off")
|
|
config.SetOption("log.warn.output", "off")
|
|
config.SetOption("log.error.output", "stderr")
|
|
config.SetOption("log.crit.output", "stderr")
|
|
}
|
|
|
|
// If the configuration has an all option we can skip some
|
|
c, _ = NewCompositeMultiHandler()
|
|
|
|
// Filters are assigned first, non filtered items override filters
|
|
initAllLog(c, basePath, config)
|
|
initLogLevels(c, basePath, config)
|
|
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
|
c.CriticalHandler = c.ErrorHandler
|
|
}
|
|
initFilterLog(c, basePath, config)
|
|
if c.CriticalHandler == nil && c.ErrorHandler != nil {
|
|
c.CriticalHandler = c.ErrorHandler
|
|
}
|
|
initRequestLog(c, basePath, config)
|
|
|
|
return c
|
|
}
|
|
|
|
// Init the log.all configuration options
|
|
func initAllLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
if config != nil {
|
|
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
if output, found := config.String("log.all.output"); found {
|
|
// Set all output for the specified handler
|
|
if extraLogFlag {
|
|
log.Printf("Adding standard handler for levels to >%s< ", output)
|
|
}
|
|
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, LvlAllList...))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Init the filter options
|
|
// log.all.filter ....
|
|
// log.error.filter ....
|
|
func initFilterLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
if config != nil {
|
|
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
|
|
// The commands to use
|
|
logFilterList := []struct {
|
|
LogPrefix, LogSuffix string
|
|
parentHandler func(map[string]interface{}) ParentLogHandler
|
|
}{{
|
|
"log.", ".filter",
|
|
func(keyMap map[string]interface{}) ParentLogHandler {
|
|
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
|
return MatchMapHandler(keyMap, child)
|
|
})
|
|
|
|
},
|
|
}, {
|
|
"log.", ".nfilter",
|
|
func(keyMap map[string]interface{}) ParentLogHandler {
|
|
return NewParentLogHandler(func(child LogHandler) LogHandler {
|
|
return NotMatchMapHandler(keyMap, child)
|
|
})
|
|
},
|
|
}}
|
|
|
|
for _, logFilter := range logFilterList {
|
|
// Init for all filters
|
|
for _, name := range []string{"all", "debug", "info", "warn", "error", "crit",
|
|
"trace", // TODO trace is deprecated
|
|
} {
|
|
optionList := config.Options(logFilter.LogPrefix + name + logFilter.LogSuffix)
|
|
for _, option := range optionList {
|
|
splitOptions := strings.Split(option, ".")
|
|
keyMap := map[string]interface{}{}
|
|
for x := 3; x < len(splitOptions); x += 2 {
|
|
keyMap[splitOptions[x]] = splitOptions[x+1]
|
|
}
|
|
phandler := logFilter.parentHandler(keyMap)
|
|
if extraLogFlag {
|
|
log.Printf("Adding key map handler %s %s output %s", option, name, config.StringDefault(option, ""))
|
|
}
|
|
|
|
if name == "all" {
|
|
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler))
|
|
} else {
|
|
initHandlerFor(c, config.StringDefault(option, ""), basePath, NewLogOptions(config, false, phandler, toLevel[name]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Init the log.error, log.warn etc configuration options
|
|
func initLogLevels(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
for _, name := range []string{"debug", "info", "warn", "error", "crit",
|
|
"trace", // TODO trace is deprecated
|
|
} {
|
|
if config != nil {
|
|
extraLogFlag := config.BoolDefault("specialUseFlag", false)
|
|
output, found := config.String("log." + name + ".output")
|
|
if found {
|
|
if extraLogFlag {
|
|
log.Printf("Adding standard handler %s output %s", name, output)
|
|
}
|
|
initHandlerFor(c, output, basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
|
}
|
|
// Gets the list of options with said prefix
|
|
} else {
|
|
initHandlerFor(c, "stderr", basePath, NewLogOptions(config, true, nil, toLevel[name]))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Init the request log options
|
|
func initRequestLog(c *CompositeMultiHandler, basePath string, config *config.Context) {
|
|
// Request logging to a separate output handler
|
|
// This takes the InfoHandlers and adds a MatchAbHandler handler to it to direct
|
|
// context with the word "section=requestlog" to that handler.
|
|
// Note if request logging is not enabled the MatchAbHandler will not be added and the
|
|
// request log messages will be sent out the INFO handler
|
|
outputRequest := "stdout"
|
|
if config != nil {
|
|
outputRequest = config.StringDefault("log.request.output", "")
|
|
}
|
|
oldInfo := c.InfoHandler
|
|
c.InfoHandler = nil
|
|
if outputRequest != "" {
|
|
initHandlerFor(c, outputRequest, basePath, NewLogOptions(config, false, nil, LvlInfo))
|
|
}
|
|
if c.InfoHandler != nil || oldInfo != nil {
|
|
if c.InfoHandler == nil {
|
|
c.InfoHandler = oldInfo
|
|
} else {
|
|
c.InfoHandler = MatchAbHandler("section", "requestlog", c.InfoHandler, oldInfo)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns a handler for the level using the output string
|
|
// Accept formats for output string are
|
|
// LogFunctionMap[value] callback function
|
|
// `stdout` `stderr` `full/file/path/to/location/app.log` `full/file/path/to/location/app.json`
|
|
func initHandlerFor(c *CompositeMultiHandler, output, basePath string, options *LogOptions) {
|
|
if options.Ctx != nil {
|
|
options.SetExtendedOptions(
|
|
"noColor", !options.Ctx.BoolDefault("log.colorize", true),
|
|
"smallDate", options.Ctx.BoolDefault("log.smallDate", true),
|
|
"maxSize", options.Ctx.IntDefault("log.maxsize", 1024*10),
|
|
"maxAge", options.Ctx.IntDefault("log.maxage", 14),
|
|
"maxBackups", options.Ctx.IntDefault("log.maxbackups", 14),
|
|
"compressBackups", !options.Ctx.BoolDefault("log.compressBackups", true),
|
|
)
|
|
}
|
|
|
|
output = strings.TrimSpace(output)
|
|
if funcHandler, found := LogFunctionMap[output]; found {
|
|
funcHandler(c, options)
|
|
} else {
|
|
switch output {
|
|
case "":
|
|
fallthrough
|
|
case "off":
|
|
// No handler, discard data
|
|
default:
|
|
// Write to file specified
|
|
if !filepath.IsAbs(output) {
|
|
output = filepath.Join(basePath, output)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(output), 0755); err != nil {
|
|
log.Panic(err)
|
|
}
|
|
|
|
if strings.HasSuffix(output, "json") {
|
|
c.SetJsonFile(output, options)
|
|
} else {
|
|
// Override defaults for a terminal file
|
|
options.SetExtendedOptions("noColor", true)
|
|
options.SetExtendedOptions("smallDate", false)
|
|
c.SetTerminalFile(output, options)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// This structure and method will handle the old output format and log it to the new format
|
|
type loggerRewrite struct {
|
|
Logger MultiLogger
|
|
Level log15.Lvl
|
|
hideDeprecated bool
|
|
}
|
|
|
|
var log_deprecated = []byte("* LOG DEPRECATED * ")
|
|
|
|
func (lr loggerRewrite) Write(p []byte) (n int, err error) {
|
|
if !lr.hideDeprecated {
|
|
p = append(log_deprecated, p...)
|
|
}
|
|
n = len(p)
|
|
if len(p) > 0 && p[n-1] == '\n' {
|
|
p = p[:n-1]
|
|
n--
|
|
}
|
|
|
|
switch lr.Level {
|
|
case log15.LvlInfo:
|
|
lr.Logger.Info(string(p))
|
|
case log15.LvlDebug:
|
|
lr.Logger.Debug(string(p))
|
|
case log15.LvlWarn:
|
|
lr.Logger.Warn(string(p))
|
|
case log15.LvlError:
|
|
lr.Logger.Error(string(p))
|
|
case log15.LvlCrit:
|
|
lr.Logger.Crit(string(p))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// For logging purposes the call stack can be used to record the stack trace of a bad error
|
|
// simply pass it as a context field in your log statement like
|
|
// `controller.Log.Critc("This should not occur","stack",revel.NewCallStack())`
|
|
func NewCallStack() interface{} {
|
|
return stack.Trace()
|
|
}
|