166 lines
4.1 KiB
Go
166 lines
4.1 KiB
Go
package logger
|
|
|
|
import (
|
|
"bytes"
|
|
"go/weather/pkg/headers"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"go.elastic.co/apm/module/apmzap"
|
|
"go.elastic.co/ecszap"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
// available level are
|
|
// DebugLevel
|
|
// InfoLevel
|
|
// WarnLevel
|
|
// ErrorLevel
|
|
// DPanicLevel
|
|
// PanicLevel
|
|
// FatalLevel
|
|
|
|
//WeatherLogger custom logger with totation and http Request and Response capability
|
|
type WeatherLogger struct {
|
|
*zap.Logger
|
|
Level zap.AtomicLevel
|
|
}
|
|
|
|
//NewLogger create new named logger
|
|
func NewLogger(loggerName, logPath string, level zap.AtomicLevel) *WeatherLogger {
|
|
rotationPolicy := &lumberjack.Logger{
|
|
Filename: logPath,
|
|
MaxSize: 5, // megabytes
|
|
MaxBackups: 2, // number of backup
|
|
// MaxAge: 28, // days
|
|
Compress: true, // disabled by default
|
|
}
|
|
|
|
rotateCatcher(rotationPolicy)
|
|
|
|
encoderConfig := ecszap.EncoderConfig{
|
|
EncodeLevel: zapcore.CapitalLevelEncoder,
|
|
EncodeDuration: zapcore.MillisDurationEncoder,
|
|
EncodeCaller: ecszap.FullCallerEncoder,
|
|
EnableStackTrace: false,
|
|
}
|
|
|
|
// log enabled by atomic level and error priority
|
|
errorPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
return lvl >= level.Level() && lvl >= zapcore.ErrorLevel
|
|
})
|
|
// log enabled by atomic level and low priority
|
|
lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
return lvl >= level.Level() && lvl < zapcore.ErrorLevel
|
|
})
|
|
consoleDebugging := zapcore.Lock(os.Stdout)
|
|
consoleErrors := zapcore.Lock(os.Stderr)
|
|
consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
|
|
|
|
core := zapcore.NewTee(
|
|
ecszap.NewCore(encoderConfig, zapcore.AddSync(rotationPolicy), level),
|
|
|
|
zapcore.NewCore(consoleEncoder, consoleDebugging, lowPriority),
|
|
zapcore.NewCore(consoleEncoder, consoleErrors, errorPriority),
|
|
)
|
|
|
|
logger := zap.New(core, zap.AddCaller())
|
|
|
|
return &WeatherLogger{
|
|
Logger: logger.Named(loggerName),
|
|
Level: level,
|
|
}
|
|
}
|
|
|
|
//HTTPLogHandler http handler to log http request and http response
|
|
func (wl *WeatherLogger) HTTPLogHandler(next http.Handler) http.Handler {
|
|
// dt := apm.DefaultTracer
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
ww := ResponseWriter{
|
|
ResponseWriter: w,
|
|
}
|
|
|
|
// span, ctx := apm.StartSpan(r.Context(), "updateRequestCount", "custom")
|
|
// defer span.End()
|
|
|
|
wl.LogHTTPRequest(r)
|
|
next.ServeHTTP(&ww, r)
|
|
wl.LogHTTPResponse(ww)
|
|
})
|
|
}
|
|
|
|
//LogHTTPRequest print request information
|
|
func (wl *WeatherLogger) LogHTTPRequest(r *http.Request) {
|
|
var buf bytes.Buffer
|
|
b, err := ioutil.ReadAll(io.TeeReader(r.Body, &buf))
|
|
wl.handleError(err)
|
|
r.Body = struct {
|
|
io.Reader
|
|
io.Closer
|
|
}{&buf, r.Body}
|
|
|
|
lwith := wl.Logger.
|
|
With(apmzap.TraceContext(r.Context())...).
|
|
With([]zap.Field{
|
|
zap.String("http.request.method", r.Method),
|
|
// zap.Int64("http.request.bytes", r.Header ContentLength), // total body+header len
|
|
zap.String("http.request.mime_type", r.Header.Get(headers.ContentType)),
|
|
// zap.String(""),
|
|
// zap.String(""),
|
|
}...)
|
|
|
|
if len(b) > 0 {
|
|
lwith.With([]zap.Field{
|
|
zap.ByteString("http.request.body.content", b),
|
|
zap.Int("http.request.body.bytes", len(b)),
|
|
}...)
|
|
}
|
|
|
|
// realy log
|
|
lwith.Debug("Request")
|
|
}
|
|
|
|
//LogHTTPResponse print response information
|
|
func (wl *WeatherLogger) LogHTTPResponse(ww ResponseWriter) {
|
|
wl.Logger.Debug("Response",
|
|
zap.Int("http.response.status_code", ww.Status),
|
|
zap.Int("http.response.body.bytes", ww.Length),
|
|
)
|
|
}
|
|
|
|
//rotateCatcher force log rotation on SIGUSR1 signal
|
|
func rotateCatcher(rotationPolicy *lumberjack.Logger) {
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, syscall.SIGUSR1)
|
|
go func() {
|
|
for {
|
|
<-c
|
|
rotationPolicy.Rotate()
|
|
}
|
|
}()
|
|
}
|
|
|
|
//handleError generic fatal error
|
|
func (wl *WeatherLogger) handleError(err error) {
|
|
if err != nil {
|
|
wl.Logger.Fatal("Logger error", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
//GetAwsLevelnconvert log zapcore.Level to aws.LogLevelType
|
|
func (wl *WeatherLogger) GetAwsLevel() aws.LogLevelType {
|
|
switch wl.Level.Level() {
|
|
case zapcore.DebugLevel:
|
|
return aws.LogDebug
|
|
default:
|
|
return aws.LogOff
|
|
}
|
|
}
|