11 #define HASH_TABLE_SIZE 128
13 static time_t first_time
, last_time
; /* first and last timestamp to show */
14 static time_t ltime
; /* the timestamp from the current log-line */
16 #define EVT_PROCESS (1 << 0)
17 #define EVT_NOTIFY (1 << 1)
18 #define EVT_ALERT (1 << 2)
19 #define EVT_COMMAND (1 << 3)
20 #define EVT_STATE (1 << 4)
21 #define EVT_FLAPPING (1 << 5)
22 #define EVT_DOWNTIME (1 << 6)
23 #define EVT_HOST (1 << 20)
24 #define EVT_SERVICE (1 << 21)
25 #define EVENT_MASK (EVT_PROCESS | EVT_NOTIFY | EVT_ALERT | EVT_COMMAND | \
26 EVT_STATE | EVT_FLAPPING | EVT_DOWNTIME)
28 static int event_filter
= EVT_ALERT
| EVT_STATE
| EVT_FLAPPING
| EVT_DOWNTIME
| EVT_PROCESS
;
29 static int host_state_filter
= -1;
30 static int service_state_filter
= -1;
31 static int statetype_filter
= (1 << HARD_STATE
) | (1 << SOFT_STATE
);
33 #define add_event(string, eventcode) add_code(0, string, eventcode)
34 static struct string_code event_codes
[] = {
35 add_event("Error", EVT_PROCESS
),
36 add_event("Warning", EVT_PROCESS
),
37 add_event("HOST NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
38 add_event("HOST FLAPPING ALERT", EVT_FLAPPING
| EVT_HOST
),
39 add_event("SERVICE NOTIFICATION", EVT_NOTIFY
| EVT_SERVICE
),
40 add_event("SERVICE FLAPPING ALERT", EVT_FLAPPING
| EVT_SERVICE
),
41 add_ignored("LOG ROTATION"),
42 add_ignored("SERVICE EVENT HANDLER"),
43 add_ignored("HOST EVENT HANDLER"),
44 add_ignored("LOG VERSION"),
45 add_ignored("EXTERNAL COMMAND"),
47 add_code(5, "HOST ALERT", EVT_ALERT
| EVT_HOST
),
48 add_code(5, "INITIAL HOST STATE", EVT_STATE
| EVT_HOST
),
49 add_code(5, "CURRENT HOST STATE", EVT_STATE
| EVT_HOST
),
50 add_code(6, "SERVICE ALERT", EVT_ALERT
| EVT_SERVICE
),
51 add_code(6, "INITIAL SERVICE STATE", EVT_STATE
| EVT_SERVICE
),
52 add_code(6, "CURRENT SERVICE STATE", EVT_STATE
| EVT_SERVICE
),
53 add_code(3, "HOST DOWNTIME ALERT", EVT_DOWNTIME
| EVT_HOST
),
54 add_code(4, "SERVICE DOWNTIME ALERT", EVT_DOWNTIME
| EVT_SERVICE
),
58 static void print_time_iso8601(struct tm
*t
)
60 printf("[%d-%02d-%02d %02d:%02d:%02d] ",
61 t
->tm_year
+ 1900, t
->tm_mon
+ 1, t
->tm_mday
,
62 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
65 static void print_time_raw(struct tm
*t
)
67 printf("[%lu] ", ltime
);
72 void (*func
)(struct tm
*);
73 } time_format_selections
[] = {
74 { "iso8601", print_time_iso8601
},
75 { "raw", print_time_raw
},
78 static void (*print_time
)(struct tm
*) = print_time_iso8601
;
80 static void parse_time_format(const char *selection
)
84 if (selection
) for (i
= 0; time_format_selections
[i
].name
; i
++) {
85 if (strcasecmp(selection
, time_format_selections
[i
].name
))
87 print_time
= time_format_selections
[i
].func
;
91 crash("Illegal timeformat selection: '%s'\n", selection
);
94 static void print_line(char *line
, uint len
)
99 for (i
= 0; i
< len
; i
++) {
112 static int parse_line(char *orig_line
, uint len
)
114 char *ptr
, *colon
, *line
;
116 struct string_code
*sc
;
120 /* ignore empty lines */
124 /* skip obviously bogus lines */
125 if (len
< 12 || *orig_line
!= '[') {
126 warn("line %d; len too short, or line doesn't start with '[' (%s)",
131 ltime
= strtoul(orig_line
+ 1, &ptr
, 10);
132 if (orig_line
+ 1 == ptr
) {
133 crash("Failed to parse log timestamp from '%s'. I can't handle malformed logdata",
138 /* only print lines in the interesting interval */
139 if ((first_time
&& ltime
< first_time
) || (last_time
&& ltime
> last_time
))
142 while (*ptr
== ']' || *ptr
== ' ')
146 len
-= line
- orig_line
;
148 if (!is_interesting(ptr
))
151 if (!(colon
= strchr(ptr
, ':'))) {
152 /* stupid heuristic, but might be good for something,
153 * somewhere, sometime. if nothing else, it should suppress
155 if (is_start_event(ptr
) || is_stop_event(ptr
)) {
156 if (event_filter
& EVT_PROCESS
)
157 print_line(line
, len
);
161 handle_unknown_event(line
);
165 if (!(sc
= get_event_type(ptr
, colon
- ptr
))) {
166 handle_unknown_event(line
);
170 if (sc
->code
== IGNORE_LINE
)
172 if ((sc
->code
& event_filter
) != sc
->code
)
182 nvecs
= vectorize_string(ptr
, sc
->nvecs
);
184 if (nvecs
!= sc
->nvecs
) {
186 warn("Line %d in %s seems to not have all the fields it should",
187 line_no
, cur_file
->path
);
191 for (i
= 0; i
< sc
->nvecs
; i
++) {
193 /* this should never happen */
194 warn("Line %d in %s seems to be broken, or we failed to parse it into a vector",
195 line_no
, cur_file
->path
);
202 case EVT_ALERT
| EVT_HOST
:
203 case EVT_STATE
| EVT_HOST
:
204 if (!(statetype_filter
& (1 << soft_hard(strv
[2]))))
206 if (!(host_state_filter
& (1 << parse_host_state(strv
[1]))))
208 if (!is_interesting_host(strv
[0]))
210 print_line(line
, len
);
213 case EVT_ALERT
| EVT_SERVICE
:
214 case EVT_STATE
| EVT_SERVICE
:
215 if (!(statetype_filter
& (1 << soft_hard(strv
[3]))))
217 if (!(service_state_filter
& (1 << parse_service_state(strv
[2]))))
219 if (!is_interesting_service(strv
[0], strv
[1]))
222 print_line(line
, len
);
225 case EVT_FLAPPING
| EVT_HOST
:
226 case EVT_DOWNTIME
| EVT_HOST
:
227 if (!is_interesting_host(strv
[0]))
229 print_line(line
, len
);
232 case EVT_FLAPPING
| EVT_SERVICE
:
233 case EVT_DOWNTIME
| EVT_SERVICE
:
234 if (!is_interesting_service(strv
[0], strv
[1]))
236 print_line(line
, len
);
247 * hashes one line from an "interesting"-file. We use (void *)1
248 * to mark this as "present in hash-table" as we have no real
249 * data to lookup but still want hash_find{,2} to return non-NULL
250 * when it finds a match
252 static int hash_one_line(char *line
, uint len
)
254 return add_interesting_object(line
);
257 static int hash_interesting(const char *path
)
261 if (stat(path
, &st
) < 0)
262 crash("failed to stat %s: %s", path
, strerror(errno
));
264 lparse_path(path
, st
.st_size
, hash_one_line
);
269 static void parse_host_state_filter(char *p
)
271 host_state_filter
= 0;
275 host_state_filter
= -1;
278 host_state_filter
|= 1 << HOST_UNREACHABLE
;
281 host_state_filter
|= 1 << HOST_DOWN
;
284 host_state_filter
|= 1 << HOST_UP
;
290 static void parse_service_state_filter(char *p
)
292 service_state_filter
= 0;
296 service_state_filter
= -1;
299 service_state_filter
|= 1 << SERVICE_OK
;
302 service_state_filter
|= 1 << SERVICE_WARNING
;
305 service_state_filter
|= 1 << SERVICE_CRITICAL
;
308 service_state_filter
|= 1 << SERVICE_UNKNOWN
;
313 extern const char *__progname
;
314 int main(int argc
, char **argv
)
317 struct naglog_file
*nfile
;
319 strv
= calloc(sizeof(char *), MAX_NVECS
);
320 nfile
= calloc(sizeof(*nfile
), argc
- 1);
322 crash("Failed to alloc initial structs");
324 for (num_nfile
= 0,i
= 1; i
< argc
; i
++) {
325 char *opt
, *arg
= argv
[i
];
326 struct naglog_file
*nf
;
329 if ((opt
= strchr(arg
, '='))) {
333 else if (i
< argc
- 1) {
337 if (!strcmp(arg
, "--reverse")) {
341 if (!strcmp(arg
, "--debug") || !strcmp(arg
, "-d")) {
345 if (!prefixcmp(arg
, "--hide-flapping")) {
346 event_filter
&= ~EVT_FLAPPING
;
349 if (!prefixcmp(arg
, "--hide-downtime")) {
350 event_filter
&= ~EVT_DOWNTIME
;
352 if (!prefixcmp(arg
, "--hide-process")) {
353 event_filter
&= ~EVT_PROCESS
;
357 if (!prefixcmp(arg
, "--")) {
359 crash("Option '%s' requires an argument\n", arg
);
364 /* options parsed below require arguments */
365 if (!strcmp(arg
, "--host")) {
366 event_filter
|= EVT_HOST
;
367 add_interesting_object(opt
);
370 if (!strcmp(arg
, "--service")) {
371 event_filter
|= EVT_SERVICE
;
372 add_interesting_object(opt
);
375 if (!strcmp(arg
, "--interesting") || !strcmp(arg
, "-i")) {
377 crash("%s requires a filename as argument", arg
);
378 hash_interesting(opt
);
381 if (!strcmp(arg
, "--first") || !strcmp(arg
, "--last")) {
385 crash("%s requires a timestamp as argument", arg
);
386 when
= strtoul(opt
, NULL
, 0);
389 if (!strcmp(arg
, "--first"))
395 if (!strcmp(arg
, "--state-type")) {
396 if (!strcasecmp(opt
, "hard"))
397 statetype_filter
= (1 << HARD_STATE
);
398 if (!strcasecmp(opt
, "soft"))
399 statetype_filter
= (1 << SOFT_STATE
);
402 if (!strcmp(arg
, "--host-states")) {
403 event_filter
|= EVT_HOST
;
404 parse_host_state_filter(opt
);
407 if (!strcmp(arg
, "--service-states")) {
408 event_filter
|= EVT_SERVICE
;
409 parse_service_state_filter(opt
);
412 if (!strcmp(arg
, "--time-format")) {
413 parse_time_format(opt
);
417 /* non-argument, so treat as file */
418 nf
= &nfile
[num_nfile
++];
424 print_interesting_objects();
427 crash("Usage: %s [--interesting <file>] logfiles\n",
431 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_rev_cmp
);
433 qsort(nfile
, num_nfile
, sizeof(*nfile
), nfile_cmp
);
435 for (i
= 0; i
< num_nfile
; i
++) {
436 struct naglog_file
*nf
= &nfile
[i
];
437 if (last_time
&& nf
->first
> last_time
) {
438 debug("ignoring %s\n", nf
->path
);
441 if (first_time
&& i
< num_nfile
- 1 && nfile
[i
+ 1].first
< first_time
) {
442 debug("ignoring %s\n", nf
->path
);
447 debug("importing from %s (%lu : %u)\n", nf
->path
, nf
->first
, nf
->cmp
);
449 lparse_path_real(reverse
, nf
->path
, nf
->size
, parse_line
);
452 if (warnings
&& debug_level
)
453 fprintf(stderr
, "Total warnings: %d\n", warnings
);
455 print_unhandled_events();