728x90

로그를 남겨야 하는이유

 

개발자는 디버깅 하는데에 있어서 가끔 로그가 정말 큰도움을 줄때가 많습니다. 꼭 디버깅의 이유가 아니더라도 로그를 남겨야 하는 이유는 많을 텐데요 오늘은 지금까지 백엔드 개발을 하면서 이렇게 로그를 남기면 좋을거 같다 싶은것과 새롭게 배우는 Go 언어에서는 어떻게 log를 남기는지 적어보겠습니다.

 

로그 라이브러리 선택

uber 에서 만든 uber-go 의 로그 라이브러리를 사용하겠습니다.

 

https://github.com/uber-go/zap

 

GitHub - uber-go/zap: Blazing fast, structured, leveled logging in Go.

Blazing fast, structured, leveled logging in Go. Contribute to uber-go/zap development by creating an account on GitHub.

github.com

 

 

해당 프로젝트에서 go mod init 프로젝트 이름 하신뒤 go get 으로 의존성 주입을 합니다.

go get -u go.uber.org/zap

 

 

mysql 과 mux는 제가 기존에 쓰던것들이고 나머지 3개가 추가 될것입니다.

 

package main

import (
	"bangking/app"
	"log"
)

func main() {
	log.Println("Starting the application...")
	app.Start()
}

기존의 log print 방식으로도 괜찮은 로그를 수집할 수 있지만 대용량의 로그와 어디서 발생했는지 로그는 어떤 레벨인지 ?! 까지 더 자세하게 나와준다면 로그를 관리하는데에 있어서 더 편하고 생산성이 늘어날거 같습니다.

 

logger setting

logger/logger.go

 

 

package logger

import (
	"go.uber.org/zap"
)

var log *zap.Logger

func init() {
	var err error

	log, err = zap.NewProduction()
	if err != nil {
		panic(err)
	}
}

func Info(message string, fields ...zap.Field) {
	log.Info(message, fields...)
}

 

이렇게 코드를 작성한 이유는 추후에 log 라이브 러리를 교체했을때 좀 더 수월하게 log 만 바꾸면 되게끔 설계 했습니다. 

코드는 최대한 의존성이 덜하면 덜할수록 좋은 코드입니다.

 

main.go

package main

import (
	"bangking/app"
	"bangking/logger"
)

func main() {
	logger.Info("Starting the application...")
	app.Start()
}

 

main.go 의 log 부분을 수정했습니다.

 

{
  "level":"info",
  "ts":1632436125.122248,
  "caller":"logger/logger.go:28",
  "msg":"Starting the application..."
}

좀더 명시적이고 구체적인 로그가 나왔습니다.

 

하지만 지금 문제점이 2가지 있습니다 ts 는 개발자 입장으로 조금 알아보기 힘들고 caller는 logger 분리된 경로로 나오고 있습니다.

 

logger.go

func init() {
	var err error

	config := zap.NewProductionConfig()
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.TimeKey = "timestamp"
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // timestamp 를 좀더 보기 좋게 하기위해서
	encoderConfig.StacktraceKey = ""                      // error 가 발생했을때 stack push 를 비어있게 하기 위해서
	config.EncoderConfig = encoderConfig

	log, err = config.Build(zap.AddCallerSkip(1)) // 설정이 끝나면 build

	//log, err = zap.NewProduction()
	if err != nil {
		panic(err)
	}
}

 

NewProductionConfig 와 NewProductionEncoderConfig 를 만들고 EncoderConfig 의 설정을 변경한뒤 Config.EncoderConfig 를 변경한뒤 build 해주면 됩니다.

 

완성코드

package logger

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var log *zap.Logger

func init() {
	var err error

	config := zap.NewProductionConfig()
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.TimeKey = "timestamp"
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // timestamp 를 좀더 보기 좋게 하기위해서
	encoderConfig.StacktraceKey = ""                      // error 가 발생했을때 stack push 를 비어있게 하기 위해서
	config.EncoderConfig = encoderConfig

	log, err = config.Build(zap.AddCallerSkip(1)) // 설정이 끝나면 build

	// log, err = zap.NewProduction()
	if err != nil {
		panic(err)
	}
}

func Info(message string, fields ...zap.Field) {
	log.Info(message, fields...)
}

func Debug(message string, fields ...zap.Field) {
	log.Debug(message, fields...)
}

func Error(message string, fields ...zap.Field) {
	log.Error(message, fields...)
}

 

시작

{
  "level":"info",
  "timestamp":"2021-09-24T07:34:37.624+0900",
  "caller":"go-micro-web-service/main.go:9",
  "msg":"Starting the application..."
}

 

데이터베이스 연결 실패시

{
  "level":"error",
  "timestamp":"2021-09-24T07:35:40.360+0900",
  "caller":"domain/CustomerRepositoryDb.go:29",
  "msg":"Error while querying customer table Error 1045: Access denied for user 'root'@'localhost' (using password: YES)"
}

 

golang 은 다른언어에 비해서 웹개발을 할때 자유도가 더 높은거 같습니다. 그만큼 처음 구조를 잡아가기가 힘든것도 있는것 같네요 ㅎ

728x90