|
|
|
@ -1,6 +1,8 @@ |
|
|
|
package logger |
|
|
|
|
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"log" |
|
|
|
"net/url" |
|
|
|
@ -15,8 +17,14 @@ import ( |
|
|
|
|
|
|
|
var logger Logger |
|
|
|
|
|
|
|
const ( |
|
|
|
DefaultLogOutput = "default" |
|
|
|
StdErrLogOutput = "stderr" |
|
|
|
StdOutLogOutput = "stdout" |
|
|
|
) |
|
|
|
|
|
|
|
func init() { |
|
|
|
InitLog(os.Getenv("LOG_LEVEL")) |
|
|
|
InitLog(os.Getenv("LOG_LEVEL"), nil, 0, "") |
|
|
|
} |
|
|
|
|
|
|
|
// Logger logger interface
|
|
|
|
@ -27,14 +35,6 @@ type Logger interface { |
|
|
|
Errorf(format string, args ...interface{}) |
|
|
|
} |
|
|
|
|
|
|
|
type lumberjackSink struct { |
|
|
|
*lumberjack.Logger |
|
|
|
} |
|
|
|
|
|
|
|
func (lumberjackSink) Sync() error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// WithLogger replaces default logger
|
|
|
|
func WithLogger(log Logger) { |
|
|
|
logger = log |
|
|
|
@ -42,27 +42,97 @@ func WithLogger(log Logger) { |
|
|
|
|
|
|
|
// InitLog is an initialization for a logger
|
|
|
|
// level can be: debug info warn error
|
|
|
|
func InitLog(level string) { |
|
|
|
func InitLog(level string, outputs []string, logRotationEnable int64, logRotateConfigJSON string) { |
|
|
|
if len(outputs) == 0 { |
|
|
|
outputs = []string{DefaultLogOutput} |
|
|
|
} |
|
|
|
|
|
|
|
// parse outputs
|
|
|
|
outputPaths := make([]string, 0) |
|
|
|
for _, v := range outputs { |
|
|
|
switch v { |
|
|
|
case DefaultLogOutput: |
|
|
|
outputPaths = append(outputPaths, StdOutLogOutput) |
|
|
|
|
|
|
|
case StdErrLogOutput: |
|
|
|
outputPaths = append(outputPaths, StdErrLogOutput) |
|
|
|
|
|
|
|
case StdOutLogOutput: |
|
|
|
outputPaths = append(outputPaths, StdOutLogOutput) |
|
|
|
|
|
|
|
default: |
|
|
|
var path string |
|
|
|
if logRotationEnable != 0 { |
|
|
|
// append rotate scheme to logs managed by lumberjack log rotation
|
|
|
|
if v[0:1] == "/" { |
|
|
|
path = fmt.Sprintf("lumberjack:/%%2F%s", v[1:]) |
|
|
|
} else { |
|
|
|
path = fmt.Sprintf("lumberjack:/%s", v) |
|
|
|
} |
|
|
|
} else { |
|
|
|
path = v |
|
|
|
} |
|
|
|
outputPaths = append(outputPaths, path) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// setup log rotation
|
|
|
|
if logRotationEnable != 0 { |
|
|
|
_ = setupLogRotation(outputs, logRotateConfigJSON) |
|
|
|
} |
|
|
|
|
|
|
|
config := loadConfig(level) |
|
|
|
config.OutputPaths = outputPaths |
|
|
|
p, err := config.Build(zap.AddCallerSkip(1)) |
|
|
|
FatalIfError(err) |
|
|
|
logger = p.Sugar() |
|
|
|
} |
|
|
|
|
|
|
|
// InitRotateLog is an initialization for a rotated logger by lumberjack
|
|
|
|
func InitRotateLog(logLevel string, ll *lumberjack.Logger) { |
|
|
|
config := loadConfig(logLevel) |
|
|
|
config.OutputPaths = []string{fmt.Sprintf("lumberjack:%s", ll.Filename), "stdout"} |
|
|
|
err := zap.RegisterSink("lumberjack", func(*url.URL) (zap.Sink, error) { |
|
|
|
return lumberjackSink{ |
|
|
|
Logger: ll, |
|
|
|
}, nil |
|
|
|
}) |
|
|
|
FatalIfError(err) |
|
|
|
type lumberjackSink struct { |
|
|
|
*lumberjack.Logger |
|
|
|
} |
|
|
|
|
|
|
|
p, err := config.Build(zap.AddCallerSkip(1)) |
|
|
|
func (lumberjackSink) Sync() error { |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
// setupLogRotation initializes log rotation for a single file path target.
|
|
|
|
func setupLogRotation(logOutputs []string, logRotateConfigJSON string) error { |
|
|
|
var lumberjackSink lumberjackSink |
|
|
|
outputFilePaths := 0 |
|
|
|
for _, v := range logOutputs { |
|
|
|
switch v { |
|
|
|
case "stdout", "stderr": |
|
|
|
continue |
|
|
|
default: |
|
|
|
outputFilePaths++ |
|
|
|
} |
|
|
|
} |
|
|
|
// log rotation requires file target
|
|
|
|
if len(logOutputs) == 1 && outputFilePaths == 0 { |
|
|
|
FatalIfError(fmt.Errorf("log outputs requires a single file path when LogRotationConfigJson is defined")) |
|
|
|
} |
|
|
|
// support max 1 file target for log rotation
|
|
|
|
if outputFilePaths > 1 { |
|
|
|
FatalIfError(fmt.Errorf("log outputs requires a single file path when LogRotationConfigJson is defined")) |
|
|
|
} |
|
|
|
|
|
|
|
if err := json.Unmarshal([]byte(logRotateConfigJSON), &lumberjackSink); err != nil { |
|
|
|
var unmarshalTypeError *json.UnmarshalTypeError |
|
|
|
var syntaxError *json.SyntaxError |
|
|
|
switch { |
|
|
|
case errors.As(err, &syntaxError): |
|
|
|
FatalIfError(fmt.Errorf("improperly formatted log rotation config: %w", err)) |
|
|
|
case errors.As(err, &unmarshalTypeError): |
|
|
|
FatalIfError(fmt.Errorf("invalid log rotation config: %w", err)) |
|
|
|
} |
|
|
|
} |
|
|
|
err := zap.RegisterSink("lumberjack", func(u *url.URL) (zap.Sink, error) { |
|
|
|
lumberjackSink.Filename = u.Path[1:] |
|
|
|
return &lumberjackSink, nil |
|
|
|
}) |
|
|
|
FatalIfError(err) |
|
|
|
logger = p.Sugar() |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func loadConfig(logLevel string) zap.Config { |
|
|
|
|