The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
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.
 
 
 
 
 
 
grafana/pkg/infra/log/log.go

296 lines
7.1 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"
"os"
"path/filepath"
"strings"
"github.com/go-stack/stack"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/inconshreveable/log15"
isatty "github.com/mattn/go-isatty"
"gopkg.in/ini.v1"
)
var Root log15.Logger
var loggersToClose []DisposableHandler
var loggersToReload []ReloadableHandler
var filters map[string]log15.Lvl
func init() {
loggersToClose = make([]DisposableHandler, 0)
loggersToReload = make([]ReloadableHandler, 0)
filters = map[string]log15.Lvl{}
Root = log15.Root()
Root.SetHandler(log15.DiscardHandler())
}
func New(logger string, ctx ...interface{}) Logger {
params := append([]interface{}{"logger", logger}, ctx...)
return Root.New(params...)
}
func Tracef(format string, v ...interface{}) {
var message string
if len(v) > 0 {
message = fmt.Sprintf(format, v...)
} else {
message = format
}
Root.Debug(message)
}
func Debugf(format string, v ...interface{}) {
var message string
if len(v) > 0 {
message = fmt.Sprintf(format, v...)
} else {
message = format
}
Root.Debug(message)
}
func Infof(format string, v ...interface{}) {
var message string
if len(v) > 0 {
message = fmt.Sprintf(format, v...)
} else {
message = format
}
Root.Info(message)
}
func Warn(msg string, v ...interface{}) {
Root.Warn(msg, v...)
}
func Warnf(format string, v ...interface{}) {
var message string
if len(v) > 0 {
message = fmt.Sprintf(format, v...)
} else {
message = format
}
Root.Warn(message)
}
func Debug(msg string, args ...interface{}) {
Root.Debug(msg, args...)
}
func Info(msg string, args ...interface{}) {
Root.Info(msg, args...)
}
func Error(msg string, args ...interface{}) {
Root.Error(msg, args...)
}
func Errorf(skip int, format string, v ...interface{}) {
Root.Error(fmt.Sprintf(format, v...))
}
func Fatalf(skip int, format string, v ...interface{}) {
Root.Crit(fmt.Sprintf(format, v...))
if err := Close(); err != nil {
fmt.Fprintf(os.Stderr, "Failed to close log: %s\n", err)
}
os.Exit(1)
}
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
}
var logLevels = map[string]log15.Lvl{
"trace": log15.LvlDebug,
"debug": log15.LvlDebug,
"info": log15.LvlInfo,
"warn": log15.LvlWarn,
"error": log15.LvlError,
"critical": log15.LvlCrit,
}
func getLogLevelFromConfig(key string, defaultName string, cfg *ini.File) (string, log15.Lvl) {
levelName := cfg.Section(key).Key("level").MustString(defaultName)
levelName = strings.ToLower(levelName)
level := getLogLevelFromString(levelName)
return levelName, level
}
func getLogLevelFromString(levelName string) log15.Lvl {
level, ok := logLevels[levelName]
if !ok {
Root.Error("Unknown log level", "level", levelName)
return log15.LvlError
}
return level
}
func getFilters(filterStrArray []string) map[string]log15.Lvl {
filterMap := make(map[string]log15.Lvl)
for _, filterStr := range filterStrArray {
parts := strings.Split(filterStr, ":")
if len(parts) > 1 {
filterMap[parts[0]] = getLogLevelFromString(parts[1])
}
}
return filterMap
}
func getLogFormat(format string) log15.Format {
switch format {
case "console":
if isatty.IsTerminal(os.Stdout.Fd()) {
return log15.TerminalFormat()
}
return log15.LogfmtFormat()
case "text":
return log15.LogfmtFormat()
case "json":
return log15.JsonFormat()
default:
return log15.LogfmtFormat()
}
}
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()))
handlers := make([]log15.Handler, 0)
for _, mode := range modes {
mode = strings.TrimSpace(mode)
sec, err := cfg.GetSection("log." + mode)
if err != nil {
Root.Error("Unknown log mode", "mode", mode)
return errutil.Wrapf(err, "failed to get config section log.%s", mode)
}
// Log level.
_, level := getLogLevelFromConfig("log."+mode, defaultLevelName, cfg)
modeFilters := getFilters(util.SplitString(sec.Key("filters").String()))
format := getLogFormat(sec.Key("format").MustString(""))
var handler log15.Handler
// Generate log configuration.
switch mode {
case "console":
handler = log15.StreamHandler(os.Stdout, format)
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 {
Root.Error("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 {
Root.Error("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 = fileHandler
case "syslog":
sysLogHandler := NewSyslog(sec, format)
loggersToClose = append(loggersToClose, sysLogHandler)
handler = sysLogHandler
}
if handler == nil {
panic(fmt.Sprintf("Handler is uninitialized for mode %q", mode))
}
for key, value := range defaultFilters {
if _, exist := modeFilters[key]; !exist {
modeFilters[key] = value
}
}
for key, value := range modeFilters {
if _, exist := filters[key]; !exist {
filters[key] = value
}
}
handler = LogFilterHandler(level, modeFilters, handler)
handlers = append(handlers, handler)
}
Root.SetHandler(log15.MultiHandler(handlers...))
return nil
}
func LogFilterHandler(maxLevel log15.Lvl, filters map[string]log15.Lvl, h log15.Handler) log15.Handler {
return log15.FilterHandler(func(r *log15.Record) (pass bool) {
if len(filters) > 0 {
for i := 0; i < len(r.Ctx); i += 2 {
key, ok := r.Ctx[i].(string)
if ok && key == "logger" {
loggerName, strOk := r.Ctx[i+1].(string)
if strOk {
if filterLevel, ok := filters[loggerName]; ok {
return r.Lvl <= filterLevel
}
}
}
}
}
return r.Lvl <= maxLevel
}, h)
}
func Stack(skip int) string {
call := stack.Caller(skip)
s := stack.Trace().TrimBelow(call).TrimRuntime()
return s.String()
}