Skip to content

Instantly share code, notes, and snippets.

@buYoung
Last active June 1, 2024 11:56
Show Gist options
  • Save buYoung/817e90a27cd6a55052e9dc1e4154abfd to your computer and use it in GitHub Desktop.
Save buYoung/817e90a27cd6a55052e9dc1e4154abfd to your computer and use it in GitHub Desktop.
Advanced Logging in Go with Zerolog and Lumberjack
package logger
type LogRequestBodyType string
const (
LogRequestBodyTypeFormData = "multipart/form-data"
LogRequestBodyTypeJSON = "application/json"
LogRequestBodyTypeTEXT = "text/plain"
LogRequestBodyTypeURLEncoded = "application/x-www-form-urlencoded"
)
type LogLevel int
const (
LogLevelDebug = -1
LogLevelInfo = 0
LogLevelWarn = 1
LogLevelError = 2
LogLevelDPanic = 3
LogLevelPanic = 4
LogLevelFatal = 5
)
type LogHTTPMethod string
const (
LogHTTPMethodGET = "GET"
LogHTTPMethodPOST = "POST"
LogHTTPMethodPUT = "PUT"
LogHTTPMethodDELETE = "DELETE"
LogHTTPMethodPATCH = "PATCH"
LogHTTPMethodHEAD = "HEAD"
LogHTTPMethodOPTIONS = "OPTIONS"
LogHTTPMethodCONNECT = "CONNECT"
LogHTTPMethodTRACE = "TRACE"
LogHTTPMethodUNKNOWN = "UNKNOWN"
)
type RequestLog[RequestBody any, ResponseBody any] struct {
RequestID string `json:"request_id"`
HTTPMethod LogHTTPMethod `json:"http_method"`
URL string `json:"url"`
ClientIP string `json:"client_ip"`
QueryParameters map[string]string `json:"query_parameters"`
RequestHeaders map[string]string `json:"request_headers"`
RequestBody RequestBody `json:"request_body"`
RequestBodyType LogRequestBodyType `json:"request_body_type"`
ResponseStatusCode int `json:"response_status_code"`
ResponseHeaders map[string]string `json:"response_headers"`
ResponseBody ResponseBody `json:"response_body"`
ProcessingTimes map[string]int `json:"processing_time_ms"`
}
type RequestErrorLog[RequestBody any] struct {
RequestID string `json:"request_id"`
HTTPMethod LogHTTPMethod `json:"http_method"`
URL string `json:"url"`
ClientIP string `json:"client_ip"`
QueryParameters map[string]string `json:"query_parameters"`
RequestHeaders map[string]string `json:"request_headers"`
RequestBody RequestBody `json:"request_body"`
RequestBodyType LogRequestBodyType `json:"request_body_type"`
ProcessingTimes map[string]int `json:"processing_time_ms"`
}
package logger
import (
"fmt"
"io"
"os"
"path"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
)
type Config struct {
IsDevAndShowConsole bool
Directory string
Filename string
MaxSize int
MaxBackups int
MaxAge int
}
type SpecificLevelWriter struct {
io.Writer
Levels []zerolog.Level
isDevAndShowConsole bool
}
func (w SpecificLevelWriter) WriteLevel(level zerolog.Level, p []byte) (int, error) {
for _, l := range w.Levels {
if w.isDevAndShowConsole && l == zerolog.NoLevel {
return w.Write(p) //nolint:wrapcheck
}
if l == level {
return w.Write(p) //nolint:wrapcheck
}
}
return len(p), nil
}
func CreateNewLogger(config Config) (*zerolog.Logger, error) {
writers := zerolog.MultiLevelWriter(
SpecificLevelWriter{
Writer: &lumberjack.Logger{
Filename: path.Join(config.Directory, fmt.Sprintf("%s_%s.%s", config.Filename, "verbose", "log")),
MaxSize: config.MaxSize,
MaxBackups: config.MaxBackups,
MaxAge: config.MaxAge,
Compress: true,
},
Levels: []zerolog.Level{zerolog.TraceLevel},
isDevAndShowConsole: false,
},
SpecificLevelWriter{
Writer: &lumberjack.Logger{
Filename: path.Join(config.Directory, fmt.Sprintf("%s_%s.%s", config.Filename, "info", "log")),
MaxSize: config.MaxSize,
MaxBackups: config.MaxBackups,
MaxAge: config.MaxAge,
Compress: true,
},
Levels: []zerolog.Level{zerolog.InfoLevel, zerolog.DebugLevel, zerolog.WarnLevel},
isDevAndShowConsole: false,
},
SpecificLevelWriter{
Writer: &lumberjack.Logger{
Filename: path.Join(config.Directory, fmt.Sprintf("%s_%s.%s", config.Filename, "error", "log")),
MaxSize: config.MaxSize,
MaxBackups: config.MaxBackups,
MaxAge: config.MaxAge,
Compress: true,
},
Levels: []zerolog.Level{zerolog.ErrorLevel, zerolog.PanicLevel, zerolog.FatalLevel},
isDevAndShowConsole: false,
},
SpecificLevelWriter{
Writer: zerolog.ConsoleWriter{Out: os.Stderr},
Levels: []zerolog.Level{zerolog.NoLevel},
isDevAndShowConsole: config.IsDevAndShowConsole,
},
)
zerolog.TimestampFieldName = "timestamp"
zerolog.TimeFieldFormat = "2006-01-02T15:04:05.000Z"
logger := zerolog.New(writers).With().Timestamp().Logger()
return &logger, nil
}
_logger, err := logger.CreateNewLogger(logger.Config{
IsDevAndShowConsole: true,
Directory: "log",
Filename: "app",
MaxSize: 1,
MaxBackups: 5,
MaxAge: 5,
})
if err != nil {
panic(err)
}
_logger.Info().Interface("http", logger.RequestLog[string, string]{
RequestID: "1234",
ClientIP: "",
HTTPMethod: logger.LogHTTPMethodGET,
URL: "http://localhost:8080",
QueryParameters: map[string]string{
"key": "value",
},
RequestHeaders: map[string]string{
"key": "value",
},
RequestBody: "Hello, World!",
RequestBodyType: logger.LogRequestBodyTypeJSON,
ResponseStatusCode: 200,
ResponseHeaders: map[string]string{
"key": "value",
},
ResponseBody: "Hello, World!",
ProcessingTimes: map[string]int{
"key": 100,
},
}).Msg("Hello, World!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment