Browse Source

restore v 0.2.0

pull/2/head v0.2.1
andrey1s 3 years ago
commit
f9ae79614a
  1. 50
      .drone.yml
  2. 17
      .gitignore
  3. 33
      .golangci.yml
  4. 19
      LICENSE
  5. 5
      README.md
  6. 4
      doc.go
  7. 40
      field.go
  8. 183
      global.go
  9. 3
      go.mod
  10. 22
      level.go
  11. 30
      level_string.go
  12. 223
      logger.go
  13. 126
      logger_example_test.go
  14. 31
      logger_test.go
  15. 111
      middleware.go
  16. 121
      std.go

50
.drone.yml

@ -0,0 +1,50 @@
kind: pipeline
name: default
steps:
- name: test
image: golang:1.14.2
volumes:
- name: deps
path: /go/src/mod
commands:
- go test
- name: golangci-lint
image: golangci/golangci-lint:v1.29
commands:
- golangci-lint run
- name: logrus golangci-lint
image: golangci/golangci-lint:v1.29
commands:
- cd logrus
- golangci-lint run
- name: logrus test
image: golang:1.14.2
volumes:
- name: deps
path: /go/src/mod
commands:
- cd logrus
- go test
- name: zap golangci-lint
image: golangci/golangci-lint:v1.29
commands:
- cd zap
- golangci-lint run
- name: zap test
image: golang:1.14.2
volumes:
- name: deps
path: /go/src/mod
commands:
- cd zap
- go test
volumes:
- name: deps
temp: {}

17
.gitignore

@ -0,0 +1,17 @@
# ---> Go
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/

33
.golangci.yml

@ -0,0 +1,33 @@
linters-settings:
dupl:
threshold: 100
funlen:
lines: 100
statements: 50
goconst:
min-len: 2
min-occurrences: 2
gocyclo:
min-complexity: 15
golint:
min-confidence: 0
govet:
check-shadowing: true
lll:
line-length: 140
maligned:
suggest-new: true
misspell:
locale: US
exhaustive:
default-signifies-exhaustive: true
linters:
enable-all: true
issues:
# Excluding configuration per-path, per-linter, per-text and per-source
exclude-rules:
- path: _test\.go
linters:
- gomnd

19
LICENSE

@ -0,0 +1,19 @@
MIT License Copyright (c) 2020 go-4devs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

5
README.md

