andrey1s
3 years ago
commit
f9ae79614a
16 changed files with 1018 additions and 0 deletions
@ -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: {} |
@ -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/ |
|||
|
@ -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 |
@ -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. |
@ -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) |
@ -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 |
@ -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) |
|||
} |
@ -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...) |
|||
} |
@ -0,0 +1,3 @@ |
|||
module gitoa.ru/go-4devs/log |
|||
|
|||
go 1.14 |
@ -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 |
|||
} |
@ -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]] |
|||
} |
@ -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...) |
|||
} |
@ -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
|
|||
} |
@ -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) |
|||
} |
|||
} |
@ -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) |
|||
} |
|||
} |
@ -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…
Reference in new issue