1package models
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "os"
8 "path/filepath"
9 "strings"
10)
11
12type WorkflowLogger interface {
13 Close() error
14 DataWriter(idx int, stream string) io.Writer
15 ControlWriter(idx int, step Step, stepStatus StepStatus) io.Writer
16}
17
18type NullLogger struct{}
19
20func (l NullLogger) Close() error { return nil }
21func (l NullLogger) DataWriter(idx int, stream string) io.Writer { return io.Discard }
22func (l NullLogger) ControlWriter(idx int, step Step, stepStatus StepStatus) io.Writer {
23 return io.Discard
24}
25
26type FileWorkflowLogger struct {
27 file *os.File
28 encoder *json.Encoder
29 mask *SecretMask
30}
31
32func NewFileWorkflowLogger(baseDir string, wid WorkflowId, secretValues []string) (WorkflowLogger, error) {
33 path := LogFilePath(baseDir, wid)
34 file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
35 if err != nil {
36 return nil, fmt.Errorf("creating log file: %w", err)
37 }
38 return &FileWorkflowLogger{
39 file: file,
40 encoder: json.NewEncoder(file),
41 mask: NewSecretMask(secretValues),
42 }, nil
43}
44
45func LogFilePath(baseDir string, workflowID WorkflowId) string {
46 logFilePath := filepath.Join(baseDir, fmt.Sprintf("%s.log", workflowID.String()))
47 return logFilePath
48}
49
50func (l *FileWorkflowLogger) Close() error {
51 return l.file.Close()
52}
53
54func (l *FileWorkflowLogger) DataWriter(idx int, stream string) io.Writer {
55 return &dataWriter{
56 logger: l,
57 idx: idx,
58 stream: stream,
59 }
60}
61
62func (l *FileWorkflowLogger) ControlWriter(idx int, step Step, stepStatus StepStatus) io.Writer {
63 return &controlWriter{
64 logger: l,
65 idx: idx,
66 step: step,
67 stepStatus: stepStatus,
68 }
69}
70
71type dataWriter struct {
72 logger *FileWorkflowLogger
73 idx int
74 stream string
75}
76
77func (w *dataWriter) Write(p []byte) (int, error) {
78 line := strings.TrimRight(string(p), "\r\n")
79 if w.logger.mask != nil {
80 line = w.logger.mask.Mask(line)
81 }
82 entry := NewDataLogLine(w.idx, line, w.stream)
83 if err := w.logger.encoder.Encode(entry); err != nil {
84 return 0, err
85 }
86 return len(p), nil
87}
88
89type controlWriter struct {
90 logger *FileWorkflowLogger
91 idx int
92 step Step
93 stepStatus StepStatus
94}
95
96func (w *controlWriter) Write(_ []byte) (int, error) {
97 entry := NewControlLogLine(w.idx, w.step, w.stepStatus)
98 if err := w.logger.encoder.Encode(entry); err != nil {
99 return 0, err
100 }
101 return len(w.step.Name()), nil
102}