@ -0,0 +1,5 @@
# log
[![Build Status](https://drone.gitoa.ru/api/badges/go-4devs/log/status.svg)](https://drone.gitoa.ru/go-4devs/log)
[![Go Report Card](https://goreportcard.com/badge/gitoa.ru/go-4devs/log)](https://goreportcard.com/report/gitoa.ru/go-4devs/log)
[![GoDoc](https://godoc.org/gitoa.ru/go-4devs/log?status.svg)](http://godoc.org/gitoa.ru/go-4devs/log)

4
doc.go

@ -0,0 +1,4 @@
/*
Package log logged data by handler and use processor, exposes eight methods to write logs to the eight RFC 5424 levels.
*/
package log

40
field.go

@ -0,0 +1,40 @@
package log
import (
"fmt"
"strings"
)
// Fields slice field.
type Fields []Field
// String implement stringer.
func (f Fields) String() string {
str := make([]string, len(f))
for i, field := range f {
str[i] = field.String()
}
return strings.Join(str, " ")
}
// NewField create field.
func NewField(key string, value interface{}) Field {
return Field{Key: key, Value: value}
}
// Field struct.
type Field struct {
Key string
Value interface{}
}
// String implent stringer.
func (f Field) String() string {
return fmt.Sprintf("%s=%+v", f.Key, f.Value)
}
// FieldError new errors field with key error.
func FieldError(err error) Field {
return NewField("error", err)
}

183
global.go

@ -0,0 +1,183 @@
package log
import (
"context"
)
//nolint:gochecknoglobals
var global = With(New(), WithLevel(LevelDebug))
// SetLogger sets global used logger. This function is not thread-safe.
func SetLogger(l Logger) {
global = l
}
// GetLogger return global logger.
func GetLogger() Logger {
return global
}
// Emerg log by emergency level.
func Emerg(ctx context.Context, args ...interface{}) {
global.Emerg(ctx, args...)
}
// Alert log by alert level.
func Alert(ctx context.Context, args ...interface{}) {
global.Alert(ctx, args...)
}
// Crit log by critical level.
func Crit(ctx context.Context, args ...interface{}) {
global.Crit(ctx, args...)
}
// Err log by error level.
func Err(ctx context.Context, args ...interface{}) {
global.Err(ctx, args...)
}
// Warn logs by warning level.
func Warn(ctx context.Context, args ...interface{}) {
global.Warn(ctx, args...)
}
// Notice log by notice level.
func Notice(ctx context.Context, args ...interface{}) {
global.Notice(ctx, args...)
}
// Info log by info level.
func Info(ctx context.Context, args ...interface{}) {
global.Info(ctx, args...)
}
// Debug log by debug level.
func Debug(ctx context.Context, args ...interface{}) {
global.Debug(ctx, args...)
}
// Print log by info level and arguments.
func Print(args ...interface{}) {
global.Print(args...)
}
// Fatal log by alert level and arguments.
func Fatal(args ...interface{}) {
global.Fatal(args...)
}
// Panic log by emergency level and arguments.
func Panic(args ...interface{}) {
global.Panic(args...)
}
// Println log by info level and arguments.
func Println(args ...interface{}) {
global.Println(args...)
}
// Fatalln log by alert level and arguments.
func Fatalln(args ...interface{}) {
global.Fatalln(args...)
}
// Panicln log by emergency level and arguments.
func Panicln(args ...interface{}) {
global.Panicln(args...)
}
// EmergKV log by emergency level and key-values.
func EmergKV(ctx context.Context, msg string, args ...interface{}) {
global.EmergKV(ctx, msg, args...)
}
// AlertKV log by alert level and key-values.
func AlertKV(ctx context.Context, msg string, args ...interface{}) {
global.AlertKV(ctx, msg, args...)
}
// CritKV log by critcal level and key-values.
func CritKV(ctx context.Context, msg string, args ...interface{}) {
global.CritKV(ctx, msg, args...)
}
// ErrKV log by error level and key-values.
func ErrKV(ctx context.Context, msg string, args ...interface{}) {
global.ErrKV(ctx, msg, args...)
}
// WarnKV log by warning level and key-values.
func WarnKV(ctx context.Context, msg string, args ...interface{}) {
global.WarnKV(ctx, msg, args...)
}
// NoticeKV log by notice level and key-values.
func NoticeKV(ctx context.Context, msg string, args ...interface{}) {
global.NoticeKV(ctx, msg, args...)
}
// InfoKV log by info level and key-values.
func InfoKV(ctx context.Context, msg string, args ...interface{}) {
global.InfoKV(ctx, msg, args...)
}
// DebugKV log by debug level and key-values.
func DebugKV(ctx context.Context, msg string, args ...interface{}) {
global.DebugKV(ctx, msg, args...)
}
// Emergf log by emergency level by format and arguments.
func Emergf(ctx context.Context, format string, args ...interface{}) {
global.Emergf(ctx, format, args...)
}
// Alertf log by alert level by format and arguments.
func Alertf(ctx context.Context, format string, args ...interface{}) {
global.Alertf(ctx, format, args...)
}
// Critf log by critical level by format and arguments.
func Critf(ctx context.Context, format string, args ...interface{}) {
global.Critf(ctx, format, args...)
}
// Errf log by error level by format and arguments.
func Errf(ctx context.Context, format string, args ...interface{}) {
global.Errf(ctx, format, args...)
}
// Warnf log by warning level by format and arguments.
func Warnf(ctx context.Context, format string, args ...interface{}) {
global.Warnf(ctx, format, args...)
}
// Noticef log by notice level by format and arguments.
func Noticef(ctx context.Context, format string, args ...interface{}) {
global.Noticef(ctx, format, args...)
}
// Infof log by info level by format and arguments.
func Infof(ctx context.Context, format string, args ...interface{}) {
global.Noticef(ctx, format, args...)
}
// Debugf log by debug level by format and arguments.
func Debugf(ctx context.Context, format string, args ...interface{}) {
global.Debugf(ctx, format, args...)
}
// Printf log by info level by format and arguments without context.
func Printf(format string, args ...interface{}) {
global.Printf(format, args...)
}
// Fatalf log by alert level by format and arguments without context.
func Fatalf(format string, args ...interface{}) {
global.Fatalf(format, args...)
}
// Panicf log by emergency level and arguments without context.
func Panicf(format string, args ...interface{}) {
global.Panicf(format, args...)
}

3
go.mod

@ -0,0 +1,3 @@
module gitoa.ru/go-4devs/log
go 1.14

22
level.go

@ -0,0 +1,22 @@
package log
//go:generate stringer -type=Level -linecomment
// Level log.
type Level uint8
// awailable log levels.
const (
LevelEmergency Level = iota // emergency
LevelAlert // alert
LevelCritical // critical
LevelError // error
LevelWarning // warning
LevelNotice // notice
LevelInfo // info
LevelDebug // debug
)
func (l Level) MarshalJSON() ([]byte, error) {
return []byte("\"" + l.String() + "\""), nil
}

30
level_string.go

@ -0,0 +1,30 @@
// Code generated by "stringer -type=Level -linecomment"; DO NOT EDIT.
package log
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[LevelEmergency-0]
_ = x[LevelAlert-1]
_ = x[LevelCritical-2]
_ = x[LevelError-3]
_ = x[LevelWarning-4]
_ = x[LevelNotice-5]
_ = x[LevelInfo-6]
_ = x[LevelDebug-7]
}
const _Level_name = "emergencyalertcriticalerrorwarningnoticeinfodebug"
var _Level_index = [...]uint8{0, 9, 14, 22, 27, 34, 40, 44, 49}
func (i Level) String() string {
if i >= Level(len(_Level_index)-1) {
return "Level(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Level_name[_Level_index[i]:_Level_index[i+1]]
}

223
logger.go

@ -0,0 +1,223 @@
package log
import (
"context"
"fmt"
)
// Logger logged message.
type Logger func(ctx context.Context, level Level, msg string, fields Fields)
func (l Logger) log(ctx context.Context, level Level, args ...interface{}) {
l(ctx, level, fmt.Sprint(args...), nil)
}
func (l Logger) logKV(ctx context.Context, level Level, msg string, args ...interface{}) {
l(ctx, level, msg, l.kv(ctx, args...))
}
func (l Logger) logf(ctx context.Context, level Level, format string, args ...interface{}) {
l(ctx, level, fmt.Sprintf(format, args...), nil)
}
func (l Logger) logln(ctx context.Context, level Level, args ...interface{}) {
l(ctx, level, fmt.Sprintln(args...), nil)
}
func (l Logger) kv(ctx context.Context, args ...interface{}) []Field {
fields := make([]Field, 0, len(args))
for i := 0; i < len(args); i++ {
if f, ok := args[i].(Field); ok {
fields = append(fields, f)
continue
}
if i == len(args)-1 {
l(ctx, LevelCritical, fmt.Sprint("Ignored key without a value.", args[i]), fields)
break
}
i++
key, val := args[i-1], args[i]
if keyStr, ok := key.(string); ok {
fields = append(fields, Field{Key: keyStr, Value: val})
continue
}
l(ctx, LevelCritical, fmt.Sprint("Ignored key-value pairs with non-string keys.", key, val), fields)
}
return fields
}
// With adds middlewares to logger.
func (l Logger) With(mw ...Middleware) Logger {
return With(l, mw...)
}
// Emerg log by emergency level.
func (l Logger) Emerg(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelEmergency, args...)
}
// Alert log by alert level.
func (l Logger) Alert(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelAlert, args...)
}
// Crit log by critical level.
func (l Logger) Crit(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelCritical, args...)
}
// Err log by error level.
func (l Logger) Err(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelError, args...)
}
// Warn log by warning level.
func (l Logger) Warn(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelWarning, args...)
}
// Notice log by notice level.
func (l Logger) Notice(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelNotice, args...)
}
// Info log by info level.
func (l Logger) Info(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelInfo, args...)
}
// Debug log by debug level.
func (l Logger) Debug(ctx context.Context, args ...interface{}) {
l.log(ctx, LevelDebug, args...)
}
// Print log by info level and arguments.
func (l Logger) Print(args ...interface{}) {
l.log(context.Background(), LevelInfo, args...)
}
// Fatal log by alert level and arguments.
func (l Logger) Fatal(args ...interface{}) {
l.log(context.Background(), LevelAlert, args...)
}
// Panic log by emergency level and arguments.
func (l Logger) Panic(args ...interface{}) {
l.log(context.Background(), LevelEmergency, args...)
}
// Println log by info level and arguments.
func (l Logger) Println(args ...interface{}) {
l.logln(context.Background(), LevelInfo, args...)
}
// Fatalln log by alert level and arguments.
func (l Logger) Fatalln(args ...interface{}) {
l.logln(context.Background(), LevelAlert, args...)
}
// Panicln log by emergency level and arguments.
func (l Logger) Panicln(args ...interface{}) {
l.logln(context.Background(), LevelEmergency, args...)
}
// EmergKV log by emergency level and key-values.
func (l Logger) EmergKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelEmergency, msg, args...)
}
// AlertKV log by alert level and key-values.
func (l Logger) AlertKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelAlert, msg, args...)
}
// CritKV log by critcal level and key-values.
func (l Logger) CritKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelCritical, msg, args...)
}
// ErrKV log by error level and key-values.
func (l Logger) ErrKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelError, msg, args...)
}
// WarnKV log by warning level and key-values.
func (l Logger) WarnKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelWarning, msg, args...)
}
// NoticeKV log by notice level and key-values.
func (l Logger) NoticeKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelNotice, msg, args...)
}
// InfoKV log by info level and key-values.
func (l Logger) InfoKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelInfo, msg, args...)
}
// DebugKV log by debug level and key-values.
func (l Logger) DebugKV(ctx context.Context, msg string, args ...interface{}) {
l.logKV(ctx, LevelDebug, msg, args...)
}
// Emergf log by emergency level by format and arguments.
func (l Logger) Emergf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelEmergency, format, args...)
}
// Alertf log by alert level by format and arguments.
func (l Logger) Alertf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelAlert, format, args...)
}
// Critf log by critical level by format and arguments.
func (l Logger) Critf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelCritical, format, args...)
}
// Errf log by error level by format and arguments.
func (l Logger) Errf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelError, format, args...)
}
// Warnf log by warning level by format and arguments.
func (l Logger) Warnf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelWarning, format, args...)
}
// Noticef log by notice level by format and arguments.
func (l Logger) Noticef(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelNotice, format, args...)
}
// Infof log by info level by format and arguments.
func (l Logger) Infof(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelInfo, format, args...)
}
// Debugf log by debug level by format and arguments.
func (l Logger) Debugf(ctx context.Context, format string, args ...interface{}) {
l.logf(ctx, LevelDebug, format, args...)
}
// Printf log by info level by format and arguments without context.
func (l Logger) Printf(format string, args ...interface{}) {
l.logf(context.Background(), LevelInfo, format, args...)
}
// Fatalf log by alert level by format and arguments without context.
func (l Logger) Fatalf(format string, args ...interface{}) {
l.logf(context.Background(), LevelAlert, format, args...)
}
// Panicf log by emergency level and arguments without context.
func (l Logger) Panicf(format string, args ...interface{}) {
l.logf(context.Background(), LevelEmergency, format, args...)
}

