Like Prometheus, but for logs.
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.
 
 
 
 
 
 
loki/pkg/util/log/log.go

122 lines
3.6 KiB

package log
import (
"fmt"
"os"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/weaveworks/common/logging"
"github.com/weaveworks/common/server"
)
var (
// Logger is a shared go-kit logger.
// TODO: Change all components to take a non-global logger via their constructors.
// Prefer accepting a non-global logger as an argument.
Logger = log.NewNopLogger()
)
// InitLogger initialises the global gokit logger (util_log.Logger) and overrides the
// default logger for the server.
func InitLogger(cfg *server.Config, reg prometheus.Registerer) {
l := newPrometheusLogger(cfg.LogLevel, cfg.LogFormat, reg)
// when use util_log.Logger, skip 3 stack frames.
Logger = log.With(l, "caller", log.Caller(3))
// cfg.Log wraps log function, skip 4 stack frames to get caller information.
// this works in go 1.12, but doesn't work in versions earlier.
// it will always shows the wrapper function generated by compiler
// marked <autogenerated> in old versions.
cfg.Log = logging.GoKit(log.With(l, "caller", log.Caller(4)))
}
// prometheusLogger exposes Prometheus counters for each of go-kit's log levels.
type prometheusLogger struct {
logger log.Logger
logMessages *prometheus.CounterVec
}
// newPrometheusLogger creates a new instance of PrometheusLogger which exposes
// Prometheus counters for various log levels.
func newPrometheusLogger(l logging.Level, format logging.Format, reg prometheus.Registerer) log.Logger {
logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))
if format.String() == "json" {
logger = log.NewJSONLogger(log.NewSyncWriter(os.Stderr))
}
logger = level.NewFilter(logger, levelFilter(l.String()))
plogger := &prometheusLogger{
logger: logger,
logMessages: promauto.With(reg).NewCounterVec(prometheus.CounterOpts{
Namespace: "loki",
Name: "log_messages_total",
Help: "Total number of log messages.",
}, []string{"level"}),
}
// Initialise counters for all supported levels:
supportedLevels := []level.Value{
level.DebugValue(),
level.InfoValue(),
level.WarnValue(),
level.ErrorValue(),
}
for _, level := range supportedLevels {
plogger.logMessages.WithLabelValues(level.String())
}
// return a Logger without caller information, shouldn't use directly
return log.With(plogger, "ts", log.DefaultTimestampUTC)
}
// Log increments the appropriate Prometheus counter depending on the log level.
func (pl *prometheusLogger) Log(kv ...interface{}) error {
pl.logger.Log(kv...)
l := "unknown"
for i := 1; i < len(kv); i += 2 {
if v, ok := kv[i].(level.Value); ok {
l = v.String()
break
}
}
pl.logMessages.WithLabelValues(l).Inc()
return nil
}
// CheckFatal prints an error and exits with error code 1 if err is non-nil.
func CheckFatal(location string, err error, logger log.Logger) {
if err == nil {
return
}
logger = level.Error(logger)
if location != "" {
logger = log.With(logger, "msg", "error "+location)
}
// %+v gets the stack trace from errors using github.com/pkg/errors
errStr := fmt.Sprintf("%+v", err)
fmt.Fprintln(os.Stderr, errStr)
logger.Log("err", errStr)
os.Exit(1)
}
// TODOe remove once weaveworks/common updates to go-kit/log
// -> we can then revert to using Level.Gokit
func levelFilter(l string) level.Option {
switch l {
case "debug":
return level.AllowDebug()
case "info":
return level.AllowInfo()
case "warn":
return level.AllowWarn()
case "error":
return level.AllowError()
default:
return level.AllowAll()
}
}