@@ -6,13 +6,16 @@ use warnings;
6
6
use Getopt::Long;
7
7
use Time::Local;
8
8
use Time::Piece;
9
+ use JSON::Tiny qw( encode_json decode_json) ;
10
+ $JSON::Tiny::TRUE = 1;
11
+ $JSON::Tiny::FALSE = 0;
9
12
10
13
use Helios::ObjectDriver;
11
14
use Helios::LogEntry::Levels ' :all' ;
12
15
use Helios::JobType;
13
16
use HeliosX::Logger::HiRes::LogEntry;
14
17
15
- our $VERSION = ' 0.10_2751 ' ;
18
+ our $VERSION = ' 0.10_3270 ' ;
16
19
17
20
our @LOG_PRIORITIES = qw( EMERG ALERT CRIT ERR WARNING NOTICE INFO DEBUG) ;
18
21
our %LOG_PRIORITY_MAP = (
@@ -28,14 +31,16 @@ our %LOG_PRIORITY_MAP = (
28
31
our $LIMIT_DEFAULT = 50;
29
32
30
33
our ($OPT_START_DATE , $OPT_END_DATE , $OPT_HOSTNAME , $OPT_PID , $OPT_JOBID ,
31
- $OPT_JOBTYPE , $OPT_SERVICE , $ OPT_PRIORITY , $OPT_MESSAGE , $OPT_LIMIT ,
32
- $OPT_SORT , $OPT_TAIL , $OPT_FOLLOW );
34
+ $OPT_JOBTYPE , $OPT_SERVICE , @ OPT_PRIORITY , $OPT_MESSAGE , $OPT_LIMIT ,
35
+ $OPT_SORT , $OPT_TAIL , $OPT_FOLLOW , $OPT_OUTPUT_FORMAT );
33
36
our ($OPT_HELP , $OPT_VERSION , $OPT_DEBUG );
34
37
35
- our $DEBUG_MODE = 0;
36
- our $FOLLOW_MODE = 0;
37
- our $TAIL_MODE = 0;
38
- our $LAST_LOGID = 0;
38
+ our $DEBUG_MODE = 0;
39
+ our $FOLLOW_MODE = 0;
40
+ our $TAIL_MODE = 0;
41
+ our $LAST_LOGID = 0;
42
+ our $OUTPUT_FORMAT = ' log' ;
43
+ our $OUTPUT_SEPARATOR = ' ' ;
39
44
40
45
GetOptions(
41
46
" start-date=s" => \$OPT_START_DATE ,
@@ -47,13 +52,15 @@ GetOptions(
47
52
" jobtype=s" => \$OPT_JOBTYPE ,
48
53
" service=s" => \$OPT_SERVICE ,
49
54
" message=s" => \$OPT_MESSAGE ,
50
- " priority=s" => \$ OPT_PRIORITY ,
55
+ " priority=s" => \@ OPT_PRIORITY ,
51
56
52
57
" n|lines|limit=i" => \$OPT_LIMIT ,
53
58
" sort=s" => \$OPT_SORT ,
54
59
" tail" => \$OPT_TAIL ,
55
60
" follow" => \$OPT_FOLLOW ,
56
61
62
+ " output-format=s" => \$OPT_OUTPUT_FORMAT ,
63
+
57
64
" help" => \$OPT_HELP ,
58
65
" version" => \$OPT_VERSION ,
59
66
" debug" => \$OPT_DEBUG ,
@@ -94,45 +101,89 @@ if ($OPT_PID) {
94
101
}
95
102
96
103
if ($OPT_JOBID ) {
97
- $search_opts {jobid } = $OPT_JOBID ;
104
+ if ($OPT_JOBID !~ / \D / ) {
105
+ $search_opts {jobid } = $OPT_JOBID ;
106
+ } else {
107
+ print STDERR " ERROR: Not a valid jobid: " ,$OPT_JOBID ," \n " ;
108
+ exit (1);
109
+ }
98
110
}
99
111
100
112
if ($OPT_HOSTNAME ) {
101
- $search_opts {host } = $OPT_HOSTNAME ;
113
+ if ( length ($OPT_HOSTNAME ) < 256 ) {
114
+ $search_opts {host } = $OPT_HOSTNAME ;
115
+ } else {
116
+ print STDERR " ERROR: Not a valid hostname: " , $OPT_HOSTNAME ," \n " ;
117
+ exit (1);
118
+ }
102
119
}
103
120
104
121
if ($OPT_SERVICE ) {
105
- $search_opts {service } = $OPT_SERVICE ;
122
+ if ($OPT_SERVICE =~ / ^[A-Za-z]([A-Za-z0-9_\- ]|:{2})*[A-Za-z0-9_\- ]$ / ) {
123
+ $search_opts {service } = $OPT_SERVICE ;
124
+ } else {
125
+ print STDERR " ERROR: Not a valid service name: " ,$OPT_SERVICE ," \n " ;
126
+ exit (1);
127
+ }
106
128
}
107
129
108
130
if ($OPT_JOBTYPE ) {
109
- my $jt = Helios::JobType-> lookup(name => $OPT_JOBTYPE );
110
- $search_opts {jobtypeid } = $jt -> getJobtypeid();
131
+ eval {
132
+ if (length ($OPT_JOBTYPE ) > 255) { die (" Jobtype too long." ); }
133
+ my $jt = Helios::JobType-> lookup(name => $OPT_JOBTYPE );
134
+ if (!defined ($jt )) { die (" Jobtype not found." ); }
135
+ $search_opts {jobtypeid } = $jt -> getJobtypeid();
136
+ 1;
137
+ } or do {
138
+ print STDERR " ERROR: Not a valid jobtype: " , $OPT_JOBTYPE ," \n " ;
139
+ exit (1);
140
+ };
111
141
}
112
142
113
- if ($OPT_PRIORITY ) {
114
- my $p = uc ($OPT_PRIORITY );
115
- if ( defined $LOG_PRIORITY_MAP {$p } ) {
116
- $search_opts {priority } = $LOG_PRIORITY_MAP {$p };
117
-
118
- }
143
+ if ( scalar @OPT_PRIORITY ) {
144
+ my @p = split (/ ,/ , join (' ,' => @OPT_PRIORITY ) );
145
+ if (scalar @p == 1) {
146
+ if ( defined $LOG_PRIORITY_MAP {$p [0]} ) {
147
+ $search_opts {priority } = $LOG_PRIORITY_MAP {$p [0]};
148
+ } else {
149
+ print STDERR " ERROR: Not a valid priority: " ,$p [0]," \n " ;
150
+ exit (1);
151
+ }
152
+ } elsif ( scalar @p > 1) {
153
+ my @p_opts ;
154
+ foreach my $p (@p ) {
155
+ $p = uc ($p );
156
+ if ( defined $LOG_PRIORITY_MAP {$p } ) {
157
+ push (@p_opts , $LOG_PRIORITY_MAP {$p });
158
+ } else {
159
+ print STDERR " ERROR: Not a valid priority: " ,$p ," \n " ;
160
+ exit (1);
161
+ }
162
+ }
163
+ $search_opts {priority } = \@p_opts ;
164
+ } else {
165
+ print STDERR " ERROR: Not a valid priority: " ,join (' ,' => @OPT_PRIORITY )," \n " ;
166
+ exit (1);
167
+ }
119
168
}
120
169
170
+ # set the default limit NOW, before we process the date and
171
+ # other options that can modify the limit
121
172
my $limit = $LIMIT_DEFAULT ;
122
173
123
174
if ($OPT_START_DATE || $OPT_END_DATE ) {
124
175
my $sd_epoch = ' ' ;
125
176
my $ed_epoch = ' ' ;
126
177
127
- if ($OPT_START_DATE ) {
178
+ if ($OPT_START_DATE && $OPT_START_DATE =~ / ^ \d {4}- \d {2}- \d {2}[T ] \d {2}: \d {2}: \d {2} $ / ) {
128
179
# convert Ts and Zs to spaces
129
180
my ($sd , $st ) = split (/ [ T]/ , $OPT_START_DATE );
130
181
my ($yyyy , $mm , $dd ) = split (' -' , $sd );
131
182
my ($hh24 , $mi , $ss ) = split (' :' , $st );
132
183
$sd_epoch = timelocal($ss , $mi , $hh24 , $dd , $mm - 1, $yyyy );
133
184
}
134
185
135
- if ($OPT_END_DATE ) {
186
+ if ($OPT_END_DATE && $OPT_END_DATE =~ / ^ \d {4}- \d {2}- \d {2}[T ] \d {2}: \d {2}: \d {2} $ / ) {
136
187
# convert Ts and Zs to spaces
137
188
my ($ed , $et ) = split (/ [ T]/ , $OPT_END_DATE );
138
189
my ($yyyy , $mm , $dd ) = split (' -' , $ed );
@@ -155,9 +206,15 @@ if ($OPT_START_DATE || $OPT_END_DATE) {
155
206
156
207
}
157
208
158
-
209
+ # handle -n/--limit/--lines
159
210
if ($OPT_LIMIT ) {
160
- $limit = $OPT_LIMIT ;
211
+ if ($OPT_LIMIT !~ / \D / ) {
212
+ $limit = $OPT_LIMIT ;
213
+ } else {
214
+ print STDERR " ERROR: Not a valid limit: " ,$OPT_LIMIT ," \n " ;
215
+ exit (1);
216
+ }
217
+
161
218
}
162
219
163
220
# Follow Mode
@@ -173,16 +230,43 @@ if ($FOLLOW_MODE) {
173
230
}
174
231
}
175
232
233
+ # output format switches
234
+ if ($OPT_OUTPUT_FORMAT ) {
235
+ my $fmt = uc ($OPT_OUTPUT_FORMAT );
236
+ SWITCH : {
237
+ if ($fmt eq ' PIPE' ) {
238
+ $OUTPUT_FORMAT = ' pipe' ;
239
+ $OUTPUT_SEPARATOR = ' |' ;
240
+ last ;
241
+ }
242
+ if ($fmt eq ' TAB' ) {
243
+ $OUTPUT_FORMAT = ' tab' ;
244
+ $OUTPUT_SEPARATOR = " \t " ;
245
+ last ;
246
+ }
247
+ if ($fmt eq ' JSON' ) {
248
+ $OUTPUT_FORMAT = ' json' ;
249
+ last ;
250
+ }
251
+ # default
252
+ print STDERR " ERROR: Not a valid output format: " ,$OPT_OUTPUT_FORMAT ," \n " ;
253
+ exit (1);
254
+ }
255
+ if ($FOLLOW_MODE ) {
256
+ print STDERR " ERROR: --follow will not work with --output-format=" ,$OPT_OUTPUT_FORMAT ," \n " ;
257
+ exit (1);
258
+ }
259
+
260
+ }
176
261
177
- # [] t
262
+ # []t
178
263
if ($DEBUG_MODE ) {
179
264
print " SEARCH OPTIONS:\n " ;
180
265
foreach my $opt (sort keys %search_opts ) {
181
- print $opt ,' => ' ,$search_opts {opt }," \n " ;
266
+ print $opt ,' => ' ,$search_opts {$ opt }," \n " ;
182
267
}
183
268
}
184
269
185
-
186
270
eval {
187
271
my $drvr = Helios::ObjectDriver-> getDriver();
188
272
my @logs = $drvr -> search(' HeliosX::Logger::HiRes::LogEntry' =>
@@ -194,19 +278,51 @@ eval {
194
278
@logs = reverse @logs ;
195
279
}
196
280
281
+ # we have results; get them to the user
197
282
foreach ( @logs ) {
283
+ # fix up some of the info to make it presentable
198
284
my $tp = localtime $_ -> log_time;
199
285
my ($sec , $fract ) = split (/ \. / , $_ -> log_time);
200
286
my $date = $tp -> ymd.' ' .$tp -> hms.' .' .$fract ;
201
287
my $jobinfo = $_ -> jobid ? ' [Jobid ' .$_ -> jobid.' ]' : ' ' ;
202
288
$LAST_LOGID = $_ -> logid;
203
- print $_ -> logid.' ' if $DEBUG_MODE ; # []t
204
- print ' [' ,$date ,' ] ' ,$_ -> host,' ' ,$_ -> service,' [' ,$_ -> pid,' ]: ' ,$LOG_PRIORITIES [$_ -> priority],$jobinfo ,' ' ,$_ -> message," \n " ;
205
- }
206
-
289
+
290
+ # output the log message in the format the user asked for
291
+ SWITCH: {
292
+
293
+ # output: JSON
294
+ if ($OUTPUT_FORMAT eq ' json' ) {
295
+
296
+ my $log_struct = {
297
+ logid => $_ -> logid,
298
+ logdate => $date ,
299
+ host => $_ -> host,
300
+ service => $_ -> service,
301
+ pid => $_ -> pid,
302
+ priority => $LOG_PRIORITIES [$_ -> priority],
303
+ jobid => $_ -> jobid,
304
+ message => $_ -> message,
305
+ };
306
+ print encode_json($log_struct )," \n " ;
307
+ last ;
308
+ }
309
+
310
+ # output: delimited values
311
+ if ($OUTPUT_FORMAT eq ' tab' || $OUTPUT_FORMAT eq ' pipe' ) {
312
+ print $_ -> logid.$OUTPUT_SEPARATOR if $DEBUG_MODE ;
313
+ print join ($OUTPUT_SEPARATOR => ($date , $_ -> host, $_ -> service, $_ -> pid, $LOG_PRIORITIES [$_ -> priority], $_ -> jobid ? $_ -> jobid : ' ' , $_ -> message)), " \n " ;
314
+ last ;
315
+ }
316
+
317
+ # default output: log
318
+ print $_ -> logid.' ' if $DEBUG_MODE ; # []t
319
+ print ' [' ,$date ,' ] ' ,$_ -> host,' ' ,$_ -> service,' [' ,$_ -> pid,' ]: ' ,$LOG_PRIORITIES [$_ -> priority],$jobinfo ,' ' ,$_ -> message," \n " ;
320
+
321
+ }
322
+ }
207
323
1;
208
324
} or do {
209
- print STDERR $@ ," \n " ;
325
+ print STDERR ' ERROR: ' , $@ ," \n " ;
210
326
exit (42);
211
327
};
212
328
@@ -230,10 +346,10 @@ if ($FOLLOW_MODE) {
230
346
}
231
347
1;
232
348
} or do {
233
- print STDERR $@ , " \n " ;
234
- exit (42 );
349
+ print STDERR ' ERROR: ' , $@ , " \n " ;
350
+ exit (1 );
235
351
};
236
- sleep 1 ;
352
+ sleep 5 ;
237
353
}
238
354
}
239
355
@@ -250,7 +366,7 @@ heliosx_logger_hires_search - search the Helios high resolution log
250
366
heliosx_logger_hires_search --jobid=12345
251
367
252
368
# heliosx_logger_hires_search normally displays only the first 50 messages
253
- # use the -n option to increase/decrease that limit
369
+ # use the -n or --lines option to increase/decrease that limit
254
370
heliosx_logger_hires_search --jobid=12345 -n 100
255
371
256
372
# display the last 10 MyService errors, sorted by most recent first
@@ -267,7 +383,7 @@ heliosx_logger_hires_search - search the Helios high resolution log
267
383
268
384
# display the last 100 log messages logged by MyService,
269
385
# then follow the log and display any new MyService messages
270
- heliosx_logger_hires_search --service=MyService -- t -n 100 -f
386
+ heliosx_logger_hires_search --service=MyService -t -n 100 -f
271
387
272
388
=head1 DESCRIPTION
273
389
@@ -354,7 +470,7 @@ include NOTICE messages logged by the service agent daemon; if you want to
354
470
limit the messages to those associated with a particular job, use --service
355
471
with the --jobtype switch.
356
472
357
- =head2 --priority=priority_name
473
+ =head2 --priority=priority_name[,priority_name][,priority_name]...
358
474
359
475
Display only log messages of a given priority. The priority names are:
360
476
@@ -378,6 +494,9 @@ Display only log messages of a given priority. The priority names are:
378
494
379
495
=back
380
496
497
+ To search for log messages of multiple, specific priorities, separate them on
498
+ the command line by spaces or specify multiple --priority options.
499
+
381
500
=head1 AUTHOR
382
501
383
502
Andrew Johnson, E<lt> lajandy at cpan dot orgE<gt>
0 commit comments