126
logger_example_test.go

@ -0,0 +1,126 @@
package log_test
import (
"context"
"fmt"
std "log"
"os"
"gitoa.ru/go-4devs/log"
)
//nolint:gochecknoglobals
var ctx = context.Background()
func ExampleNew() {
logger := log.New(log.WithStdout())
logger.Info(ctx, "same message")
// Output: msg="same message"
}
func ExampleInfo() {
std.SetOutput(os.Stdout)
std.SetFlags(0)
log.Info(ctx, "same message")
// Output: msg="same message" level=info
}
func ExampleErrKV() {
std.SetOutput(os.Stdout)
std.SetFlags(0)
log.ErrKV(ctx, "same message", "key", "addition value")
// Output: msg="same message" key=addition value level=error
}
func ExampleNew_errf() {
logger := log.New(log.WithStdout())
logger.Errf(ctx, "same message %d", 1)
// Output: msg="same message 1"
}
func ExampleNew_debugKV() {
logger := log.New(log.WithStdout()).With(log.WithLevel(log.LevelDebug))
logger.DebugKV(ctx, "same message", "error", os.ErrNotExist)
// Output: msg="same message" error=file does not exist level=debug
}
func ExampleNew_level() {
logger := log.New(log.WithStdout()).With(log.WithLevel(log.LevelError))
logger.Info(ctx, "same message")
// Output:
logger.Err(ctx, "same error message")
// Output: msg="same error message" level=error
}
func ExampleNew_jsonFormat() {
logger := log.New(log.WithStdout(), log.WithJSONFormat()).
With(
log.WithCaller(4, true),
log.WithLevel(log.LevelDebug),
log.GoVersion("go-version"),
)
logger.Err(ctx, "same error message")
// Output: {"caller":"logger_example_test.go:63","go-version":"go1.14.2","level":"error","msg":"same error message"}
}
func ExampleNew_withLogger() {
stdlogger := std.New(os.Stdout, "same prefix ", std.Lshortfile)
logger := log.With(
log.New(
log.WithLogger(
stdlogger,
func(msg string, fields log.Fields) string {
return fmt.Sprint("msg=\"", msg, "\" ", fields)
},
),
log.WithCalldepth(9),
),
log.WithLevel(log.LevelDebug),
log.GoVersion("go-version"),
)
logger.Err(ctx, "same error message")
logger.InfoKV(ctx, "same info message", "api-version", 0.1)
// Output:
// same prefix logger_example_test.go:82: msg="same error message" level=error go-version=go1.14.2
// same prefix logger_example_test.go:83: msg="same info message" api-version=0.1 level=info go-version=go1.14.2
}
type ctxKey string
func (c ctxKey) String() string {
return string(c)
}
func levelInfo(ctx context.Context, level log.Level, msg string, fields log.Fields, handler log.Logger) {
handler(ctx, level, msg, append(fields, log.Field{Key: "level", Value: level}))
}
func ExampleWith() {
var requestID ctxKey = "requestID"
vctx := context.WithValue(ctx, requestID, "6a5fa048-7181-11ea-bc55-0242ac130003")
logger := log.With(
log.New(log.WithStdout()),
levelInfo, log.WithContextValue(requestID), log.KeyValue("api", "0.1.0"), log.GoVersion("go"),
)
logger.Info(vctx, "same message")
// Output: msg="same message" level=info requestID=6a5fa048-7181-11ea-bc55-0242ac130003 api=0.1.0 go=go1.14.2
}
func ExampleLogger_Print() {
logger := log.With(
log.New(log.WithStdout()),
levelInfo, log.KeyValue("client", "http"), log.KeyValue("api", "0.1.0"), log.GoVersion("go"),
)
logger.Print("same message")
// Output: msg="same message" level=info client=http api=0.1.0 go=go1.14.2
}
func ExamplePrint() {
std.SetOutput(os.Stdout)
std.SetFlags(0)
log.Print("same message")
// Output: msg="same message" level=info
}

