forked from grafana.jool/grafana-jool
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
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
|
|
}
|
|
|