-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathalertlog.go
154 lines (125 loc) · 3.74 KB
/
alertlog.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright (c) 2024, 2025, Oracle and/or its affiliates.
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.package vault
package alertlog
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"os"
"strings"
)
type LogRecord struct {
Timestamp string `json:"timestamp"`
ModuleId string `json:"moduleId"`
ECID string `json:"ecid"`
Message string `json:"message"`
}
var queryFailures int = 0
func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
if queryFailures == 3 {
logger.Info("Failed to query the alert log three consecutive times, so will not try any more")
queryFailures++
return
}
if queryFailures > 3 {
return
}
// check if the log file exists, and if not, create it
if _, err := os.Stat(logDestination); errors.Is(err, os.ErrNotExist) {
logger.Info("Log destination file does not exist, will try to create it: " + logDestination)
f, e := os.Create(logDestination)
if e != nil {
logger.Error("Failed to create the log file: " + logDestination)
return
}
f.Close()
}
// read the last line of the file to get the latest timestamp
file, err := os.Open(logDestination)
if err != nil {
logger.Error("Could not open the alert log destination file: " + logDestination)
return
}
// create an empty line
line := ""
// the file could be very large, so we will read backwards from the end of the file
// until the first line break is found, or until we reach the start of the file
var pointer int64 = 0
stat, _ := file.Stat()
filesize := stat.Size()
for {
if filesize == 0 {
break
}
pointer -= 1
file.Seek(pointer, io.SeekEnd)
char := make([]byte, 1)
file.Read(char)
if pointer != -1 && (char[0] == 10 || char[0] == 13) {
// we found a new line
break
}
line = fmt.Sprintf("%s%s", string(char), line)
if pointer == -filesize {
// we got all the way to the start of the file
break
}
}
// if we got an empty line, then set a reasonable default
if len(line) <= 1 {
// 2024-06-06T14:01:22.513Z
line = `{"timestamp":"1900-01-01T01:01:01.001Z","moduleId":"","ecid":"","message":""}`
}
file.Close()
// read the timestamp from the line
var lastLogRecord LogRecord
err = json.Unmarshal([]byte(line), &lastLogRecord)
if err != nil {
logger.Error("Could not parse last line of log file")
return
}
// query for any new alert log entries
stmt := fmt.Sprintf(`select originating_timestamp, module_id, execution_context_id, message_text
from v$diag_alert_ext
where originating_timestamp > to_utc_timestamp_tz('%s')`, lastLogRecord.Timestamp)
rows, err := db.Query(stmt)
if err != nil {
logger.Error("Error querying the alert logs")
queryFailures++
return
}
defer rows.Close()
// write them to the file
outfile, err := os.OpenFile(logDestination, os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
logger.Error("Could not open log file for writing: " + logDestination)
return
}
defer outfile.Close()
queryFailures = 0
for rows.Next() {
var newRecord LogRecord
if err := rows.Scan(&newRecord.Timestamp, &newRecord.ModuleId, &newRecord.ECID, &newRecord.Message); err != nil {
logger.Error("Error reading a row from the alert logs")
return
}
// strip the newline from end of message
newRecord.Message = strings.TrimSuffix(newRecord.Message, "\n")
jsonLogRecord, err := json.Marshal(newRecord)
if err != nil {
logger.Error("Error marshalling alert log record")
return
}
if _, err = outfile.WriteString(string(jsonLogRecord) + "\n"); err != nil {
logger.Error("Could not write to log file: " + logDestination)
return
}
}
if err = rows.Err(); err != nil {
logger.Error("Error querying the alert logs")
queryFailures++
}
}