-
Notifications
You must be signed in to change notification settings - Fork 340
feat: 新增 dmesg 错误日志检查插件 #1415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 新增 dmesg 错误日志检查插件 #1415
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,241 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package dmesg | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "bytes" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "log" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strconv" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "syscall" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "flashcat.cloud/categraf/config" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "flashcat.cloud/categraf/inputs" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "flashcat.cloud/categraf/types" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+15
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inputName = "dmesg" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultBufSize = uint32(1 << 14) // 16KB by default | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| levelMask = uint64(1<<3 - 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OomError = "Out of memory" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NfConntrackTableFull = "nf_conntrack: table full" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DropPacket = "dropping packet" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| WillResetAdapter = "will reset adapter" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MemoryError = "memory error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResetSuccessfulForScsi = "Reset successful for scsi" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CallTrace = "Call Trace" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Segfault = "segfault" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NicLinkDown = "NIC Link is Down" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ext4FsError = "EXT4-fs error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediumError = "Medium Error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PackageTemperatureAboveThreshold = "Package temperature above threshold" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Msg struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Level uint64 // SYSLOG lvel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Level uint64 // SYSLOG lvel | |
| Level uint64 // SYSLOG level |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The read loop ignores the byte count returned by syscall.Read and always passes the full fixed-size buffer to parseData. That means parseData is parsing trailing zero bytes and can mis-handle end-of-line logic. Capture n and call parseData(buf[:n]) (and consider skipping parse on non-nil errors like EINVAL if the message is known-truncated).
| _, err := syscall.Read(int(fd), buf) | |
| if err != nil { | |
| syscallError = err | |
| // EINVAL means buf is not enough, data would be truncated, but still can continue. | |
| if !errors.Is(err, syscall.EINVAL) { | |
| return true | |
| } | |
| } | |
| msg := parseData(buf) | |
| n, err := syscall.Read(int(fd), buf) | |
| if err != nil { | |
| syscallError = err | |
| // EINVAL means buf is not enough, data would be truncated, but still can continue. | |
| if errors.Is(err, syscall.EINVAL) { | |
| // Skip parsing known-truncated data. | |
| continue | |
| } | |
| return true | |
| } | |
| if n <= 0 { | |
| continue | |
| } | |
| msg := parseData(buf[:n]) |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Gather loop allocates a new 16KB buffer and appends Msg structs for every kmsg line, then does a second pass just to count keyword hits. This creates avoidable allocations and memory growth under heavy kernel logging. Consider reusing a single buffer (or a pool) and incrementing counters as each message is parsed, without storing all messages in msgs.
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a potential panic in parseData: msg.IsFragment = prefix[0] != '-' assumes the 4th prefix field is non-empty. If the field is empty/malformed, this will index out of range. Add a length check before accessing prefix[0] and treat empty as non-fragment (or return nil).
| msg.IsFragment = prefix[0] != '-' | |
| if len(prefix) > 0 { | |
| msg.IsFragment = prefix[0] != '-' | |
| } else { | |
| // Treat empty or malformed fragment field as non-fragment. | |
| msg.IsFragment = false | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a potential panic in device-info parsing: if info[0] != ' ' will panic if info is empty (e.g., consecutive newlines or an empty trailer slice). Guard len(info) > 0 before indexing.
| if info[0] != ' ' { | |
| if len(info) == 0 || info[0] != ' ' { |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseData returns nil when the log line ends right after the message text (textEnd == dataLen-1). That drops the common /dev/kmsg case where there is no device-info trailer, so keyword detection will miss most messages. This function should return the parsed Msg even when there is no device info section.
| if textEnd == dataLen-1 { | |
| return nil | |
| } | |
| msg.DeviceInfo = make(map[string]string, 2) | |
| deviceInfo := bytes.Split(data[textEnd+1:dataLen-1], []byte("\n")) | |
| for _, info := range deviceInfo { | |
| if info[0] != ' ' { | |
| continue | |
| } | |
| kv := bytes.Split(info, []byte("=")) | |
| if len(kv) != 2 { | |
| continue | |
| } | |
| msg.DeviceInfo[string(kv[0])] = string(kv[1]) | |
| // Only parse device info if there is data after the message text. | |
| if textEnd < dataLen-1 { | |
| msg.DeviceInfo = make(map[string]string, 2) | |
| deviceInfo := bytes.Split(data[textEnd+1:dataLen-1], []byte("\n")) | |
| for _, info := range deviceInfo { | |
| if len(info) == 0 || info[0] != ' ' { | |
| continue | |
| } | |
| kv := bytes.Split(info, []byte("=")) | |
| if len(kv) != 2 { | |
| continue | |
| } | |
| msg.DeviceInfo[string(kv[0])] = string(kv[1]) | |
| } |
Copilot
AI
Mar 11, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new parser/keyword matching logic isn’t covered by tests. Since it’s easy to regress and interacts with tricky /dev/kmsg formatting (prefix parsing, no-device-info lines, empty segments), please add unit tests for parseData (at least: normal line without device info, line with device info, and malformed/empty segments).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blank-importing the dmesg input will break non-Linux builds unless the input package is guarded by Linux build tags and provides a
//go:build !linuxstub file. Please ensure the dmesg input follows the same cross-platform pattern as other Linux-only inputs (e.g., inputs/conntrack).