Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/agent/core/log.go
3434 views
1
package core
2
3
import (
4
"os"
5
"path/filepath"
6
7
"go.uber.org/zap"
8
"go.uber.org/zap/zapcore"
9
10
"github.com/kardolus/chatgpt-cli/internal"
11
)
12
13
type Logs struct {
14
Dir string
15
HumanPath string
16
DebugPath string
17
18
HumanLogger *zap.SugaredLogger
19
DebugLogger *zap.SugaredLogger
20
21
HumanZap *zap.Logger
22
DebugZap *zap.Logger
23
24
humanFile *os.File
25
debugFile *os.File
26
}
27
28
func (l *Logs) Close() {
29
// best-effort; ignore errors
30
if l.HumanZap != nil {
31
_ = l.HumanZap.Sync()
32
}
33
if l.DebugZap != nil {
34
_ = l.DebugZap.Sync()
35
}
36
if l.humanFile != nil {
37
_ = l.humanFile.Close()
38
}
39
if l.debugFile != nil {
40
_ = l.debugFile.Close()
41
}
42
}
43
44
func NewLogs() (*Logs, error) {
45
cacheHome, err := internal.GetCacheHome()
46
if err != nil {
47
return nil, err
48
}
49
50
dir := filepath.Join(cacheHome, "agent")
51
if err := os.MkdirAll(dir, 0o700); err != nil {
52
return nil, err
53
}
54
55
// Naming + formats:
56
// - transcript: human-readable, line-oriented
57
// - debug: JSONL for machines/grep/jq tooling
58
humanPath := filepath.Join(dir, "agent.transcript.log")
59
debugPath := filepath.Join(dir, "agent.debug.jsonl")
60
61
humanSug, humanZap, humanFile, err := newFileLogger(humanPath, zapcore.InfoLevel, false /* json */)
62
if err != nil {
63
return nil, err
64
}
65
66
debugSug, debugZap, debugFile, err := newFileLogger(debugPath, zapcore.DebugLevel, true /* json */)
67
if err != nil {
68
_ = humanZap.Sync()
69
_ = humanFile.Close()
70
return nil, err
71
}
72
73
return &Logs{
74
Dir: dir,
75
HumanPath: humanPath,
76
DebugPath: debugPath,
77
HumanLogger: humanSug,
78
DebugLogger: debugSug,
79
HumanZap: humanZap,
80
DebugZap: debugZap,
81
humanFile: humanFile,
82
debugFile: debugFile,
83
}, nil
84
}
85
86
func newFileLogger(path string, level zapcore.Level, json bool) (*zap.SugaredLogger, *zap.Logger, *os.File, error) {
87
f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
88
if err != nil {
89
return nil, nil, nil, err
90
}
91
92
encCfg := zap.NewProductionEncoderConfig()
93
encCfg.TimeKey = "ts"
94
encCfg.EncodeTime = zapcore.ISO8601TimeEncoder
95
96
var enc zapcore.Encoder
97
if json {
98
enc = zapcore.NewJSONEncoder(encCfg) // JSONL: 1 JSON object per line
99
} else {
100
enc = zapcore.NewConsoleEncoder(encCfg) // human-readable
101
}
102
103
core := zapcore.NewCore(enc, zapcore.AddSync(f), level)
104
zl := zap.New(core)
105
return zl.Sugar(), zl, f, nil
106
}
107
108