@@ -17,86 +17,88 @@ package syslog
17
17
18
18
import (
19
19
"fmt"
20
- "io"
21
- "os"
22
- "strings"
20
+ "regexp"
23
21
"time"
24
22
25
23
kerntypes "k8s.io/node-problem-detector/pkg/kernelmonitor/types"
26
24
27
- "github.com/google/cadvisor/utils/tail "
25
+ "github.com/golang/glog "
28
26
)
29
27
28
+ // translator translates log line into internal log type based on user defined
29
+ // regular expression.
30
+ type translator struct {
31
+ timestampRegexp * regexp.Regexp
32
+ messageRegexp * regexp.Regexp
33
+ timestampFormat string
34
+ }
35
+
36
+ const (
37
+ // NOTE that we support submatch for both timestamp and message regular expressions. When
38
+ // there are multiple matches returned by submatch, only **the last** is used.
39
+ // timestampKey is the key of timestamp regular expression in the plugin configuration.
40
+ timestampKey = "timestamp"
41
+ // messageKey is the key of message regular expression in the plugin configuration.
42
+ messageKey = "message"
43
+ // timestampFormatKey is the key of timestamp format string in the plugin configuration.
44
+ timestampFormatKey = "timestampFormat"
45
+ )
46
+
47
+ func newTranslatorOrDie (pluginConfig map [string ]string ) * translator {
48
+ if err := validatePluginConfig (pluginConfig ); err != nil {
49
+ glog .Errorf ("Failed to validate plugin configuration %+v: %v" , pluginConfig , err )
50
+ }
51
+ return & translator {
52
+ timestampRegexp : regexp .MustCompile (pluginConfig [timestampKey ]),
53
+ messageRegexp : regexp .MustCompile (pluginConfig [messageKey ]),
54
+ timestampFormat : pluginConfig [timestampFormatKey ],
55
+ }
56
+ }
57
+
30
58
// translate translates the log line into internal type.
31
- func translate (line string ) (* kerntypes.KernelLog , error ) {
32
- timestamp , message , err := parseLine (line )
59
+ func (t * translator ) translate (line string ) (* kerntypes.KernelLog , error ) {
60
+ // Parse timestamp.
61
+ matches := t .timestampRegexp .FindStringSubmatch (line )
62
+ if len (matches ) == 0 {
63
+ return nil , fmt .Errorf ("no timestamp found in line %q with regular expression %v" , line , t .timestampRegexp )
64
+ }
65
+ timestamp , err := time .ParseInLocation (t .timestampFormat , matches [len (matches )- 1 ], time .Local )
33
66
if err != nil {
34
- return nil , err
67
+ return nil , fmt .Errorf ("failed to parse timestamp %q: %v" , matches [len (matches )- 1 ], err )
68
+ }
69
+ // Formalize the timestmap.
70
+ timestamp = formalizeTimestamp (timestamp )
71
+ // Parse message.
72
+ matches = t .messageRegexp .FindStringSubmatch (line )
73
+ if len (matches ) == 0 {
74
+ return nil , fmt .Errorf ("no message found in line %q with regular expression %v" , line , t .messageRegexp )
35
75
}
76
+ message := matches [len (matches )- 1 ]
36
77
return & kerntypes.KernelLog {
37
78
Timestamp : timestamp ,
38
79
Message : message ,
39
80
}, nil
40
81
}
41
82
42
- const (
43
- // timestampLen is the length of timestamp in syslog logging format.
44
- timestampLen = 15
45
- // messagePrefix is the character before real message.
46
- messagePrefix = "]"
47
- )
48
-
49
- // parseLine parses one log line into timestamp and message.
50
- func parseLine (line string ) (time.Time , string , error ) {
51
- // Trim the spaces to make sure timestamp could be found
52
- line = strings .TrimSpace (line )
53
- if len (line ) < timestampLen {
54
- return time.Time {}, "" , fmt .Errorf ("the line is too short: %q" , line )
83
+ // validatePluginConfig validates whether the plugin configuration.
84
+ func validatePluginConfig (cfg map [string ]string ) error {
85
+ if cfg [timestampKey ] == "" {
86
+ return fmt .Errorf ("unexpected empty timestamp regular expression" )
55
87
}
56
- // Example line: Jan 1 00:00:00 hostname kernel: [0.000000] component: log message
57
- now := time .Now ()
58
- // There is no time zone information in kernel log timestamp, apply the current time
59
- // zone.
60
- timestamp , err := time .ParseInLocation (time .Stamp , line [:timestampLen ], time .Local )
61
- if err != nil {
62
- return time.Time {}, "" , fmt .Errorf ("error parsing timestamp in line %q: %v" , line , err )
88
+ if cfg [messageKey ] == "" {
89
+ return fmt .Errorf ("unexpected empty message regular expression" )
63
90
}
64
- // There is no year information in kernel log timestamp, apply the current year.
65
- // This could go wrong during looking back phase after kernel monitor is started,
66
- // and the old logs are generated in old year.
67
- timestamp = timestamp .AddDate (now .Year (), 0 , 0 )
68
-
69
- loc := strings .Index (line , messagePrefix )
70
- if loc == - 1 {
71
- return timestamp , "" , fmt .Errorf ("can't find message prefix %q in line %q" , messagePrefix , line )
91
+ if cfg [timestampFormatKey ] == "" {
92
+ return fmt .Errorf ("unexpected empty timestamp format string" )
72
93
}
73
- message := strings .Trim (line [loc + 1 :], " " )
74
-
75
- return timestamp , message , nil
94
+ return nil
76
95
}
77
96
78
- // defaultKernelLogPath the default path of syslog kernel log.
79
- const defaultKernelLogPath = "/var/log/kern.log"
80
-
81
- // getLogReader returns log reader for syslog log. Note that getLogReader doesn't look back
82
- // to the rolled out logs.
83
- func getLogReader (path string ) (io.ReadCloser , error ) {
84
- if path == "" {
85
- path = defaultKernelLogPath
86
- }
87
- // To handle log rotation, tail will not report error immediately if
88
- // the file doesn't exist. So we check file existence frist.
89
- // This could go wrong during mid-rotation. It should recover after
90
- // several restart when the log file is created again. The chance
91
- // is slim but we should still fix this in the future.
92
- // TODO(random-liu): Handle log missing during rotation.
93
- _ , err := os .Stat (path )
94
- if err != nil {
95
- return nil , fmt .Errorf ("failed to stat the file %q: %v" , path , err )
96
- }
97
- tail , err := tail .NewTail (path )
98
- if err != nil {
99
- return nil , fmt .Errorf ("failed to tail the file %q: %v" , path , err )
97
+ // formalizeTimestamp formalizes the timestamp. We need this because some log doesn't contain full
98
+ // timestamp, e.g. syslog.
99
+ func formalizeTimestamp (t time.Time ) time.Time {
100
+ if t .Year () == 0 {
101
+ t = t .AddDate (time .Now ().Year (), 0 , 0 )
100
102
}
101
- return tail , nil
103
+ return t
102
104
}
0 commit comments