Zap: Golang 로깅의 완전한 가능성을 열다
Min-jun Kim
Dev Intern · Leapcell

개요
Zap은 Uber에서 개발한 매우 빠르고 구조화된 로그 레벨 Go 로깅 라이브러리입니다. Uber - go Zap 문서에 따르면 유사한 구조화된 로깅 패키지보다 성능이 뛰어나고 표준 라이브러리보다 빠릅니다. 특정 성능 테스트는 GitHub에서 확인할 수 있습니다.
GitHub 주소: https://github.com/uber - go/zap
인스턴스 생성
zap.NewProduction()/zap.NewDevelopment() 또는 zap.Example()을 호출하여 Logger를 생성합니다. 이 세 가지 방법의 차이점은 기록할 정보에 있으며, 매개변수는 문자열 유형만 가능합니다.
// 코드 var log *zap.Logger log = zap.NewExample() log, _ := zap.NewDevelopment() log, _ := zap.NewProduction() log.Debug("This is a DEBUG message") log.Info("This is an INFO message")
// 예제 출력 {"level":"debug","msg":"This is a DEBUG message"} {"level":"info","msg":"This is an INFO message"}
// 개발 출력 2025-01-28T00:00:00.000+0800 DEBUG development/main.go:7 This is a DEBUG message 2025-01-28T00:00:00.000+0800 INFO development/main.go:8 This is an INFO message
// 프로덕션 출력 {"level":"info","ts":1737907200.0000000,"caller":"production/main.go:8","msg":"This is an INFO message"} {"level":"info","ts":1737907200.0000000,"caller":"production/main.go:9","msg":"This is an INFO message with fields","region":["us-west"],"id":2}
세 가지 생성 방법 비교:
- Example과 Production은 모두 JSON 형식을 사용하여 출력하는 반면, Development는 줄 단위 출력 형식을 사용합니다.
- Development
- 추적을 위해 경고 레벨부터 스택까지 출력합니다.
- 항상 패키지/파일/라인(메서드)을 출력합니다.
- 추가 필드를 JSON 문자열로 줄 끝에 추가합니다.
- 레벨 이름을 대문자로 출력합니다.
- 타임스탬프를 ISO8601 형식으로 밀리초 단위로 출력합니다.
- Production
- 디버그 레벨 메시지는 기록되지 않습니다.
- Error 및 Dpanic 레벨 기록의 경우 파일이 스택에서 추적되지만 Warn은 추적되지 않습니다.
- 항상 호출자를 파일에 추가합니다.
- 날짜를 타임스탬프 형식으로 출력합니다.
- 레벨 이름을 소문자로 출력합니다.
서식이 지정된 출력
Zap에는 *zap.Logger와 *zap.SugaredLogger의 두 가지 유형이 있습니다. 이 둘의 유일한 차이점은 메인 로거의 .Sugar() 메서드를 호출하여 SugaredLogger를 얻은 다음 SugaredLogger를 사용하여 printf 형식으로 문을 기록할 수 있다는 것입니다. 예를 들면 다음과 같습니다.
var sugarLogger *zap.SugaredLogger func InitLogger() { logger, _ := zap.NewProduction() sugarLogger = logger.Sugar() } func main() { InitLogger() defer sugarLogger.Sync() sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err) }
파일에 쓰기
기본적으로 로그는 애플리케이션의 콘솔 인터페이스에 출력됩니다. 그러나 쉽게 쿼리할 수 있도록 로그를 파일에 쓸 수 있습니다. 하지만 이전처럼 인스턴스를 생성하는 세 가지 방법은 더 이상 사용할 수 없습니다. 대신 zap.New()를 사용합니다.
package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { writeSyncer, _ := os.Create("./info.log") // 로그 파일 저장 디렉터리 encoderConfig := zap.NewProductionEncoderConfig() // 시간 형식 지정 encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // 인코더 가져오기, NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. log = zap.New(core,zap.AddCaller()) // AddCaller()는 파일 이름과 라인 번호를 표시하는 데 사용됩니다. log.Info("hello world") log.Error("hello world") }
// 로그 파일 출력 결과: 2025-01-28T00:00:00.000+0800 INFO geth/main.go:18 hello world 2025-01-28T00:00:00.000+0800 ERROR geth/main.go:19 hello world
콘솔과 파일 모두에 출력
콘솔과 파일 모두에 출력해야 하는 경우 zapcore.NewCore만 수정하면 됩니다. 예:
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { // 인코더 가져오기, NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 시간 형식 지정 encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder encoder := zapcore.NewConsoleEncoder(encoderConfig) // 파일 writeSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // 로그 파일 저장 디렉터리 MaxSize: 1, // 파일 크기 제한, 단위 MB MaxBackups: 5, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 }) fileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(fileWriteSyncer,zapcore.AddSync(os.Stdout)), zapcore.DebugLevel) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. log = zap.New(fileCore, zap.AddCaller()) // AddCaller()는 파일 이름과 라인 번호를 표시하는 데 사용됩니다. log.Info("hello world") log.Error("hello world") }
파일 분할
로그 파일은 시간이 지남에 따라 커집니다. 하드 디스크 공간이 가득 차는 것을 방지하려면 특정 조건에 따라 로그 파일을 분할해야 합니다. Zap 패키지 자체는 파일 분할 기능을 제공하지 않지만 Zap에서 권장하는 lumberjack 패키지를 사용하여 처리할 수 있습니다.
// 파일 writeSyncer fileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./info.log", // 로그 파일 저장 디렉터리. 폴더가 없으면 자동으로 생성됩니다. MaxSize: 1, // 파일 크기 제한, 단위 MB MaxBackups: 5, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 })
레벨별 파일 쓰기
관리 담당자의 쿼리 편의를 위해 일반적으로 오류 레벨 미만의 로그는 info.log에, 오류 레벨 이상의 로그는 error.log 파일에 넣어야 합니다. zapcore.NewCore 메서드의 세 번째 매개변수만 수정하면 되고, 파일 WriteSyncer를 info와 error로 분할하면 됩니다. 예:
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { var coreArr []zapcore.Core // 인코더 가져오기 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 시간 형식 지정 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 레벨에 따라 다른 색상을 표시합니다. 필요하지 않은 경우 zapcore.CapitalLevelEncoder를 사용합니다. //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // 전체 파일 경로 표시 encoder := zapcore.NewConsoleEncoder(encoderConfig) // 로그 레벨 highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // 오류 레벨 return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // Info 및 debug 레벨, debug 레벨이 가장 낮음 return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // Info 파일 writeSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // 로그 파일 저장 디렉터리. 폴더가 없으면 자동으로 생성됩니다. MaxSize: 1, // 파일 크기 제한, 단위 MB MaxBackups: 5, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. // Error 파일 writeSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // 로그 파일 저장 디렉터리 MaxSize: 1, // 파일 크기 제한, 단위 MB MaxBackups: 5, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller()는 파일 이름과 라인 번호를 표시하는 데 사용되며 생략할 수 있습니다. log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
이러한 수정 후 info 및 debug 레벨 로그는 info.log에 저장되고 error 레벨 로그는 error.log 파일에 별도로 저장됩니다.
레벨별 콘솔에 색상 표시
인코더의 EncodeLevel을 지정하기만 하면 됩니다.
// 인코더 가져오기 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 시간 형식 지정 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 레벨에 따라 다른 색상을 표시합니다. 필요하지 않은 경우 zapcore.CapitalLevelEncoder를 사용합니다. encoder := zapcore.NewConsoleEncoder(encoderConfig)
파일 경로 및 라인 번호 표시
이전에 언급했듯이 파일 경로와 라인 번호를 표시하려면 zap.New 메서드에 매개변수 zap.AddCaller()를 추가하기만 하면 됩니다. 전체 경로를 표시하려면 인코더 구성에서 지정해야 합니다.
// 인코더 가져오기 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 시간 형식 지정 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 레벨에 따라 다른 색상을 표시합니다. 필요하지 않은 경우 zapcore.CapitalLevelEncoder를 사용합니다. encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // 전체 파일 경로 표시 encoder := zapcore.NewConsoleEncoder(encoderConfig)
전체 코드
package main import ( "github.com/natefinch/lumberjack" "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" ) var log *zap.Logger func main() { var coreArr []zapcore.Core // 인코더 가져오기 encoderConfig := zap.NewProductionEncoderConfig() // NewJSONEncoder()는 JSON 형식으로, NewConsoleEncoder()는 일반 텍스트 형식으로 출력합니다. encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder // 시간 형식 지정 encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder // 레벨에 따라 다른 색상을 표시합니다. 필요하지 않은 경우 zapcore.CapitalLevelEncoder를 사용합니다. //encoderConfig.EncodeCaller = zapcore.FullCallerEncoder // 전체 파일 경로 표시 encoder := zapcore.NewConsoleEncoder(encoderConfig) // 로그 레벨 highPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool{ // 오류 레벨 return lev >= zap.ErrorLevel }) lowPriority := zap.LevelEnablerFunc(func(lev zapcore.Level) bool { // Info 및 debug 레벨, debug 레벨이 가장 낮음 return lev < zap.ErrorLevel && lev >= zap.DebugLevel }) // Info 파일 writeSyncer infoFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/info.log", // 로그 파일 저장 디렉터리. 폴더가 없으면 자동으로 생성됩니다. MaxSize: 2, // 파일 크기 제한, 단위 MB MaxBackups: 100, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 }) infoFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(infoFileWriteSyncer,zapcore.AddSync(os.Stdout)), lowPriority) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. // Error 파일 writeSyncer errorFileWriteSyncer := zapcore.AddSync(&lumberjack.Logger{ Filename: "./log/error.log", // 로그 파일 저장 디렉터리 MaxSize: 1, // 파일 크기 제한, 단위 MB MaxBackups: 5, // 보존할 최대 로그 파일 수 MaxAge: 30, // 로그 파일 보존 일수 Compress: false, // 압축 여부 }) errorFileCore := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(errorFileWriteSyncer,zapcore.AddSync(os.Stdout)), highPriority) // 세 번째 및 후속 매개변수는 파일에 쓸 로그 레벨입니다. ErrorLevel 모드에서는 오류 레벨 로그만 기록됩니다. coreArr = append(coreArr, infoFileCore) coreArr = append(coreArr, errorFileCore) log = zap.New(zapcore.NewTee(coreArr...), zap.AddCaller()) // zap.AddCaller()는 파일 이름과 라인 번호를 표시하는 데 사용되며 생략할 수 있습니다. log.Info("hello info") log.Debug("hello debug") log.Error("hello error") }
Leapcell: Golang 앱 호스팅, 비동기 작업 및 Redis를 위한 최고의 서버리스 플랫폼
마지막으로, Golang 서비스 배포를 위한 최고의 플랫폼인 Leapcell을 추천합니다.
1. 다국어 지원
- JavaScript, Python, Go 또는 Rust로 개발하세요.
2. 무제한 프로젝트를 무료로 배포하세요.
- 사용량에 대해서만 지불하세요. 요청도 없고, 요금도 없습니다.
3. 최고의 비용 효율성
- 유휴 요금 없이 사용량에 따라 지불하세요.
- 예: $25로 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
4. 간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
5. 손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있는 자동 확장.
- 운영 오버헤드가 없으므로 구축에만 집중하세요.
Leapcell Twitter: https://x.com/LeapcellHQ