Skip to content

Commit

Permalink
refactor: move the logger package from influxdb to platform
Browse files Browse the repository at this point in the history
We are moving the necessary code for 2.0 from the influxdb 1.X
repository to the platform 2.0 repository. The logger is an unnecessary
dependency on the old influxdb that is making life more complicated.
  • Loading branch information
jsternberg committed Oct 10, 2018
1 parent 18cdd69 commit e7e17d6
Show file tree
Hide file tree
Showing 21 changed files with 489 additions and 16 deletions.
2 changes: 1 addition & 1 deletion cmd/influxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import (

"github.com/influxdata/flux/control"
"github.com/influxdata/flux/execute"
influxlogger "github.com/influxdata/influxdb/logger"
"github.com/influxdata/platform"
"github.com/influxdata/platform/bolt"
"github.com/influxdata/platform/chronograf/server"
"github.com/influxdata/platform/gather"
"github.com/influxdata/platform/http"
"github.com/influxdata/platform/kit/prom"
influxlogger "github.com/influxdata/platform/logger"
"github.com/influxdata/platform/nats"
"github.com/influxdata/platform/query"
_ "github.com/influxdata/platform/query/builtin"
Expand Down
2 changes: 1 addition & 1 deletion gather/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
"time"

"github.com/google/go-cmp/cmp"
influxlogger "github.com/influxdata/influxdb/logger"
"github.com/influxdata/platform"
influxlogger "github.com/influxdata/platform/logger"
"github.com/influxdata/platform/mock"
)

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ require (
github.com/influxdata/roaring v0.4.12 // indirect
github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368
github.com/jessevdk/go-flags v1.4.0
github.com/jsternberg/zap-logfmt v1.2.0 // indirect
github.com/jsternberg/zap-logfmt v1.2.0
github.com/jtolds/gls v4.2.1+incompatible // indirect
github.com/julienschmidt/httprouter v0.0.0-20180715161854-348b672cd90d
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef
github.com/kevinburke/go-bindata v3.11.0+incompatible
github.com/mattn/go-isatty v0.0.4
github.com/mattn/go-zglob v0.0.0-20180803001819-2ea3427bfa53 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/mitchellh/go-homedir v1.0.0 // indirect
Expand Down
2 changes: 1 addition & 1 deletion http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"syscall"
"time"

"github.com/influxdata/influxdb/logger"
"github.com/influxdata/platform/logger"
"go.uber.org/zap"
)

Expand Down
18 changes: 18 additions & 0 deletions logger/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package logger

import (
"go.uber.org/zap/zapcore"
)

type Config struct {
Format string `toml:"format"`
Level zapcore.Level `toml:"level"`
SuppressLogo bool `toml:"suppress-logo"`
}

// NewConfig returns a new instance of Config with defaults.
func NewConfig() Config {
return Config{
Format: "auto",
}
}
24 changes: 24 additions & 0 deletions logger/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package logger

import (
"context"

"go.uber.org/zap"
)

type key int

const (
loggerKey key = iota
)

// NewContextWithLogger returns a new context with log added.
func NewContextWithLogger(ctx context.Context, log *zap.Logger) context.Context {
return context.WithValue(ctx, loggerKey, log)
}

// LoggerFromContext returns the zap.Logger associated with ctx or nil if no logger has been assigned.
func LoggerFromContext(ctx context.Context) *zap.Logger {
l, _ := ctx.Value(loggerKey).(*zap.Logger)
return l
}
111 changes: 111 additions & 0 deletions logger/fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package logger

import (
"time"

"github.com/influxdata/platform/pkg/snowflake"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

const (
// TraceIDKey is the logging context key used for identifying unique traces.
TraceIDKey = "trace_id"

// OperationNameKey is the logging context key used for identifying name of an operation.
OperationNameKey = "op_name"

// OperationEventKey is the logging context key used for identifying a notable
// event during the course of an operation.
OperationEventKey = "op_event"

// OperationElapsedKey is the logging context key used for identifying time elapsed to finish an operation.
OperationElapsedKey = "op_elapsed"

// DBInstanceKey is the logging context key used for identifying name of the relevant database.
DBInstanceKey = "db_instance"

// DBRetentionKey is the logging context key used for identifying name of the relevant retention policy.
DBRetentionKey = "db_rp"

// DBShardGroupKey is the logging context key used for identifying relevant shard group.
DBShardGroupKey = "db_shard_group"

// DBShardIDKey is the logging context key used for identifying name of the relevant shard number.
DBShardIDKey = "db_shard_id"
)
const (
eventStart = "start"
eventEnd = "end"
)

var (
gen = snowflake.New(0)
)

func nextID() string {
return gen.NextString()
}

// TraceID returns a field for tracking the trace identifier.
func TraceID(id string) zapcore.Field {
return zap.String(TraceIDKey, id)
}

// OperationName returns a field for tracking the name of an operation.
func OperationName(name string) zapcore.Field {
return zap.String(OperationNameKey, name)
}

// OperationElapsed returns a field for tracking the duration of an operation.
func OperationElapsed(d time.Duration) zapcore.Field {
return zap.Duration(OperationElapsedKey, d)
}

// OperationEventStart returns a field for tracking the start of an operation.
func OperationEventStart() zapcore.Field {
return zap.String(OperationEventKey, eventStart)
}

// OperationEventFinish returns a field for tracking the end of an operation.
func OperationEventEnd() zapcore.Field {
return zap.String(OperationEventKey, eventEnd)
}

// Database returns a field for tracking the name of a database.
func Database(name string) zapcore.Field {
return zap.String(DBInstanceKey, name)
}

// Database returns a field for tracking the name of a database.
func RetentionPolicy(name string) zapcore.Field {
return zap.String(DBRetentionKey, name)
}

// ShardGroup returns a field for tracking the shard group identifier.
func ShardGroup(id uint64) zapcore.Field {
return zap.Uint64(DBShardGroupKey, id)
}

// Shard returns a field for tracking the shard identifier.
func Shard(id uint64) zapcore.Field {
return zap.Uint64(DBShardIDKey, id)
}

// NewOperation uses the exiting log to create a new logger with context
// containing a trace id and the operation. Prior to returning, a standardized message
// is logged indicating the operation has started. The returned function should be
// called when the operation concludes in order to log a corresponding message which
// includes an elapsed time and that the operation has ended.
func NewOperation(log *zap.Logger, msg, name string, fields ...zapcore.Field) (*zap.Logger, func()) {
f := []zapcore.Field{TraceID(nextID()), OperationName(name)}
if len(fields) > 0 {
f = append(f, fields...)
}

now := time.Now()
log = log.With(f...)
log.Info(msg+" (start)", OperationEventStart())

return log, func() { log.Info(msg+" (end)", OperationEventEnd(), OperationElapsed(time.Since(now))) }
}
127 changes: 127 additions & 0 deletions logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package logger

import (
"fmt"
"io"
"time"

"github.com/jsternberg/zap-logfmt"
isatty "github.com/mattn/go-isatty"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

const TimeFormat = "2006-01-02T15:04:05.000000Z07:00"

func New(w io.Writer) *zap.Logger {
config := NewConfig()
l, _ := config.New(w)
return l
}

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
)

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))
}
Loading

0 comments on commit e7e17d6

Please sign in to comment.