31
logger_test.go

@ -0,0 +1,31 @@
package log_test
import (
"bytes"
"context"
"os"
"testing"
"gitoa.ru/go-4devs/log"
)
func TestFields(t *testing.T) {
type rObj struct {
id string
}
ctx := context.Background()
buf := &bytes.Buffer{}
log := log.New(log.WithWriter(buf))
success := "msg=\"message\" err=file already exists version=0.1.0 obj={id:uid}\n"
log.InfoKV(ctx, "message",
"err", os.ErrExist,
"version", "0.1.0",
"obj", rObj{id: "uid"},
)
if success != buf.String() {
t.Errorf("invalid value\n got:%s\n exp:%s", buf, success)
}
}

111
middleware.go

@ -0,0 +1,111 @@
package log
import (
"context"
"fmt"
"path/filepath"
"runtime"
"time"
)
// Middleware handle.
type Middleware func(ctx context.Context, level Level, msg string, fields Fields, handler Logger)
// With add middleware to logger.
func With(logger Logger, mw ...Middleware) Logger {
switch len(mw) {
case 0:
return logger
case 1:
return func(ctx context.Context, level Level, msg string, fields Fields) {
mw[0](ctx, level, msg, fields, logger)
}
}
lastI := len(mw) - 1
return func(ctx context.Context, level Level, msg string, fields Fields) {
var (
chainHandler func(ctx context.Context, level Level, msg string, fields Fields)
curI int
)
chainHandler = func(currentCtx context.Context, currentLevel Level, currentMsg string, currentFields Fields) {
if curI == lastI {
logger(currentCtx, currentLevel, currentMsg, currentFields)
return
}
curI++
mw[curI](currentCtx, currentLevel, currentMsg, currentFields, chainHandler)
curI--
}
mw[0](ctx, level, msg, fields, chainHandler)
}
}
// WithLevel sets log level.
func WithLevel(lvl Level) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
if level <= lvl {
handler(ctx, level, msg, append(fields, Field{Key: "level", Value: level}))
}
}
}
// KeyValue add field by const key value.
func KeyValue(key string, value interface{}) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
handler(ctx, level, msg, append(fields, Field{Key: key, Value: value}))
}
}
// GoVersion add field by go version.
func GoVersion(key string) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
handler(ctx, level, msg, append(fields, Field{Key: key, Value: runtime.Version()}))
}
}
// WithContext add field by context key.
func WithContextValue(keys ...fmt.Stringer) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
ctxFields := make(Fields, len(keys))
for i, key := range keys {
ctxFields[i] = Field{Key: key.String(), Value: ctx.Value(key)}
}
handler(ctx, level, msg, append(fields, ctxFields...))
}
}
// WithCaller adds called file.
func WithCaller(calldepth int, short bool) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
_, file, line, ok := runtime.Caller(calldepth)
if !ok {
file, line = "???", 0
}
if short && ok {
file = filepath.Base(file)
}
handler(ctx, level, msg, append(fields, NewField("caller", fmt.Sprint(file, ":", line))))
}
}
// WithTime adds time.
func WithTime(format string) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
handler(ctx, level, msg, append(fields, NewField("time", time.Now().Format(format))))
}
}
// WithMetrics adds handle metrics.
func WithMetrics(metrics func(level Level)) Middleware {
return func(ctx context.Context, level Level, msg string, fields Fields, handler Logger) {
go metrics(level)
handler(ctx, level, msg, fields)
}
}

