상황별 로그인 이동
26302 단어 developmentgologgingcontext
방금 일어났는데 오류 로그를 추적하기 위해 taboo을 생성했던 때를 어떻게든 기억합니다. 그런 다음 오류 로그 자체 대신 로거를 전달하면 더 좋을까요?
컨텍스트가 전달되면 내부에
x-request-id
가 있는 하위 로거가 포함됩니다. HTTP 처리기에서 요청된 모든 이벤트를 함수의 가장 구석까지 추적합니다.일어나자마자 바로 쓴거라 잘 될지 모르겠네요. 이는 최적화되지 않았거나 안티패턴일 수 있습니다. 그러나 적어도 이것은 상황에 맞는 로거를 만드는 데 참조가 될 수 있습니다.
나무꾼
먼저 로거가 필요합니다. 무엇이든 될 수 있지만 이 게시물에서는 zerolog을 사용하겠습니다. Zerolog에 대해서는 아무거나 사용할 수 있으므로 설명하지 않겠습니다.
로깅 스타일과 일치하도록 기본 로거를 포맷합니다.
func configureBaseLogger() {
output := zerolog.ConsoleWriter{
Out: os.Stdout,
FormatTimestamp: func(i interface{}) string {
parse, _ := time.Parse(time.RFC3339, i.(string))
return parse.Format("2006-01-02 15:04:05")
},
FormatLevel: func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf(" %-6s ", i))
},
}
log.Logger = zerolog.New(output).With().
Timestamp().CallerWithSkipFrameCount(2).Logger()
}
기본 로거를 테스트해 봅시다
func main() {
configureBaseLogger()
log.Info().Msg("testing")
}
산출:
2020-12-19 01:01:37 INFO logger/main.go:57 > testing
LGTM! 핸들러를 계속 만들어 봅시다!
미들웨어
여기에서 미들웨어를 사용하여 요청을 기록하겠습니다. 그래서 내 미들웨어와 일치하는 새로운 유형의 핸들러를 만들 것입니다.
type HandlerFuncWithStatus func(writer http.ResponseWriter, request *http.Request) int
const (
LoggerCtxKey = iota
)
func middleware(next HandlerFuncWithStatus) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
requestId := request.Header.Get("x-request-id")
if requestId == "" {
requestId = uuid.New().String()
}
subLogger := log.With().Str("requestId", requestId).Logger()
subLogger.Info().
Str("path", request.URL.Path).
Str("method", request.Method).Msg("in")
ctx := context.WithValue(context.Background(), LoggerCtxKey, subLogger)
statusCode := next(writer, request.WithContext(ctx))
writer.WriteHeader(statusCode)
subLogger.Info().Int("status", statusCode).Msg("out")
}
}
주요 아이디어는 다음과 같습니다.
x-request-id
가져오기, 그렇지 않으면 UUIDv4 형식으로 생성request id
로 시작합니다.HTTP 핸들러
이제 미들웨어를 사용할 http 핸들러를 만들겠습니다. 화려할 필요는 없습니다.
func anotherFunc(ctx context.Context) {
logger := ctx.Value(LoggerCtxKey).(zerolog.Logger)
logger.Info().Msg("inside anotherFunc")
}
func handler (writer http.ResponseWriter, request *http.Request) int {
logger := request.Context().Value(LoggerCtxKey).(zerolog.Logger)
logger.Info().Msg("inside handler")
anotherFunc(request.Context())
return 204
}
따라서 이제 미들웨어에서 전달된 컨텍스트가 있는 모든 함수는 컨텍스트 키로 로거에 액세스할 수 있습니다.
모든 코드를 조합해 봅시다.
결정적인
이것이 우리 코드의 최종 모습이 될 것입니다.
package main
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"net/http"
"os"
"strings"
"time"
)
type HandlerFuncWithStatus func(writer http.ResponseWriter, request *http.Request) int
const (
LoggerCtxKey = iota
)
func middleware(next HandlerFuncWithStatus) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
requestId := request.Header.Get("x-request-id")
if requestId == "" {
requestId = uuid.New().String()
}
subLogger := log.With().Str("requestId", requestId).Logger()
subLogger.Info().
Str("path", request.URL.Path).
Str("method", request.Method).Msg("in")
ctx := context.WithValue(context.Background(), LoggerCtxKey, subLogger)
statusCode := next(writer, request.WithContext(ctx))
writer.WriteHeader(statusCode)
subLogger.Info().Int("status", statusCode).Msg("out")
}
}
func anotherFunc(ctx context.Context) {
logger := ctx.Value(LoggerCtxKey).(zerolog.Logger)
logger.Info().Msg("inside anotherFunc")
}
func handler (writer http.ResponseWriter, request *http.Request) int {
logger := request.Context().Value(LoggerCtxKey).(zerolog.Logger)
logger.Info().Msg("inside handler")
anotherFunc(request.Context())
return 204
}
func configureBaseLogger() {
output := zerolog.ConsoleWriter{
Out: os.Stdout,
FormatTimestamp: func(i interface{}) string {
parse, _ := time.Parse(time.RFC3339, i.(string))
return parse.Format("2006-01-02 15:04:05")
},
FormatLevel: func(i interface{}) string {
return strings.ToUpper(fmt.Sprintf(" %-6s ", i))
},
}
log.Logger = zerolog.New(output).With().
Timestamp().CallerWithSkipFrameCount(2).Logger()
}
func main() {
configureBaseLogger()
log.Info().Msg("testing")
http.Handle("/", middleware(handler))
if err := http.ListenAndServe(":8000", nil) ; err != nil {
log.Error().Msg(err.Error())
}
}
http://localhost:8000에 액세스하여 사용해 보십시오. 두 번 액세스하면 출력은 다음과 같습니다.
2020-12-19 01:25:16 INFO logger/main.go:75 > testing
2020-12-19 01:25:20 INFO logger/main.go:33 > in method=GET path=/ requestId=6eb1f209-dac9-42b2-8ba4-883efffcbd9e
2020-12-19 01:25:20 INFO logger/main.go:50 > inside handler requestId=6eb1f209-dac9-42b2-8ba4-883efffcbd9e
2020-12-19 01:25:20 INFO logger/main.go:45 > inside anotherFunc requestId=6eb1f209-dac9-42b2-8ba4-883efffcbd9e
2020-12-19 01:25:20 INFO logger/main.go:39 > out requestId=6eb1f209-dac9-42b2-8ba4-883efffcbd9e status=204
2020-12-19 01:25:31 INFO logger/main.go:33 > in method=GET path=/ requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:50 > inside handler requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:45 > inside anotherFunc requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:39 > out requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0 status=204
이제 모든 이벤트를 추적하려면 파일에 기록하고 요청 ID를 grep할 수 있습니다.
$ grep 88a140b8-2aed-4d82-bf56-0ab9a1c7cce0 /tmp/log
2020-12-19 01:25:31 INFO logger/main.go:33 > in method=GET path=/ requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:50 > inside handler requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:45 > inside anotherFunc requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0
2020-12-19 01:25:31 INFO logger/main.go:39 > out requestId=88a140b8-2aed-4d82-bf56-0ab9a1c7cce0 status=204
참조용으로 충분할 것입니다. 용도에 따라 코드를 수정하십시오. 나는 성능을 테스트하거나 벤치마킹하지 않았으므로 소금 한 알과 함께 가져 가십시오.
Reference
이 문제에 관하여(상황별 로그인 이동), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/clavinjune/contextual-logging-in-go-4fhj텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)