From 5c3fcc603e64374dd78257b4998410594d5195bc Mon Sep 17 00:00:00 2001 From: ___ Date: Tue, 9 May 2023 20:55:16 +0000 Subject: [PATCH 1/4] feat: Add logging options for coder agent Similar to logging options in the coderd server, but for the agent running in workspaces. Meant to make hollistic log collection and querying simpler. --- cli/agent.go | 88 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 9 deletions(-) diff --git a/cli/agent.go b/cli/agent.go index da39f1caa22b9..de3d237f806df 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -22,6 +22,8 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" + "cdr.dev/slog/sloggers/slogjson" + "cdr.dev/slog/sloggers/slogstackdriver" "github.com/coder/coder/agent" "github.com/coder/coder/agent/reaper" "github.com/coder/coder/buildinfo" @@ -31,14 +33,17 @@ import ( func (r *RootCmd) workspaceAgent() *clibase.Cmd { var ( - auth string - logDir string - pprofAddress string - noReap bool - sshMaxTimeout time.Duration - tailnetListenPort int64 - prometheusAddress string - debugAddress string + auth string + logDir string + pprofAddress string + noReap bool + sshMaxTimeout time.Duration + tailnetListenPort int64 + prometheusAddress string + debugAddress string + slogHumanPath string + slogJsonPath string + slogStackdriverPath string ) cmd := &clibase.Cmd{ Use: "agent", @@ -61,7 +66,48 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { MaxSize: 5, // MB } defer logWriter.Close() - logger := slog.Make(sloghuman.Sink(inv.Stderr), sloghuman.Sink(logWriter)).Leveled(slog.LevelDebug) + + sinks := []slog.Sink{sloghuman.Sink(logWriter)} + closers := []func() error{} + addSinkIfProvided := func(sinkFn func(io.Writer) slog.Sink, loc string) error { + switch loc { + case "": + + case "/dev/stdout": + sinks = append(sinks, sinkFn(inv.Stdout)) + break + + case "/dev/stderr": + sinks = append(sinks, sinkFn(inv.Stderr)) + break + + default: + fi, err := os.OpenFile(loc, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) + if err != nil { + return xerrors.Errorf("open log file %q: %w", loc, err) + } + closers = append(closers, fi.Close) + sinks = append(sinks, sinkFn(fi)) + } + return nil + } + + if err := addSinkIfProvided(sloghuman.Sink, slogHumanPath); err != nil { + return xerrors.Errorf("add human sink: %w", err) + } + if err := addSinkIfProvided(slogjson.Sink, slogJsonPath); err != nil { + return xerrors.Errorf("add json sink: %w", err) + } + if err := addSinkIfProvided(slogstackdriver.Sink, slogStackdriverPath); err != nil { + return xerrors.Errorf("add stackdriver sink: %w", err) + } + + logger := slog.Make(sinks...).Leveled(slog.LevelDebug) + defer func() { + for _, closer := range closers { + _ = closer() + } + }() logger.Info(ctx, "spawning reaper process") // Do not start a reaper on the child process. It's important @@ -289,6 +335,30 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { Value: clibase.StringOf(&debugAddress), Description: "The bind address to serve a debug HTTP server.", }, + { + Name: "Human Log Location", + Description: "Output human-readable logs to a given file.", + Flag: "log-human", + Env: "CODER_AGENT_LOGGING_HUMAN", + Default: "/dev/stderr", + Value: clibase.StringOf(&slogHumanPath), + }, + { + Name: "JSON Log Location", + Description: "Output JSON logs to a given file.", + Flag: "log-json", + Env: "CODER_AGENT_LOGGING_JSON", + Default: "", + Value: clibase.StringOf(&slogJsonPath), + }, + { + Name: "Stackdriver Log Location", + Description: "Output Stackdriver compatible logs to a given file.", + Flag: "log-stackdriver", + Env: "CODER_AGENT_LOGGING_STACKDRIVER", + Default: "", + Value: clibase.StringOf(&slogStackdriverPath), + }, } return cmd From 1034c0c5bda80d48b437427dc19f621a02a359ae Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 24 May 2023 08:42:52 +0000 Subject: [PATCH 2/4] make gen --- coderd/database/models.go | 42 +++++++++++++++++----------------- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/coderd/database/models.go b/coderd/database/models.go index 4cef35e644a4c..a0bd9595c89ce 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.17.2 +// sqlc v1.16.0 package database @@ -56,7 +56,7 @@ func (ns NullAPIKeyScope) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.APIKeyScope), nil + return ns.APIKeyScope, nil } func (e APIKeyScope) Valid() bool { @@ -115,7 +115,7 @@ func (ns NullAppSharingLevel) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.AppSharingLevel), nil + return ns.AppSharingLevel, nil } func (e AppSharingLevel) Valid() bool { @@ -181,7 +181,7 @@ func (ns NullAuditAction) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.AuditAction), nil + return ns.AuditAction, nil } func (e AuditAction) Valid() bool { @@ -252,7 +252,7 @@ func (ns NullBuildReason) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.BuildReason), nil + return ns.BuildReason, nil } func (e BuildReason) Valid() bool { @@ -315,7 +315,7 @@ func (ns NullLogLevel) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.LogLevel), nil + return ns.LogLevel, nil } func (e LogLevel) Valid() bool { @@ -379,7 +379,7 @@ func (ns NullLogSource) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.LogSource), nil + return ns.LogSource, nil } func (e LogSource) Valid() bool { @@ -439,7 +439,7 @@ func (ns NullLoginType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.LoginType), nil + return ns.LoginType, nil } func (e LoginType) Valid() bool { @@ -502,7 +502,7 @@ func (ns NullParameterDestinationScheme) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ParameterDestinationScheme), nil + return ns.ParameterDestinationScheme, nil } func (e ParameterDestinationScheme) Valid() bool { @@ -563,7 +563,7 @@ func (ns NullParameterScope) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ParameterScope), nil + return ns.ParameterScope, nil } func (e ParameterScope) Valid() bool { @@ -623,7 +623,7 @@ func (ns NullParameterSourceScheme) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ParameterSourceScheme), nil + return ns.ParameterSourceScheme, nil } func (e ParameterSourceScheme) Valid() bool { @@ -681,7 +681,7 @@ func (ns NullParameterTypeSystem) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ParameterTypeSystem), nil + return ns.ParameterTypeSystem, nil } func (e ParameterTypeSystem) Valid() bool { @@ -740,7 +740,7 @@ func (ns NullProvisionerJobType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ProvisionerJobType), nil + return ns.ProvisionerJobType, nil } func (e ProvisionerJobType) Valid() bool { @@ -799,7 +799,7 @@ func (ns NullProvisionerStorageMethod) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ProvisionerStorageMethod), nil + return ns.ProvisionerStorageMethod, nil } func (e ProvisionerStorageMethod) Valid() bool { @@ -855,7 +855,7 @@ func (ns NullProvisionerType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ProvisionerType), nil + return ns.ProvisionerType, nil } func (e ProvisionerType) Valid() bool { @@ -922,7 +922,7 @@ func (ns NullResourceType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.ResourceType), nil + return ns.ResourceType, nil } func (e ResourceType) Valid() bool { @@ -998,7 +998,7 @@ func (ns NullUserStatus) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.UserStatus), nil + return ns.UserStatus, nil } func (e UserStatus) Valid() bool { @@ -1063,7 +1063,7 @@ func (ns NullWorkspaceAgentLifecycleState) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.WorkspaceAgentLifecycleState), nil + return ns.WorkspaceAgentLifecycleState, nil } func (e WorkspaceAgentLifecycleState) Valid() bool { @@ -1136,7 +1136,7 @@ func (ns NullWorkspaceAgentSubsystem) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.WorkspaceAgentSubsystem), nil + return ns.WorkspaceAgentSubsystem, nil } func (e WorkspaceAgentSubsystem) Valid() bool { @@ -1198,7 +1198,7 @@ func (ns NullWorkspaceAppHealth) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.WorkspaceAppHealth), nil + return ns.WorkspaceAppHealth, nil } func (e WorkspaceAppHealth) Valid() bool { @@ -1261,7 +1261,7 @@ func (ns NullWorkspaceTransition) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return string(ns.WorkspaceTransition), nil + return ns.WorkspaceTransition, nil } func (e WorkspaceTransition) Valid() bool { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index e30207c7ba44e..fbb564358049c 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.17.2 +// sqlc v1.16.0 package database diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 9955deb439ffd..4070e6b67861b 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.17.2 +// sqlc v1.16.0 package database From 26e34181d329a8361a8121eff41a6eb93689500c Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 24 May 2023 08:48:26 +0000 Subject: [PATCH 3/4] make lint --- cli/agent.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cli/agent.go b/cli/agent.go index 0d3dc726651ed..cb127ce292264 100644 --- a/cli/agent.go +++ b/cli/agent.go @@ -43,7 +43,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { prometheusAddress string debugAddress string slogHumanPath string - slogJsonPath string + slogJSONPath string slogStackdriverPath string ) cmd := &clibase.Cmd{ @@ -76,11 +76,9 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { case "/dev/stdout": sinks = append(sinks, sinkFn(inv.Stdout)) - break case "/dev/stderr": sinks = append(sinks, sinkFn(inv.Stderr)) - break default: fi, err := os.OpenFile(loc, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) @@ -96,7 +94,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { if err := addSinkIfProvided(sloghuman.Sink, slogHumanPath); err != nil { return xerrors.Errorf("add human sink: %w", err) } - if err := addSinkIfProvided(slogjson.Sink, slogJsonPath); err != nil { + if err := addSinkIfProvided(slogjson.Sink, slogJSONPath); err != nil { return xerrors.Errorf("add json sink: %w", err) } if err := addSinkIfProvided(slogstackdriver.Sink, slogStackdriverPath); err != nil { @@ -350,7 +348,7 @@ func (r *RootCmd) workspaceAgent() *clibase.Cmd { Flag: "log-json", Env: "CODER_AGENT_LOGGING_JSON", Default: "", - Value: clibase.StringOf(&slogJsonPath), + Value: clibase.StringOf(&slogJSONPath), }, { Name: "Stackdriver Log Location", From f99a470e4a3669c7f720a870fbd7eefcb21b22f0 Mon Sep 17 00:00:00 2001 From: Atif Ali Date: Wed, 24 May 2023 14:23:33 +0000 Subject: [PATCH 4/4] make update-golden-file --- cli/testdata/coder_agent_--help.golden | 9 ++++++ coderd/database/models.go | 42 +++++++++++++------------- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 2 +- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/cli/testdata/coder_agent_--help.golden b/cli/testdata/coder_agent_--help.golden index 5b9e6f394076e..90fff753cd365 100644 --- a/cli/testdata/coder_agent_--help.golden +++ b/cli/testdata/coder_agent_--help.golden @@ -3,6 +3,15 @@ Usage: coder agent [flags] Starts the Coder workspace agent. Options + --log-human string, $CODER_AGENT_LOGGING_HUMAN (default: /dev/stderr) + Output human-readable logs to a given file. + + --log-json string, $CODER_AGENT_LOGGING_JSON + Output JSON logs to a given file. + + --log-stackdriver string, $CODER_AGENT_LOGGING_STACKDRIVER + Output Stackdriver compatible logs to a given file. + --auth string, $CODER_AGENT_AUTH (default: token) Specify the authentication type to use for the agent. diff --git a/coderd/database/models.go b/coderd/database/models.go index a0bd9595c89ce..4cef35e644a4c 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 package database @@ -56,7 +56,7 @@ func (ns NullAPIKeyScope) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.APIKeyScope, nil + return string(ns.APIKeyScope), nil } func (e APIKeyScope) Valid() bool { @@ -115,7 +115,7 @@ func (ns NullAppSharingLevel) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.AppSharingLevel, nil + return string(ns.AppSharingLevel), nil } func (e AppSharingLevel) Valid() bool { @@ -181,7 +181,7 @@ func (ns NullAuditAction) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.AuditAction, nil + return string(ns.AuditAction), nil } func (e AuditAction) Valid() bool { @@ -252,7 +252,7 @@ func (ns NullBuildReason) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.BuildReason, nil + return string(ns.BuildReason), nil } func (e BuildReason) Valid() bool { @@ -315,7 +315,7 @@ func (ns NullLogLevel) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.LogLevel, nil + return string(ns.LogLevel), nil } func (e LogLevel) Valid() bool { @@ -379,7 +379,7 @@ func (ns NullLogSource) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.LogSource, nil + return string(ns.LogSource), nil } func (e LogSource) Valid() bool { @@ -439,7 +439,7 @@ func (ns NullLoginType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.LoginType, nil + return string(ns.LoginType), nil } func (e LoginType) Valid() bool { @@ -502,7 +502,7 @@ func (ns NullParameterDestinationScheme) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ParameterDestinationScheme, nil + return string(ns.ParameterDestinationScheme), nil } func (e ParameterDestinationScheme) Valid() bool { @@ -563,7 +563,7 @@ func (ns NullParameterScope) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ParameterScope, nil + return string(ns.ParameterScope), nil } func (e ParameterScope) Valid() bool { @@ -623,7 +623,7 @@ func (ns NullParameterSourceScheme) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ParameterSourceScheme, nil + return string(ns.ParameterSourceScheme), nil } func (e ParameterSourceScheme) Valid() bool { @@ -681,7 +681,7 @@ func (ns NullParameterTypeSystem) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ParameterTypeSystem, nil + return string(ns.ParameterTypeSystem), nil } func (e ParameterTypeSystem) Valid() bool { @@ -740,7 +740,7 @@ func (ns NullProvisionerJobType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ProvisionerJobType, nil + return string(ns.ProvisionerJobType), nil } func (e ProvisionerJobType) Valid() bool { @@ -799,7 +799,7 @@ func (ns NullProvisionerStorageMethod) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ProvisionerStorageMethod, nil + return string(ns.ProvisionerStorageMethod), nil } func (e ProvisionerStorageMethod) Valid() bool { @@ -855,7 +855,7 @@ func (ns NullProvisionerType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ProvisionerType, nil + return string(ns.ProvisionerType), nil } func (e ProvisionerType) Valid() bool { @@ -922,7 +922,7 @@ func (ns NullResourceType) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.ResourceType, nil + return string(ns.ResourceType), nil } func (e ResourceType) Valid() bool { @@ -998,7 +998,7 @@ func (ns NullUserStatus) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.UserStatus, nil + return string(ns.UserStatus), nil } func (e UserStatus) Valid() bool { @@ -1063,7 +1063,7 @@ func (ns NullWorkspaceAgentLifecycleState) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.WorkspaceAgentLifecycleState, nil + return string(ns.WorkspaceAgentLifecycleState), nil } func (e WorkspaceAgentLifecycleState) Valid() bool { @@ -1136,7 +1136,7 @@ func (ns NullWorkspaceAgentSubsystem) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.WorkspaceAgentSubsystem, nil + return string(ns.WorkspaceAgentSubsystem), nil } func (e WorkspaceAgentSubsystem) Valid() bool { @@ -1198,7 +1198,7 @@ func (ns NullWorkspaceAppHealth) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.WorkspaceAppHealth, nil + return string(ns.WorkspaceAppHealth), nil } func (e WorkspaceAppHealth) Valid() bool { @@ -1261,7 +1261,7 @@ func (ns NullWorkspaceTransition) Value() (driver.Value, error) { if !ns.Valid { return nil, nil } - return ns.WorkspaceTransition, nil + return string(ns.WorkspaceTransition), nil } func (e WorkspaceTransition) Valid() bool { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index fbb564358049c..e30207c7ba44e 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 package database diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 4070e6b67861b..9955deb439ffd 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.16.0 +// sqlc v1.17.2 package database