파일 출력과 로테이션
DD 는 유연한 파일 출력 기능을 제공하여 자동 로테이션, 버퍼 쓰기, 다중 대상 분산을 지원하며, 프로덕션 환경에 적합합니다.
빠른 시작
기본 파일 출력
go
package main
import (
"log"
"github.com/cybergodev/dd"
)
func main() {
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{
dd.FileOutput("logs/app.log"),
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()
logger.Info("로그가 파일에 기록됩니다") // logs/app.log에 기록
}콘솔 + 파일 이중 출력
go
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{
dd.ConsoleOutput(),
dd.FileOutput("logs/app.log"),
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()FileWriter 로테이션 구성
FileWriter 는 크기별 자동 로테이션과 시간별 오래된 파일 정리를 지원합니다.
기본 구성
go
cfg := dd.DefaultFileWriterConfig()
// MaxSizeMB: 100 — 단일 파일 최대 100MB
// MaxAge: 30 * 24 * time.Hour — 30 일 보존
// MaxBackups: 10 — 최대 10 개 백업 보존
// Compress: false — 압축 안 함커스텀 로테이션 정책
go
// 고트래픽 서비스: 작은 파일, 빠른 로테이션
fwCfg := dd.DefaultFileWriterConfig()
fwCfg.MaxSizeMB = 50 // 50MB 로테이션
fwCfg.MaxBackups = 20 // 20 개 백업 보존
fwCfg.MaxAge = 7 * 24 * time.Hour // 7 일 정리
fwCfg.Compress = true // 이전 파일 압축
fw, err := dd.NewFileWriter("logs/app.log", fwCfg)
if err != nil {
log.Fatal(err)
}
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{dd.CustomOutput(fw)},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()JSON 형식 로그 파일
go
logger, err := dd.New(dd.Config{
Format: dd.FormatJSON,
Targets: []dd.OutputTarget{
dd.FileOutput("logs/app.json"),
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()로테이션 후 파일 명명 규칙:
text
logs/app.log ← 현재 로그
logs/app_log_1.log ← 첫 번째 로테이션 (가장 최근 백업)
logs/app_log_2.log ← 더 이전 백업
logs/app_log_1.log.gz ← Compress 활성화 시 이전 백업은 .gz 로 압축정보 압축과 백업은 공존하지 않음
Compress 활성화 시 압축은 로테이션 이후 별도 goroutine 에서 비동기로 진행됩니다. 압축 완료 시 원본 .log 백업은 .log.gz로 이름이 변경되며, 두 파일은 공존하지 않습니다.
BufferedWriter 버퍼 쓰기
고처리량 시나리오에서는 BufferedWriter로 I/O 횟수를 줄입니다.
go
// 파일 Writer 생성
fw, err := dd.NewFileWriter("logs/app.log", dd.DefaultFileWriterConfig())
if err != nil {
log.Fatal(err)
}
// 버퍼 Writer 로 래핑
bwCfg := dd.DefaultBufferedWriterConfig()
// BufferSize: 1024 — 1KB 버퍼
// FlushTime: 100ms — 100ms 자동 flush
bw, err := dd.NewBufferedWriter(fw, bwCfg)
if err != nil {
log.Fatal(err)
}
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{dd.CustomOutput(bw)},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close() // Close 시 자동 Flush튜닝 권장 사항
| 시나리오 | BufferSize | FlushTime | 설명 |
|---|---|---|---|
| 저지연 요구 | 512 | 50ms | 빠른 flush, 지연 감소 |
| 일반 시나리오 | 1024 | 100ms | 기본값, 지연과 처리량 균형 |
| 고효율 처리 | 4096 | 500ms | 대형 버퍼, 처리량 극대화 |
| 배치 작업 | 8192 | 1000ms | 최대 버퍼, 오프라인 처리에 적합 |
경고 데이터 안전
BufferedWriter 는 버퍼가 반 찰 때 (BufferSize/2 도달) 또는 타이머 트리거 시 flush 합니다. 프로그램 비정상 종료 시 버퍼 데이터가 유실될 수 있습니다. 데이터 무결성을 위해 Close() 또는 Flush() 호출을 보장하세요.
MultiWriter 다중 대상 분산
go
// 파일과 원격 서비스에 동시 쓰기
fw, err := dd.NewFileWriter("logs/app.log", dd.DefaultFileWriterConfig())
if err != nil {
log.Fatal(err)
}
remote := &RemoteLogWriter{endpoint: "http://log-service/ingest"}
mw := dd.NewMultiWriter(fw, remote)
logger, err := dd.New(dd.Config{
Targets: []dd.OutputTarget{dd.CustomOutput(mw)},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()MultiWriter 는 모든 Writer 에 로그를 분산하며, 어느 한 Writer 실패가 다른 Writer 에 영향을 주지 않습니다.
동적 Writer 관리
Logger 는 런타임에 Writer 추가와 제거를 지원합니다.
go
// 런타임에 Writer 추가
fw, err := dd.NewFileWriter("logs/debug.log", dd.DefaultFileWriterConfig())
if err != nil {
log.Fatal(err)
}
err = logger.AddWriter(fw)
// 런타임에 Writer 제거
err = logger.RemoveWriter(fw)
// 현재 Writer 수 조회
count := logger.WriterCount()
_ = count팁 사용 시나리오
동적 Writer 는 런타임에 로그 대상을 전환해야 하는 시나리오에 적합합니다. 예: 디버그 모드 켤 때 상세 로그 파일 추가, 또는 디스크 공간 부족 시 원격 로그 서비스로 전환.
사용자 정의 Writer
io.Writer 인터페이스를 구현하면 커스텀 출력 대상을 만들 수 있습니다.
go
// 네트워크 로그 전송기
type LogstashWriter struct {
endpoint string
client *http.Client
}
func (w *LogstashWriter) Write(p []byte) (n int, err error) {
resp, err := w.client.Post(w.endpoint, "application/json", bytes.NewReader(p))
if err != nil {
return 0, err
}
defer resp.Body.Close()
return len(p), nil
}
// 사용자 정의 Writer 사용
logger, err := dd.New(dd.Config{
Format: dd.FormatJSON,
Targets: []dd.OutputTarget{
dd.FileOutput("logs/app.json"),
dd.CustomOutput(&LogstashWriter{
endpoint: "http://logstash:5044",
client: &http.Client{Timeout: 5 * time.Second},
}),
},
})
if err != nil {
log.Fatal(err)
}
defer logger.Close()프로덕션 환경 권장 구성
go
func NewProductionLogger() (*dd.Logger, error) {
// 파일 Writer: 중간 로테이션 + 압축
fwCfg := dd.DefaultFileWriterConfig()
fwCfg.MaxSizeMB = 100
fwCfg.MaxAge = 30 * 24 * time.Hour
fwCfg.MaxBackups = 15
fwCfg.Compress = true
fw, err := dd.NewFileWriter("logs/app.json", fwCfg)
if err != nil {
return nil, err
}
// 버퍼 래핑
bw, err := dd.NewBufferedWriter(fw, dd.DefaultBufferedWriterConfig())
if err != nil {
return nil, err
}
return dd.New(dd.Config{
Level: dd.LevelInfo,
Format: dd.FormatJSON,
Targets: []dd.OutputTarget{
dd.ConsoleOutput(),
dd.CustomOutput(bw),
},
})
}다음 단계
- 구조화 로그 -- 필드와 체인 호출
- 민감 데이터 필터링 -- 자동 마스킹
- API 레퍼런스 - Writers -- Writer 의 완전한 API
- 성능 최적화 -- 성능 튜닝 권장