-
Notifications
You must be signed in to change notification settings - Fork 0
/
job_log.go
139 lines (114 loc) · 3.08 KB
/
job_log.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// SPDX-FileCopyrightText: 2022 M. Shulhan <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later
package karajo
import (
"bytes"
"encoding/base64"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
)
// JobLog contains the content, status, and counter for job's log.
//
// Each log file name is using the following format:
//
// <job.ID>.<counter>.<status>
//
// Counter is a number that unique between log, start from 1.
//
// Status can be success or fail.
// If status is missing its considered fail.
type JobLog struct {
jobKind jobKind
JobID string `json:"job_id"`
Name string `json:"name"`
path string
Status string `json:"status,omitempty"`
Content []byte `json:"content,omitempty"` // Only used to transfrom from/to JSON.
content []byte
// listNotif contains list of notification where the job log will be
// send.
listNotif []string
Counter int64 `json:"counter,omitempty"`
sync.Mutex
}
// parseJobLogName parse the log file name to unpack the name, counter, and
// status.
// If the name is not valid, the file is removed and it will return nil.
func parseJobLogName(dir, name string) (jlog *JobLog) {
var logFields = strings.Split(name, `.`)
jlog = &JobLog{
Name: name,
path: filepath.Join(dir, name),
}
if len(logFields) <= 1 {
_ = os.Remove(jlog.path)
return nil
}
jlog.JobID = logFields[0]
var err error
jlog.Counter, err = strconv.ParseInt(logFields[1], 10, 64)
if err != nil {
_ = os.Remove(jlog.path)
return nil
}
if len(logFields) == 2 {
// No status on filename, assume it as fail.
_ = os.Remove(jlog.path)
return nil
}
jlog.Status = logFields[2]
return jlog
}
func (jlog *JobLog) flush() (err error) {
jlog.Lock()
jlog.Name = jlog.Name + `.` + jlog.Status
jlog.path = jlog.path + `.` + jlog.Status
err = os.WriteFile(jlog.path, jlog.content, 0600)
jlog.Unlock()
return err
}
// load the content of log from storage.
func (jlog *JobLog) load() (err error) {
jlog.Lock()
if len(jlog.content) == 0 {
jlog.content, err = os.ReadFile(jlog.path)
}
jlog.Unlock()
return err
}
func (jlog *JobLog) marshalJSON() ([]byte, error) {
jlog.Lock()
var (
buf bytes.Buffer
content = base64.StdEncoding.EncodeToString(jlog.content)
)
fmt.Fprintf(&buf, `{"job_id":%q,"name":%q,"status":%q,"counter":%d,"content":%q}`,
jlog.JobID, jlog.Name, jlog.Status, jlog.Counter, content)
jlog.Unlock()
return buf.Bytes(), nil
}
func (jlog *JobLog) setStatus(status string) {
jlog.Lock()
jlog.Status = status
jlog.Unlock()
}
func (jlog *JobLog) Write(b []byte) (n int, err error) {
jlog.Lock()
n = len(jlog.content)
if n == 0 || n > 0 && jlog.content[n-1] == '\n' {
var timestamp = timeNow().Format(defTimeLayout)
jlog.content = append(jlog.content, []byte(timestamp)...)
jlog.content = append(jlog.content, ' ')
jlog.content = append(jlog.content, []byte(jlog.jobKind)...)
jlog.content = append(jlog.content, []byte(": ")...)
jlog.content = append(jlog.content, []byte(jlog.JobID)...)
jlog.content = append(jlog.content, []byte(": ")...)
}
jlog.content = append(jlog.content, b...)
jlog.Unlock()
return len(b), nil
}