influxdb/logger/logger.go

132 lines
3.0 KiB
Go

package logger
import (
"fmt"
"io"
"time"
zaplogfmt "github.com/jsternberg/zap-logfmt"
isatty "github.com/mattn/go-isatty"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// TimeFormat represents the logger time format.
const TimeFormat = "2006-01-02T15:04:05.000000Z07:00"
// New creates a new zap.Logger.
func New(w io.Writer) *zap.Logger {
config := NewConfig()
l, _ := config.New(w)
return l
}
// New creates a new zap.Logger from config settings.
func (c *Config) New(defaultOutput io.Writer) (*zap.Logger, error) {
w := defaultOutput
format := c.Format
if format == "console" {
// Disallow the console logger if the output is not a terminal.
return nil, fmt.Errorf("unknown logging format: %s", format)
}
// If the format is empty or auto, then set the format depending
// on whether or not a terminal is present.
if format == "" || format == "auto" {
if IsTerminal(w) {
format = "console"
} else {
format = "logfmt"
}
}
encoder, err := newEncoder(format)
if err != nil {
return nil, err
}
return zap.New(zapcore.NewCore(
encoder,
zapcore.Lock(zapcore.AddSync(w)),
c.Level,
), zap.Fields(zap.String("log_id", nextID()))), nil
}
func newEncoder(format string) (zapcore.Encoder, error) {
config := newEncoderConfig()
switch format {
case "json":
return zapcore.NewJSONEncoder(config), nil
case "console":
return zapcore.NewConsoleEncoder(config), nil
case "logfmt":
return zaplogfmt.NewEncoder(config), nil
default:
return nil, fmt.Errorf("unknown logging format: %s", format)
}
}
func newEncoderConfig() zapcore.EncoderConfig {
config := zap.NewProductionEncoderConfig()
config.EncodeTime = func(ts time.Time, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(ts.UTC().Format(TimeFormat))
}
config.EncodeDuration = func(d time.Duration, encoder zapcore.PrimitiveArrayEncoder) {
val := float64(d) / float64(time.Millisecond)
encoder.AppendString(fmt.Sprintf("%.3fms", val))
}
config.LevelKey = "lvl"
return config
}
// IsTerminal checks if w is a file and whether it is an interactive terminal session.
func IsTerminal(w io.Writer) bool {
if f, ok := w.(interface {
Fd() uintptr
}); ok {
return isatty.IsTerminal(f.Fd())
}
return false
}
const (
year = 365 * 24 * time.Hour
week = 7 * 24 * time.Hour
day = 24 * time.Hour
)
// DurationLiteral represents a duration literal from a key and time duration.
func DurationLiteral(key string, val time.Duration) zapcore.Field {
if val == 0 {
return zap.String(key, "0s")
}
var (
value int
unit string
)
switch {
case val%year == 0:
value = int(val / year)
unit = "y"
case val%week == 0:
value = int(val / week)
unit = "w"
case val%day == 0:
value = int(val / day)
unit = "d"
case val%time.Hour == 0:
value = int(val / time.Hour)
unit = "h"
case val%time.Minute == 0:
value = int(val / time.Minute)
unit = "m"
case val%time.Second == 0:
value = int(val / time.Second)
unit = "s"
default:
value = int(val / time.Millisecond)
unit = "ms"
}
return zap.String(key, fmt.Sprintf("%d%s", value, unit))
}