121
std.go

@ -0,0 +1,121 @@
package log
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"os"
)
const (
calldepth = 3
)
// New creates standart logger.
func New(opts ...Option) Logger {
logger := logger{
format: stringFormat,
output: log.Output,
calldepth: calldepth,
}
for _, opt := range opts {
opt(&logger)
}
return func(ctx context.Context, level Level, msg string, fields Fields) {
_ = logger.output(logger.calldepth, logger.format(msg, fields))
switch level {
case LevelEmergency:
panic(msg)
case LevelAlert:
os.Exit(1)
default:
}
}
}
// Option configure log.
type Option func(*logger)
// Format sets formats output message.
type Format func(msg string, fields Fields) string
type logger struct {
output func(calldepth int, s string) error
format Format
calldepth int
}
// WithWriter sets writer logger.
func WithWriter(writer io.Writer) Option {
return func(l *logger) {
l.output = log.New(writer, "", 0).Output
}
}
// WithStdout sets logged to os.Stdout.
func WithStdout() Option {
return func(l *logger) {
l.output = log.New(os.Stdout, "", 0).Output
}
}
// WithFormat sets format log.
func WithFormat(format Format) Option {
return func(l *logger) {
l.format = format
}
}
// WithStringFormat sets format as simple string.
func WithStringFormat() Option {
return func(l *logger) {
l.format = stringFormat
}
}
// WithJSONFormat sets json output format.
func WithJSONFormat() Option {
return func(l *logger) {
l.format = jsonFormat
}
}
// WithCalldepth sets depth filename.
func WithCalldepth(calldepth int) Option {
return func(l *logger) {
l.calldepth = calldepth
}
}
// WithLogger sets logger anf format.
func WithLogger(std *log.Logger, format Format) Option {
return func(l *logger) {
l.output = std.Output
l.format = format
}
}
func stringFormat(msg string, fields Fields) string {
return fmt.Sprint("msg=\"", msg, "\" ", fields)
}
func jsonFormat(msg string, fields Fields) string {
data := make(map[string]interface{}, len(fields)+1)
data["msg"] = msg
for _, field := range fields {
data[field.Key] = field.Value
}
res, err := json.Marshal(data)
if err != nil {
return stringFormat(msg, append(fields, FieldError(err)))
}
return string(res)
}
Loading…
Cancel
Save