mirror of
https://github.com/dalbodeule/hop-gate.git
synced 2025-12-07 20:35:44 +09:00
110 lines
3.2 KiB
Go
110 lines
3.2 KiB
Go
package logging
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
// Level 은 로그의 심각도 레벨을 나타냅니다.
|
|
type Level string
|
|
|
|
const (
|
|
DebugLevel Level = "debug"
|
|
InfoLevel Level = "info"
|
|
WarnLevel Level = "warn"
|
|
ErrorLevel Level = "error"
|
|
)
|
|
|
|
// Fields 는 구조적 로그의 key/value 필드를 표현합니다.
|
|
// Loki/Promtail 에서 라벨/필드로 활용할 수 있습니다.
|
|
type Fields map[string]any
|
|
|
|
// Logger 는 Loki/Grafana 스택에 적합한 구조적 로그 인터페이스입니다.
|
|
//
|
|
// - 모든 구현체는 단일 라인 JSON 을 stdout/stderr 로 출력하는 것을 목표로 합니다.
|
|
// - Promtail 은 stdout 을 수집해 Loki 로 전송하고, Grafana 에서 쿼리/대시보딩 할 수 있습니다.
|
|
type Logger interface {
|
|
// Debug 는 디버그 레벨 로그를 기록합니다.
|
|
Debug(msg string, fields Fields)
|
|
|
|
// Info 는 정보 레벨 로그를 기록합니다.
|
|
Info(msg string, fields Fields)
|
|
|
|
// Warn 는 경고 레벨 로그를 기록합니다.
|
|
Warn(msg string, fields Fields)
|
|
|
|
// Error 는 에러 레벨 로그를 기록합니다.
|
|
Error(msg string, fields Fields)
|
|
|
|
// With 는 추가 필드를 항상 포함하는 child logger 를 생성합니다.
|
|
With(fields Fields) Logger
|
|
}
|
|
|
|
// stdLogger 는 표준 log.Logger 를 감싼 구현체입니다.
|
|
// 개발 단계에서 간단히 사용하거나 JSON 형식이 필요 없을 때 사용할 수 있습니다.
|
|
type stdLogger struct {
|
|
l *log.Logger
|
|
fields Fields
|
|
}
|
|
|
|
func (s *stdLogger) log(level Level, msg string, fields Fields) {
|
|
entry := map[string]any{
|
|
"ts": time.Now().UTC().Format(time.RFC3339Nano),
|
|
"level": level,
|
|
"msg": msg,
|
|
}
|
|
|
|
// 공통 필드 병합
|
|
for k, v := range s.fields {
|
|
entry[k] = v
|
|
}
|
|
// 호출 시 전달된 필드 병합(우선순위 높음)
|
|
for k, v := range fields {
|
|
entry[k] = v
|
|
}
|
|
|
|
b, err := json.Marshal(entry)
|
|
if err != nil {
|
|
// JSON 마샬 실패 시 fallback 으로 기본 포맷 사용
|
|
s.l.Printf("level=%s msg=%s marshal_error=%v", level, msg, err)
|
|
return
|
|
}
|
|
s.l.Println(string(b))
|
|
}
|
|
|
|
func (s *stdLogger) Debug(msg string, fields Fields) { s.log(DebugLevel, msg, fields) }
|
|
func (s *stdLogger) Info(msg string, fields Fields) { s.log(InfoLevel, msg, fields) }
|
|
func (s *stdLogger) Warn(msg string, fields Fields) { s.log(WarnLevel, msg, fields) }
|
|
func (s *stdLogger) Error(msg string, fields Fields) { s.log(ErrorLevel, msg, fields) }
|
|
|
|
func (s *stdLogger) With(fields Fields) Logger {
|
|
merged := Fields{}
|
|
for k, v := range s.fields {
|
|
merged[k] = v
|
|
}
|
|
for k, v := range fields {
|
|
merged[k] = v
|
|
}
|
|
return &stdLogger{
|
|
l: s.l,
|
|
fields: merged,
|
|
}
|
|
}
|
|
|
|
// NewStdJSONLogger 는 stdout 으로 단일 라인 JSON 로그를 출력하는 기본 Logger 를 생성합니다.
|
|
// Promtail 이 stdout 을 Loki 로 수집하는 전형적인 구성에 적합합니다.
|
|
//
|
|
// component, service, client_id, request_id 같은 필드를 With 로 미리 설정해 두면
|
|
// Grafana 에서 필터링/그룹핑에 활용할 수 있습니다.
|
|
func NewStdJSONLogger(component string) Logger {
|
|
baseFields := Fields{
|
|
"component": component,
|
|
}
|
|
return &stdLogger{
|
|
l: log.New(os.Stdout, "", 0), // 프리픽스/타임스탬프는 JSON 필드로만 사용
|
|
fields: baseFields,
|
|
}
|
|
}
|