Skip to content

Commit da2a46f

Browse files
authored
feat: emit run summary on stderr for agent/observability (#197)
1 parent 8aabbb1 commit da2a46f

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

pkg/cmd/root/root.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package root
44

55
import (
66
"context"
7+
"encoding/json"
78
"errors"
89
"fmt"
910
"io"
@@ -13,6 +14,7 @@ import (
1314
"os/exec"
1415
"path/filepath"
1516
"strings"
17+
"time"
1618

1719
"github.com/AlecAivazis/survey/v2/terminal"
1820
"github.com/MakeNowJust/heredoc"
@@ -115,6 +117,56 @@ func NewRootCmd(f *cmdutil.Factory) *cobra.Command {
115117
return cmd
116118
}
117119

120+
// runSummary is the JSON object written to stderr when observability env vars are set.
121+
type runSummary struct {
122+
Event string `json:"event"`
123+
InvocationID string `json:"invocation_id"`
124+
Command string `json:"command"`
125+
Status string `json:"status"`
126+
DurationMs int64 `json:"duration_ms"`
127+
Error string `json:"error,omitempty"`
128+
}
129+
130+
func shouldEmitRunSummary() bool {
131+
return os.Getenv("ALGOLIA_CLI_NON_INTERACTIVE") == "1" ||
132+
os.Getenv("ALGOLIA_CLI_OBSERVABILITY") == "1"
133+
}
134+
135+
func emitRunSummary(stderr io.Writer, ctx context.Context, cmd *cobra.Command, runErr error, duration time.Duration) {
136+
meta := telemetry.GetEventMetadata(ctx)
137+
invocationID := ""
138+
if meta != nil {
139+
invocationID = meta.InvocationID
140+
}
141+
commandPath := ""
142+
if meta != nil && meta.CommandPath != "" {
143+
commandPath = meta.CommandPath
144+
}
145+
if commandPath == "" && cmd != nil {
146+
commandPath = cmd.CommandPath()
147+
}
148+
status := "ok"
149+
errMsg := ""
150+
if runErr != nil {
151+
status = "error"
152+
errMsg = runErr.Error()
153+
if len(errMsg) > 500 {
154+
errMsg = errMsg[:497] + "..."
155+
}
156+
}
157+
s := runSummary{
158+
Event: "cli_run",
159+
InvocationID: invocationID,
160+
Command: commandPath,
161+
Status: status,
162+
DurationMs: duration.Milliseconds(),
163+
Error: errMsg,
164+
}
165+
enc := json.NewEncoder(stderr)
166+
enc.SetEscapeHTML(false)
167+
_ = enc.Encode(s)
168+
}
169+
118170
func Execute() exitCode {
119171
hasDebug := os.Getenv("DEBUG") != ""
120172
hasTelemetry := os.Getenv("ALGOLIA_CLI_TELEMETRY") != "0"
@@ -200,7 +252,14 @@ func Execute() exitCode {
200252
}
201253

202254
// Run the command.
255+
start := time.Now()
203256
cmd, err := rootCmd.ExecuteContextC(ctx)
257+
duration := time.Since(start)
258+
259+
if shouldEmitRunSummary() {
260+
emitRunSummary(stderr, ctx, cmd, err, duration)
261+
}
262+
204263
// Handle eventual errors.
205264
if err != nil {
206265
if err == cmdutil.ErrSilent {

0 commit comments

Comments
 (0)