1
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

346 lines
9.6 KiB

// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
gokitlog "github.com/go-kit/log"
"github.com/go-kit/log/term"
"github.com/go-stack/stack"
"github.com/grafana/grafana/pkg/infra/log/level"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/mattn/go-isatty"
"gopkg.in/ini.v1"
)
var loggersToClose []DisposableHandler
var loggersToReload []ReloadableHandler
var filters map[string]level.Option
var Root MultiLoggers
func init() {
loggersToClose = make([]DisposableHandler, 0)
loggersToReload = make([]ReloadableHandler, 0)
filters = map[string]level.Option{}
Root.AddLogger(gokitlog.NewLogfmtLogger(os.Stderr), "info", filters)
}
type LogWithFilters struct {
val gokitlog.Logger
filters map[string]level.Option
maxLevel level.Option
}
type MultiLoggers struct {
loggers []LogWithFilters
}
func (ml *MultiLoggers) AddLogger(val gokitlog.Logger, levelName string, filters map[string]level.Option) {
logger := LogWithFilters{val: val, filters: filters, maxLevel: getLogLevelFromString(levelName)}
ml.loggers = append(ml.loggers, logger)
}
func (ml *MultiLoggers) SetLogger(des MultiLoggers) {
ml.loggers = des.loggers
}
func (ml *MultiLoggers) GetLogger() MultiLoggers {
return *ml
}
func (ml MultiLoggers) Warn(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.WarnValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Debug(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.DebugValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Error(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.ErrorValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Info(msg string, args ...interface{}) {
args = append([]interface{}{level.Key(), level.InfoValue(), "msg", msg}, args...)
err := ml.Log(args...)
if err != nil {
_ = level.Error(Root).Log("Logging error", "error", err)
}
}
func (ml MultiLoggers) Log(keyvals ...interface{}) error {
for _, multilogger := range ml.loggers {
multilogger.val = gokitlog.With(multilogger.val, "t", gokitlog.TimestampFormat(time.Now, "2006-01-02T15:04:05.99-0700"))
if err := multilogger.val.Log(keyvals...); err != nil {
return err
}
}
return nil
}
// we need to implement new function for multiloggers
func (ml MultiLoggers) New(ctx ...interface{}) MultiLoggers {
var newloger MultiLoggers
for _, logWithFilter := range ml.loggers {
logWithFilter.val = gokitlog.With(logWithFilter.val, ctx)
if len(ctx) > 0 {
v, ok := logWithFilter.filters[ctx[0].(string)]
if ok {
logWithFilter.val = level.NewFilter(logWithFilter.val, v)
} else {
logWithFilter.val = level.NewFilter(logWithFilter.val, logWithFilter.maxLevel)
}
}
newloger.loggers = append(newloger.loggers, logWithFilter)
}
return newloger
}
func New(ctx ...interface{}) MultiLoggers {
if len(ctx) == 0 {
return Root
}
var newloger MultiLoggers
ctx = append([]interface{}{"logger"}, ctx...)
for _, logWithFilter := range Root.loggers {
logWithFilter.val = gokitlog.With(logWithFilter.val, ctx...)
v, ok := logWithFilter.filters[ctx[0].(string)]
if ok {
logWithFilter.val = level.NewFilter(logWithFilter.val, v)
} else {
logWithFilter.val = level.NewFilter(logWithFilter.val, logWithFilter.maxLevel)
}
newloger.loggers = append(newloger.loggers, logWithFilter)
}
return newloger
}
var logLevels = map[string]level.Option{
"trace": level.AllowDebug(),
"debug": level.AllowDebug(),
"info": level.AllowInfo(),
"warn": level.AllowWarn(),
"error": level.AllowError(),
"critical": level.AllowError(),
}
func getLogLevelFromConfig(key string, defaultName string, cfg *ini.File) (string, level.Option) {
levelName := cfg.Section(key).Key("level").MustString(defaultName)
levelName = strings.ToLower(levelName)
level := getLogLevelFromString(levelName)
return levelName, level
}
func getLogLevelFromString(levelName string) level.Option {
loglevel, ok := logLevels[levelName]
if !ok {
_ = level.Error(Root).Log("Unknown log level", "level", levelName)
return level.AllowError()
}
return loglevel
}
// the filter is composed with logger name and level
func getFilters(filterStrArray []string) map[string]level.Option {
filterMap := make(map[string]level.Option)
for _, filterStr := range filterStrArray {
parts := strings.Split(filterStr, ":")
if len(parts) > 1 {
filterMap[parts[0]] = getLogLevelFromString(parts[1])
}
}
return filterMap
}
func Stack(skip int) string {
call := stack.Caller(skip)
s := stack.Trace().TrimBelow(call).TrimRuntime()
return s.String()
}
type Formatedlogger func(w io.Writer) gokitlog.Logger
func terminalColorFn(keyvals ...interface{}) term.FgBgColor {
for i := 0; i < len(keyvals)-1; i += 2 {
if keyvals[i] != level.Key() {
continue
}
switch keyvals[i+1] {
case "trace":
return term.FgBgColor{Fg: term.Gray}
case level.DebugValue():
return term.FgBgColor{Fg: term.Gray}
case level.InfoValue():
return term.FgBgColor{Fg: term.Green}
case level.WarnValue():
return term.FgBgColor{Fg: term.Yellow}
case level.ErrorValue():
return term.FgBgColor{Fg: term.Red}
case "crit":
return term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed}
default:
return term.FgBgColor{}
}
}
return term.FgBgColor{}
}
func getLogFormat(format string) Formatedlogger {
switch format {
case "console":
if isatty.IsTerminal(os.Stdout.Fd()) {
return func(w io.Writer) gokitlog.Logger {
return term.NewColorLogger(w, gokitlog.NewLogfmtLogger, terminalColorFn)
}
}
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
case "text":
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
case "json":
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewJSONLogger(gokitlog.NewSyncWriter(w))
}
default:
return func(w io.Writer) gokitlog.Logger {
return gokitlog.NewLogfmtLogger(w)
}
}
}
// this is for file logger only
func Close() error {
var err error
for _, logger := range loggersToClose {
if e := logger.Close(); e != nil && err == nil {
err = e
}
}
loggersToClose = make([]DisposableHandler, 0)
return err
}
// Reload reloads all loggers.
func Reload() error {
for _, logger := range loggersToReload {
if err := logger.Reload(); err != nil {
return err
}
}
return nil
}
func ReadLoggingConfig(modes []string, logsPath string, cfg *ini.File) error {
if err := Close(); err != nil {
return err
}
defaultLevelName, _ := getLogLevelFromConfig("log", "info", cfg)
defaultFilters := getFilters(util.SplitString(cfg.Section("log").Key("filters").String()))
var configLoggers []LogWithFilters
for _, mode := range modes {
mode = strings.TrimSpace(mode)
sec, err := cfg.GetSection("log." + mode)
if err != nil {
_ = level.Error(Root).Log("Unknown log mode", "mode", mode)
return errutil.Wrapf(err, "failed to get config section log.%s", mode)
}
// Log level.
_, leveloption := getLogLevelFromConfig("log."+mode, defaultLevelName, cfg)
modeFilters := getFilters(util.SplitString(sec.Key("filters").String()))
format := getLogFormat(sec.Key("format").MustString(""))
var handler LogWithFilters
switch mode {
case "console":
handler.val = format(os.Stdout)
case "file":
fileName := sec.Key("file_name").MustString(filepath.Join(logsPath, "grafana.log"))
dpath := filepath.Dir(fileName)
if err := os.MkdirAll(dpath, os.ModePerm); err != nil {
_ = level.Error(Root).Log("Failed to create directory", "dpath", dpath, "err", err)
return errutil.Wrapf(err, "failed to create log directory %q", dpath)
}
fileHandler := NewFileWriter()
fileHandler.Filename = fileName
fileHandler.Format = format
fileHandler.Rotate = sec.Key("log_rotate").MustBool(true)
fileHandler.Maxlines = sec.Key("max_lines").MustInt(1000000)
fileHandler.Maxsize = 1 << uint(sec.Key("max_size_shift").MustInt(28))
fileHandler.Daily = sec.Key("daily_rotate").MustBool(true)
fileHandler.Maxdays = sec.Key("max_days").MustInt64(7)
if err := fileHandler.Init(); err != nil {
_ = level.Error(Root).Log("Failed to initialize file handler", "dpath", dpath, "err", err)
return errutil.Wrapf(err, "failed to initialize file handler")
}
loggersToClose = append(loggersToClose, fileHandler)
loggersToReload = append(loggersToReload, fileHandler)
handler.val = fileHandler
case "syslog":
sysLogHandler := NewSyslog(sec, format)
loggersToClose = append(loggersToClose, sysLogHandler)
handler.val = sysLogHandler.logger
}
if handler.val == nil {
panic(fmt.Sprintf("Handler is uninitialized for mode %q", mode))
}
// join default filters and mode filters together
for key, value := range defaultFilters {
if _, exist := modeFilters[key]; !exist {
modeFilters[key] = value
}
}
// copy joined default + mode filters into filters
for key, value := range modeFilters {
if _, exist := filters[key]; !exist {
filters[key] = value
}
}
handler.filters = modeFilters
handler.maxLevel = leveloption
// handler = LogFilterHandler(leveloption, modeFilters, handler)
configLoggers = append(configLoggers, handler)
}
if len(configLoggers) > 0 {
Root.loggers = configLoggers
}
return nil
}