1package engine
2
3import (
4 "fmt"
5 "io"
6 "os"
7 "path/filepath"
8)
9
10type StepLogger struct {
11 stderr *os.File
12 stdout *os.File
13}
14
15func NewStepLogger(baseDir, workflowID string, stepIdx int) (*StepLogger, error) {
16 dir := filepath.Join(baseDir, workflowID)
17 if err := os.MkdirAll(dir, 0755); err != nil {
18 return nil, fmt.Errorf("creating log dir: %w", err)
19 }
20
21 stdoutPath := logFilePath(baseDir, workflowID, "stdout", stepIdx)
22 stderrPath := logFilePath(baseDir, workflowID, "stderr", stepIdx)
23
24 stdoutFile, err := os.Create(stdoutPath)
25 if err != nil {
26 return nil, fmt.Errorf("creating stdout log file: %w", err)
27 }
28
29 stderrFile, err := os.Create(stderrPath)
30 if err != nil {
31 stdoutFile.Close()
32 return nil, fmt.Errorf("creating stderr log file: %w", err)
33 }
34
35 return &StepLogger{
36 stdout: stdoutFile,
37 stderr: stderrFile,
38 }, nil
39}
40
41func (l *StepLogger) Stdout() io.Writer {
42 return l.stdout
43}
44
45func (l *StepLogger) Stderr() io.Writer {
46 return l.stderr
47}
48
49func (l *StepLogger) Close() error {
50 err1 := l.stdout.Close()
51 err2 := l.stderr.Close()
52 if err1 != nil {
53 return err1
54 }
55 return err2
56}
57
58func ReadStepLog(baseDir, workflowID, stream string, stepIdx int) (string, error) {
59 logPath := logFilePath(baseDir, workflowID, stream, stepIdx)
60
61 data, err := os.ReadFile(logPath)
62 if err != nil {
63 return "", fmt.Errorf("error reading log file: %w", err)
64 }
65
66 return string(data), nil
67}
68
69func logFilePath(baseDir, workflowID, stream string, stepIdx int) string {
70 logFilePath := filepath.Join(baseDir, workflowID, fmt.Sprintf("%d-%s.log", stepIdx, stream))
71 return logFilePath
72}