@@ -22,6 +22,8 @@ import (
22
22
23
23
"cdr.dev/slog"
24
24
"cdr.dev/slog/sloggers/sloghuman"
25
+ "cdr.dev/slog/sloggers/slogjson"
26
+ "cdr.dev/slog/sloggers/slogstackdriver"
25
27
"github.com/coder/coder/agent"
26
28
"github.com/coder/coder/agent/reaper"
27
29
"github.com/coder/coder/buildinfo"
@@ -32,14 +34,17 @@ import (
32
34
33
35
func (r * RootCmd ) workspaceAgent () * clibase.Cmd {
34
36
var (
35
- auth string
36
- logDir string
37
- pprofAddress string
38
- noReap bool
39
- sshMaxTimeout time.Duration
40
- tailnetListenPort int64
41
- prometheusAddress string
42
- debugAddress string
37
+ auth string
38
+ logDir string
39
+ pprofAddress string
40
+ noReap bool
41
+ sshMaxTimeout time.Duration
42
+ tailnetListenPort int64
43
+ prometheusAddress string
44
+ debugAddress string
45
+ slogHumanPath string
46
+ slogJSONPath string
47
+ slogStackdriverPath string
43
48
)
44
49
cmd := & clibase.Cmd {
45
50
Use : "agent" ,
@@ -62,7 +67,46 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
62
67
MaxSize : 5 , // MB
63
68
}
64
69
defer logWriter .Close ()
65
- logger := slog .Make (sloghuman .Sink (inv .Stderr ), sloghuman .Sink (logWriter )).Leveled (slog .LevelDebug )
70
+
71
+ sinks := []slog.Sink {sloghuman .Sink (logWriter )}
72
+ closers := []func () error {}
73
+ addSinkIfProvided := func (sinkFn func (io.Writer ) slog.Sink , loc string ) error {
74
+ switch loc {
75
+ case "" :
76
+
77
+ case "/dev/stdout" :
78
+ sinks = append (sinks , sinkFn (inv .Stdout ))
79
+
80
+ case "/dev/stderr" :
81
+ sinks = append (sinks , sinkFn (inv .Stderr ))
82
+
83
+ default :
84
+ fi , err := os .OpenFile (loc , os .O_WRONLY | os .O_CREATE | os .O_APPEND , 0o644 )
85
+ if err != nil {
86
+ return xerrors .Errorf ("open log file %q: %w" , loc , err )
87
+ }
88
+ closers = append (closers , fi .Close )
89
+ sinks = append (sinks , sinkFn (fi ))
90
+ }
91
+ return nil
92
+ }
93
+
94
+ if err := addSinkIfProvided (sloghuman .Sink , slogHumanPath ); err != nil {
95
+ return xerrors .Errorf ("add human sink: %w" , err )
96
+ }
97
+ if err := addSinkIfProvided (slogjson .Sink , slogJSONPath ); err != nil {
98
+ return xerrors .Errorf ("add json sink: %w" , err )
99
+ }
100
+ if err := addSinkIfProvided (slogstackdriver .Sink , slogStackdriverPath ); err != nil {
101
+ return xerrors .Errorf ("add stackdriver sink: %w" , err )
102
+ }
103
+
104
+ logger := slog .Make (sinks ... ).Leveled (slog .LevelDebug )
105
+ defer func () {
106
+ for _ , closer := range closers {
107
+ _ = closer ()
108
+ }
109
+ }()
66
110
67
111
logger .Info (ctx , "spawning reaper process" )
68
112
// Do not start a reaper on the child process. It's important
@@ -290,6 +334,30 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd {
290
334
Value : clibase .StringOf (& debugAddress ),
291
335
Description : "The bind address to serve a debug HTTP server." ,
292
336
},
337
+ {
338
+ Name : "Human Log Location" ,
339
+ Description : "Output human-readable logs to a given file." ,
340
+ Flag : "log-human" ,
341
+ Env : "CODER_AGENT_LOGGING_HUMAN" ,
342
+ Default : "/dev/stderr" ,
343
+ Value : clibase .StringOf (& slogHumanPath ),
344
+ },
345
+ {
346
+ Name : "JSON Log Location" ,
347
+ Description : "Output JSON logs to a given file." ,
348
+ Flag : "log-json" ,
349
+ Env : "CODER_AGENT_LOGGING_JSON" ,
350
+ Default : "" ,
351
+ Value : clibase .StringOf (& slogJSONPath ),
352
+ },
353
+ {
354
+ Name : "Stackdriver Log Location" ,
355
+ Description : "Output Stackdriver compatible logs to a given file." ,
356
+ Flag : "log-stackdriver" ,
357
+ Env : "CODER_AGENT_LOGGING_STACKDRIVER" ,
358
+ Default : "" ,
359
+ Value : clibase .StringOf (& slogStackdriverPath ),
360
+ },
293
361
}
294
362
295
363
return cmd
0 commit comments