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