Go의 slog 패키지 실습
Min-jun Kim
Dev Intern · Leapcell

서문
Go 버전 1.21.0에서는 구조화된 로깅 기능을 제공하는 새로운 패키지인 log/slog
가 도입되었습니다. 기존 로깅에 비해 구조화된 로깅은 가독성이 더 뛰어나고 처리, 분석 및 검색에서 상당한 이점을 제공하므로 더 인기가 있습니다.
slog 패키지
slog 패키지는 각 로그 항목에 메시지, 심각도 수준 및 다양한 기타 속성이 키-값 쌍으로 표시되는 구조화된 로그를 제공합니다.
slog 패키지의 주요 기능은 다음과 같습니다.
- 구조화된 로깅
- 로그 심각도 수준
- 사용자 정의 로그 핸들러
- 로그 그룹화
첫 번째 경험
package main import ( "context" "log/slog" ) func main() { slog.Info("slog msg", "greeting", "hello slog") // Carrying context slog.InfoContext(context.Background(), "slog msg with context", "greeting", "hello slog") }
위의 예에서는 패키지 함수 slog.Info
를 호출하여 정보 수준 로그를 직접 출력합니다. 내부적으로 이 함수는 기본 Logger
인스턴스를 사용하여 로깅 작업을 수행합니다. 또한 slog.InfoContext
를 사용하여 관련 컨텍스트와 함께 로그를 출력할 수 있습니다.
Info()
및 InfoContext()
외에도 다양한 수준에서 로깅하기 위한 Debug()
, Warn()
및 Error()
와 같은 함수도 있습니다.
위의 프로그램을 실행하면 다음 출력이 생성됩니다.
2025/06/18 21:08:08 INFO slog msg greeting="hello slog"
2025/06/18 21:08:08 INFO slog msg with context greeting="hello slog"
Logger 생성
기본적으로 slog 패키지 함수를 사용하여 로그를 출력할 때 형식은 일반 텍스트입니다. JSON 또는 key=value 형식으로 출력하려면 slog.New()
를 사용하여 Logger 인스턴스를 만들어야 합니다. 이 함수를 사용할 때는 slog.Handler
구현을 전달해야 합니다. slog 패키지는 TextHandler
와 JsonHandler
의 두 가지 구현을 제공합니다.
TextHandler
TextHandler는 로그 레코드를 일련의 키-값 쌍으로 io.Writer
에 쓰는 로그 핸들러입니다. 각 키-값 쌍은 key=value 형식으로 표현되며 공백으로 구분됩니다.
package main import ( "context" "log/slog" "os" ) func main() { textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil)) textLogger.InfoContext(context.Background(), "TextHandler", "Name", "Leapcell") }
위의 예에서는 slog.NewTextHandler
를 사용하여 로그 핸들러를 만듭니다. 첫 번째 매개변수 os.Stdout
는 로그가 콘솔에 출력됨을 나타냅니다. 그런 다음 핸들러는 slog.New
에 매개변수로 전달되어 Logger 인스턴스를 만들고, 이 인스턴스는 로깅 작업을 수행하는 데 사용됩니다.
프로그램의 출력은 다음과 같습니다.
time=2025-06-18T21:09:03.912+00:00 level=INFO msg=TextHandler Name=Leapcell
JsonHandler
JsonHandler는 로그 레코드를 JSON 형식으로 io.Writer
에 쓰는 로그 핸들러입니다.
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) jsonLogger.InfoContext(context.Background(), "JsonHandler", "name", "Leapcell") }
위의 예에서는 slog.NewJsonHandler
를 사용하여 JSON 로그 핸들러를 만듭니다. 첫 번째 매개변수 os.Stdout
는 콘솔에 출력을 나타냅니다. 핸들러는 slog.New
에 전달되어 Logger 인스턴스를 만들고, 이 인스턴스는 로깅 작업에 사용됩니다.
프로그램 출력은 다음과 같습니다.
{ "time": "2025-06-18T21:09:22.614686104+00:00", "level": "INFO", "msg": "JsonHandler", "name": "Leapcell" }
전역 Logger 인스턴스
slog에는 기본 Logger 인스턴스가 있습니다. 기본 Logger를 얻으려면 다음 코드를 참조하십시오.
logger := slog.Default()
이전 예에서는 항상 특별히 생성된 Logger 인스턴스를 사용하여 로그를 출력했습니다. 그러나 매번 특정 Logger 인스턴스를 통해 로그를 기록하는 대신 전역적으로 작동하려면 slog.SetDefault
함수를 사용하여 기본 Logger 인스턴스를 설정하고 바꿀 수 있습니다. 이렇게 하면 로깅이 더욱 편리하고 유연해집니다.
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) slog.SetDefault(jsonLogger) slog.InfoContext(context.Background(), "JsonHandler", "name", "Leapcell") //{"time":"2025-06-18T21:11:22.41760604+00:00","level":"INFO","msg":"JsonHandler","name":"Leapcell"} }
그룹화
그룹화는 로그 레코드에서 관련 속성(키-값 쌍)을 그룹화하는 것을 의미합니다. 다음은 예입니다.
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)).WithGroup("information") jsonLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell"), slog.Int("phone", 1234567890)) textLogger := slog.New(slog.NewTextHandler(os.Stdout, nil)).WithGroup("information") textLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell"), slog.Int("phone", 1234567890)) }
이 프로그램을 실행한 결과는 다음과 같습니다.
{"time":"2025-06-18T21:12:23.124255258+00:00","level":"INFO","msg":"json-log","information":{"name":"Leapcell","phone":1234567890}}
time=2025-06-18T21:12:23.127+00:00 level=INFO msg=json-log information.name=Leapcell information.phone=1234567890
출력에 따르면 Logger 인스턴스를 JsonHandler
로 그룹화하면 그룹 이름이 키가 되고 값은 모든 키-값 쌍으로 구성된 JSON 개체가 됩니다.
Logger를 TextHandler
로 그룹화하면 그룹 이름이 모든 키-값 쌍의 키와 결합되어 최종적으로 groupName.key=value
로 표시됩니다.
LogAttrs를 사용한 효율적인 로깅
자주 로깅해야 하는 경우 이전 예에 비해 slog.LogAttrs
함수를 slog.Attr
유형과 함께 사용하는 것이 더 효율적입니다. 유형 구문 분석 프로세스를 줄이기 때문입니다.
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) jsonLogger.LogAttrs(context.Background(), slog.LevelInfo, "Efficient log output", slog.String("Name", "Leapcell"), slog.Int("Contact", 12345678901)) }
위의 예에서는 LogAttrs
메서드를 사용하여 로그 항목을 출력합니다. 메서드의 서명은 다음과 같습니다.
func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr)
서명에 따라 첫 번째 매개변수는 context.Context
이고, 두 번째 매개변수는 Level
(slog 패키지에 정의된 로그 심각도 수준)이고, 세 번째 매개변수는 Attr
키-값 쌍입니다.
Info
와 같은 다른 메서드를 사용하여 로그를 출력할 때 키-값 쌍은 내부적으로 Attr
유형으로 변환됩니다. LogAttrs
메서드를 사용하면 Attr
유형을 직접 지정하여 변환 프로세스를 줄여 로깅을 더욱 효율적으로 만들 수 있습니다.
With: 일반 속성 설정
모든 로그에 동일한 키-값 쌍이 포함되어야 하는 경우 일반 속성을 설정하는 것을 고려할 수 있습니다.
package main import ( "context" "log/slog" "os" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) logger := jsonLogger.With("systemID", "s1") logger.LogAttrs(context.Background(), slog.LevelInfo, "json-log", slog.String("k1", "v1")) logger.LogAttrs(context.Background(), slog.LevelInfo, "json-log", slog.String("k2", "v2")) }
With
메서드를 사용하여 하나 이상의 고정 속성을 추가하고 새 Logger 인스턴스를 반환할 수 있습니다. 이 새 인스턴스에서 출력된 모든 로그에는 추가된 고정 속성이 포함되므로 모든 로그 문에 동일한 키-값 쌍을 추가할 필요가 없습니다.
이 프로그램의 출력은 다음과 같습니다.
{"time":"2025-06-18T21:19:51.338328238+00:00","level":"INFO","msg":"json-log","systemID":"s1","k1":"v1"} {"time":"2025-06-18T21:19:51.338604943+00:00","level":"INFO","msg":"json-log","systemID":"s1","k2":"v2"}
HandlerOptions: 로그 핸들러에 대한 구성 옵션
주의 깊은 독자는 이전 예에서 NewJSONHandler
또는 NewTextHandler
를 사용하는지 여부에 관계없이 두 번째 매개변수가 nil
로 설정되어 있음을 알았을 것입니다. 이는 기본 구성이 사용됨을 의미합니다.
이 매개변수는 *HandlerOptions
유형입니다. 이를 통해 로그 문의 소스 코드 위치, 최소 로그 출력 수준, 키-값 쌍 속성을 다시 작성하는 방법을 구성할 수 있습니다.
package main import ( "context" "log/slog" "os" "time" ) func main() { jsonLogger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelError, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { if t, ok := a.Value.Any().(time.Time); ok { a.Value = slog.StringValue(t.Format(time.DateTime)) } } return a }, })) jsonLogger.InfoContext(context.Background(), "json-log", slog.String("name", "Leapcell")) jsonLogger.ErrorContext(context.Background(), "json-log", slog.String("name", "Leapcell")) }
이 예에서는 JsonHandler
를 사용하여 Logger 인스턴스를 만듭니다. JsonHandler
를 만들 때 HandlerOptions
매개변수를 통해 다음 구성이 지정됩니다.
- 로그 문의 소스 코드(소스 정보) 출력
- 최소 로그 수준을 Error로 설정
- 키가
"time"
인 속성의 형식을"2006-01-02 15:04:05"
로 다시 작성
이 프로그램의 출력은 다음과 같습니다.
{ "time": "2025-06-18 21:21:31", "level": "ERROR", "source": { "function": "main.main", "file": "D:/goproject/src/gocode/play/main.go", "line": 24 }, "msg": "json-log", "name": "Leapcell" }
출력은 예상과 일치합니다. INFO 수준의 로그는 출력되지 않고 소스 정보가 포함되어 있으며 "time"
키의 값이 다시 작성되었습니다.
키-값 쌍에서 값 사용자 정의
이전 예에서는 HandlerOptions
구성을 사용하여 키-값 쌍의 값을 수정했습니다. 이 방법 외에도 slog 패키지는 값을 변경하는 또 다른 방법을 지원합니다.
package main import ( "context" "log/slog" ) type Password string func (Password) LogValue() slog.Value { return slog.StringValue("REDACTED_PASSWORD") } func main() { slog.LogAttrs(context.Background(), slog.LevelInfo, "Sensitive Data", slog.Any("password", Password("1234567890"))) }
위의 예에서는 slog.LogValuer
인터페이스(유형에 LogValue() slog.Value
메서드 추가)를 구현하여 키-값 쌍의 값을 덮어쓸 수 있습니다. 로깅할 때 값은 LogValue
메서드의 반환 값으로 바뀝니다.
이 프로그램의 출력은 다음과 같습니다.
2025/06/18 21:37:11 INFO Sensitive Data password=REDACTED_PASSWORD
예상대로 password
의 값이 변경되었습니다.
요약
이 기사에서는 기본 사용법, Logger 인스턴스 생성, 효율적인 로깅 및 로그 정보 사용자 정의를 포함하여 Go의 slog 패키지에 대한 자세한 소개를 제공합니다.
이 기사를 읽은 후에는 slog 패키지에 대한 더 깊은 이해를 얻고 이를 사용하여 로그를 보다 효과적으로 관리하고 기록할 수 있어야 합니다.
Leapcell은 Go 프로젝트 호스팅을 위한 최고의 선택입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포하세요
- 사용량에 대해서만 비용을 지불하세요. 요청도 없고, 요금도 없습니다.
탁월한 비용 효율성
- 유휴 요금 없이 사용량에 따라 지불하세요.
- 예: $25는 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고도의 동시성을 쉽게 처리할 수 있도록 자동 확장이 가능합니다.
- 운영 오버헤드가 없습니다. 구축에만 집중하십시오.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