line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ABSTRACT: Simple syslog line parser |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Parse::Syslog::Line; |
4
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
822115
|
use warnings; |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
136
|
|
6
|
4
|
|
|
4
|
|
25
|
use strict; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
87
|
|
7
|
|
|
|
|
|
|
|
8
|
4
|
|
|
4
|
|
24
|
use Carp; |
|
4
|
|
|
|
|
16
|
|
|
4
|
|
|
|
|
228
|
|
9
|
4
|
|
|
4
|
|
1815
|
use Const::Fast; |
|
4
|
|
|
|
|
8300
|
|
|
4
|
|
|
|
|
26
|
|
10
|
4
|
|
|
4
|
|
2189
|
use English qw(-no_match_vars); |
|
4
|
|
|
|
|
13333
|
|
|
4
|
|
|
|
|
23
|
|
11
|
4
|
|
|
4
|
|
1204
|
use Exporter; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
149
|
|
12
|
4
|
|
|
4
|
|
1745
|
use HTTP::Date qw( str2time ); |
|
4
|
|
|
|
|
14514
|
|
|
4
|
|
|
|
|
273
|
|
13
|
4
|
|
|
4
|
|
2047
|
use Module::Load qw( load ); |
|
4
|
|
|
|
|
4257
|
|
|
4
|
|
|
|
|
90
|
|
14
|
4
|
|
|
4
|
|
1933
|
use Module::Loaded qw( is_loaded ); |
|
4
|
|
|
|
|
2090
|
|
|
4
|
|
|
|
|
194
|
|
15
|
4
|
|
|
4
|
|
1178
|
use POSIX qw( strftime tzset ); |
|
4
|
|
|
|
|
15484
|
|
|
4
|
|
|
|
|
32
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = '4.0'; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Default for Handling Parsing |
20
|
|
|
|
|
|
|
our $DateParsing = 1; |
21
|
|
|
|
|
|
|
our $DateTimeCreate = 0; |
22
|
|
|
|
|
|
|
our $EpochCreate = 1; |
23
|
|
|
|
|
|
|
our $NormalizeToUTC = 0; |
24
|
|
|
|
|
|
|
our $OutputTimeZone = 0; |
25
|
|
|
|
|
|
|
our $IgnoreTimeZones = 0; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $ExtractProgram = 1; |
28
|
|
|
|
|
|
|
our $PruneRaw = 0; |
29
|
|
|
|
|
|
|
our $PruneEmpty = 0; |
30
|
|
|
|
|
|
|
our @PruneFields = (); |
31
|
|
|
|
|
|
|
our $FmtDate; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my %INT_PRIORITY = ( |
35
|
|
|
|
|
|
|
'emerg' => 0, |
36
|
|
|
|
|
|
|
'alert' => 1, |
37
|
|
|
|
|
|
|
'crit' => 2, |
38
|
|
|
|
|
|
|
'err' => 3, |
39
|
|
|
|
|
|
|
'warn' => 4, |
40
|
|
|
|
|
|
|
'notice' => 5, |
41
|
|
|
|
|
|
|
'info' => 6, |
42
|
|
|
|
|
|
|
'debug' => 7, |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my %INT_FACILITY = ( |
46
|
|
|
|
|
|
|
# |
47
|
|
|
|
|
|
|
# POSIX Facilities |
48
|
|
|
|
|
|
|
'kern' => 0 << 3, |
49
|
|
|
|
|
|
|
'user' => 1 << 3, |
50
|
|
|
|
|
|
|
'mail' => 2 << 3, |
51
|
|
|
|
|
|
|
'daemon' => 3 << 3, |
52
|
|
|
|
|
|
|
'auth' => 4 << 3, |
53
|
|
|
|
|
|
|
'syslog' => 5 << 3, |
54
|
|
|
|
|
|
|
'lpr' => 6 << 3, |
55
|
|
|
|
|
|
|
'news' => 7 << 3, |
56
|
|
|
|
|
|
|
'uucp' => 8 << 3, |
57
|
|
|
|
|
|
|
'cron' => 9 << 3, |
58
|
|
|
|
|
|
|
'authpriv' => 10 << 3, |
59
|
|
|
|
|
|
|
'ftp' => 11 << 3, |
60
|
|
|
|
|
|
|
# |
61
|
|
|
|
|
|
|
# Local Reserved |
62
|
|
|
|
|
|
|
'local0' => 16 << 3, |
63
|
|
|
|
|
|
|
'local1' => 17 << 3, |
64
|
|
|
|
|
|
|
'local2' => 18 << 3, |
65
|
|
|
|
|
|
|
'local3' => 19 << 3, |
66
|
|
|
|
|
|
|
'local4' => 20 << 3, |
67
|
|
|
|
|
|
|
'local5' => 21 << 3, |
68
|
|
|
|
|
|
|
'local6' => 22 << 3, |
69
|
|
|
|
|
|
|
'local7' => 23 << 3, |
70
|
|
|
|
|
|
|
# |
71
|
|
|
|
|
|
|
# Apple Additions |
72
|
|
|
|
|
|
|
'netinfo' => 12 << 3, |
73
|
|
|
|
|
|
|
'remoteauth' => 13 << 3, |
74
|
|
|
|
|
|
|
'install' => 14 << 3, |
75
|
|
|
|
|
|
|
'ras' => 15 << 3, |
76
|
|
|
|
|
|
|
'launchd' => 24 << 3, |
77
|
|
|
|
|
|
|
); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
const our %LOG_PRIORITY => ( |
80
|
|
|
|
|
|
|
%INT_PRIORITY, |
81
|
|
|
|
|
|
|
reverse(%INT_PRIORITY), |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
const our %LOG_FACILITY => ( |
85
|
|
|
|
|
|
|
%INT_FACILITY, |
86
|
|
|
|
|
|
|
reverse(%INT_FACILITY), |
87
|
|
|
|
|
|
|
); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
const our %CONV_MASK => ( |
90
|
|
|
|
|
|
|
priority => 0x07, |
91
|
|
|
|
|
|
|
facility => 0x03f8, |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
96
|
|
|
|
|
|
|
our @EXPORT = qw(parse_syslog_line); |
97
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
98
|
|
|
|
|
|
|
parse_syslog_line |
99
|
|
|
|
|
|
|
preamble_priority preamble_facility |
100
|
|
|
|
|
|
|
%LOG_FACILITY %LOG_PRIORITY |
101
|
|
|
|
|
|
|
get_syslog_timezone set_syslog_timezone |
102
|
|
|
|
|
|
|
use_utc_syslog |
103
|
|
|
|
|
|
|
); |
104
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
105
|
|
|
|
|
|
|
constants => [ qw( %LOG_FACILITY %LOG_PRIORITY ) ], |
106
|
|
|
|
|
|
|
preamble => [ qw(preamble_priority preamble_facility) ], |
107
|
|
|
|
|
|
|
with_timezones => [ qw(parse_syslog_line set_syslog_timezone get_syslog_timezone use_utc_syslog) ], |
108
|
|
|
|
|
|
|
); |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Regex to Extract Data |
111
|
|
|
|
|
|
|
const my %RE => ( |
112
|
|
|
|
|
|
|
IPv4 => qr/(?>(?:[0-9]{1,3}\.){3}[0-9]{1,3})/, |
113
|
|
|
|
|
|
|
preamble => qr/(?>^\<(\d+)\>)/, |
114
|
|
|
|
|
|
|
year => qr/(?>^(\d{4}) )/, |
115
|
|
|
|
|
|
|
date => qr/(?>^([A-Za-z]{3}\s+[0-9]+\s+[0-9]{1,2}(?:\:[0-9]{2}){1,2}))/, |
116
|
|
|
|
|
|
|
date_long => qr/^(?> |
117
|
|
|
|
|
|
|
(?:[0-9]{4}\s+)? # Year: Because, Cisco |
118
|
|
|
|
|
|
|
([.*])? # Cisco adds a * for no ntp, and a . for configured but out of sync |
119
|
|
|
|
|
|
|
[a-zA-Z]{3}\s+[0-9]+ # Date: Jan 1 |
120
|
|
|
|
|
|
|
(?:\s+[0-9]{4})? # Year: Because, Cisco |
121
|
|
|
|
|
|
|
\s+ # Date Separator: spaces |
122
|
|
|
|
|
|
|
[0-9]{1,2}(?:\:[0-9]{2}){1,2} # Time: HH:MM or HH:MM:SS |
123
|
|
|
|
|
|
|
(?:\.[0-9]{3,6})? # Time: .DDD(DDD) ms resolution |
124
|
|
|
|
|
|
|
(?:\s+[A-Z]{3,4})? # Timezone, ZZZ or ZZZZ |
125
|
|
|
|
|
|
|
(?:\:?) # Cisco adds a : after the second timestamp |
126
|
|
|
|
|
|
|
)/x, |
127
|
|
|
|
|
|
|
date_iso8601 => qr/(?>^( |
128
|
|
|
|
|
|
|
[0-9]{4}(?:\-[0-9]{2}){2} # Date YYYY-MM-DD |
129
|
|
|
|
|
|
|
(?:\s|T) # Date Separator T or ' ' |
130
|
|
|
|
|
|
|
[0-9]{2}(?:\:[0-9]{2}){1,2} # Time HH:MM:SS |
131
|
|
|
|
|
|
|
(?:\.(?:[0-9]{3}){1,2})? # Time: .DDD millisecond or .DDDDDD microsecond resolution |
132
|
|
|
|
|
|
|
(?:[Zz]|[+\-][0-9]{2}\:[0-9]{2}) # UTC Offset +DD:MM or 'Z' indicating UTC-0 |
133
|
|
|
|
|
|
|
))/x, |
134
|
|
|
|
|
|
|
host => qr/^\s*([^:\s]+)\s+/, |
135
|
|
|
|
|
|
|
cisco_hates_you => qr/^\s*[0-9]*:\s+/, |
136
|
|
|
|
|
|
|
program_raw => qr/^\s*([^\[][^:]+):\s*/, |
137
|
|
|
|
|
|
|
program_name => qr/^([^\[\(\ ]+)/, |
138
|
|
|
|
|
|
|
program_sub => qr/(?>\(([^\)]+)\))/, |
139
|
|
|
|
|
|
|
program_pid => qr/(?>\[([^\]]+)\])/, |
140
|
|
|
|
|
|
|
program_netapp => qr/(?>\[([^\]]+)\]:\s*)/, |
141
|
|
|
|
|
|
|
); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
my %_empty_msg = map { $_ => undef } qw( |
145
|
|
|
|
|
|
|
preamble priority priority_int facility facility_int |
146
|
|
|
|
|
|
|
datetime_raw date_raw date time datetime_str datetime_obj epoch |
147
|
|
|
|
|
|
|
host_raw host domain |
148
|
|
|
|
|
|
|
program_raw program_name program_pid program_sub |
149
|
|
|
|
|
|
|
); |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
my $SYSLOG_TIMEZONE = ''; |
153
|
|
|
|
|
|
|
my $DateTimeTried = 0; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
sub parse_syslog_line { |
156
|
83
|
|
|
83
|
1
|
101242
|
my ($raw_string) = @_; |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Initialize everything to undef |
159
|
83
|
100
|
|
|
|
802
|
my %msg = $PruneEmpty ? () : %_empty_msg; |
160
|
83
|
100
|
|
|
|
332
|
$msg{message_raw} = $raw_string unless $PruneRaw; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# |
163
|
|
|
|
|
|
|
# grab the preamble: |
164
|
83
|
100
|
|
|
|
557
|
if( $raw_string =~ s/$RE{preamble}//o ) { |
165
|
|
|
|
|
|
|
# Cast to integer |
166
|
54
|
|
|
|
|
200
|
$msg{preamble} = int $1; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# Extract Integers |
169
|
54
|
|
|
|
|
130
|
$msg{priority_int} = $msg{preamble} & $CONV_MASK{priority}; |
170
|
54
|
|
|
|
|
99
|
$msg{facility_int} = $msg{preamble} & $CONV_MASK{facility}; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Lookups |
173
|
54
|
|
|
|
|
163
|
$msg{priority} = $LOG_PRIORITY{ $msg{priority_int} }; |
174
|
54
|
|
|
|
|
142
|
$msg{facility} = $LOG_FACILITY{ $msg{facility_int} }; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# |
178
|
|
|
|
|
|
|
# Handle Date/Time |
179
|
83
|
|
|
|
|
154
|
my $year; |
180
|
83
|
100
|
|
|
|
456
|
if( $raw_string =~ s/$RE{year}//o ) { |
181
|
3
|
|
|
|
|
14
|
$year = $1; |
182
|
|
|
|
|
|
|
} |
183
|
83
|
100
|
|
|
|
666
|
if( $raw_string =~ s/$RE{date}//o) { |
|
|
50
|
|
|
|
|
|
184
|
65
|
|
|
|
|
193
|
$msg{datetime_raw} = $1; |
185
|
65
|
100
|
|
|
|
168
|
$msg{datetime_raw} .= " $year" |
186
|
|
|
|
|
|
|
if $year; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
elsif( $raw_string =~ s/$RE{date_iso8601}//o) { |
189
|
18
|
|
|
|
|
65
|
$msg{datetime_raw} = $1; |
190
|
|
|
|
|
|
|
} |
191
|
83
|
50
|
33
|
|
|
447
|
if( exists $msg{datetime_raw} && length $msg{datetime_raw} ) { |
192
|
83
|
|
|
|
|
165
|
$msg{date_raw} = $msg{datetime_raw}; |
193
|
|
|
|
|
|
|
|
194
|
83
|
50
|
|
|
|
201
|
if ( $DateParsing ) { |
195
|
|
|
|
|
|
|
# if User wants to fight with dates himself, let him :) |
196
|
83
|
100
|
66
|
|
|
310
|
if( $FmtDate && ref $FmtDate eq 'CODE' ) { |
197
|
20
|
|
|
|
|
64
|
@msg{qw(date time epoch datetime_str)} = $FmtDate->($msg{datetime_raw}); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
else { |
200
|
|
|
|
|
|
|
# Parse the Epoch |
201
|
63
|
|
|
|
|
222
|
$msg{epoch} = HTTP::Date::str2time($msg{datetime_raw}); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# Format accordingly, keep highest resolution we can |
204
|
63
|
|
|
|
|
8926
|
my $diff = ($msg{epoch} - int($msg{epoch})); |
205
|
63
|
100
|
|
|
|
263
|
my $hires = $diff > 0 ? substr(sprintf('%0.6f', $diff),1) : ''; |
206
|
63
|
100
|
|
|
|
241
|
my $tm_fmt = '%FT%T' . $hires . ( $OutputTimeZone ? ($SYSLOG_TIMEZONE eq 'UTC' ? 'Z' : '%z') : '' ); |
|
|
100
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Set the Date Strings |
209
|
|
|
|
|
|
|
$msg{datetime_str} = $NormalizeToUTC ? strftime($tm_fmt, gmtime $msg{epoch}) |
210
|
63
|
100
|
|
|
|
655
|
: strftime($tm_fmt, localtime $msg{epoch}); |
211
|
|
|
|
|
|
|
# Split this up into parts |
212
|
63
|
|
|
|
|
1276
|
my @parts = split /[ T]/, $msg{datetime_str}; |
213
|
63
|
|
|
|
|
150
|
$msg{date} = $parts[0]; |
214
|
63
|
|
|
|
|
204
|
$msg{time} = (split /[+\-Z]/, $parts[1])[0]; |
215
|
63
|
100
|
|
|
|
349
|
$msg{offset} = $NormalizeToUTC ? 'Z' |
|
|
100
|
|
|
|
|
|
216
|
|
|
|
|
|
|
: $parts[1] =~ /([Z+\-].*)/ ? $1 |
217
|
|
|
|
|
|
|
: $SYSLOG_TIMEZONE; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# Debugging for my sanity |
220
|
|
|
|
|
|
|
printf("TZ=%s Parsed: %s to [%s] %s D:%s T:%s O:%s\n", |
221
|
|
|
|
|
|
|
$SYSLOG_TIMEZONE, |
222
|
|
|
|
|
|
|
@msg{qw(datetime_raw epoch datetime_str date time offset)}, |
223
|
63
|
50
|
|
|
|
244
|
) if $ENV{DEBUG_PARSE_SYSLOG_LINE}; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
# Use Module::Load to runtime load the DateTime libraries *if* necessary |
226
|
83
|
100
|
|
|
|
403
|
if( $DateTimeCreate ) { |
227
|
6
|
100
|
66
|
|
|
244
|
unless( $DateTimeTried && is_loaded('DateTime') ) { |
228
|
1
|
|
|
|
|
36
|
warn "DateTime seriously degrades performance, please start using 'epoch' and/or 'datetime_str' instead."; |
229
|
|
|
|
|
|
|
eval { |
230
|
1
|
|
|
|
|
11
|
load DateTime; |
231
|
1
|
|
|
|
|
115
|
1; |
232
|
1
|
50
|
|
|
|
6
|
} or do { |
233
|
0
|
|
|
|
|
0
|
my $err = $@; |
234
|
0
|
|
|
|
|
0
|
warn "DateTime unavailable, disabling it: $err"; |
235
|
0
|
|
|
|
|
0
|
$DateTimeCreate = 0; |
236
|
|
|
|
|
|
|
}; |
237
|
1
|
|
|
|
|
3
|
$DateTimeTried++; |
238
|
|
|
|
|
|
|
} |
239
|
6
|
50
|
|
|
|
103
|
eval { |
240
|
6
|
|
|
|
|
165
|
my %args = (epoch => $msg{epoch}); |
241
|
6
|
50
|
|
|
|
21
|
$args{time_zone} = $SYSLOG_TIMEZONE if $SYSLOG_TIMEZONE; |
242
|
6
|
|
|
|
|
39
|
$msg{datetime_obj} = DateTime->from_epoch(%args); |
243
|
|
|
|
|
|
|
} if $DateTimeCreate; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# |
249
|
|
|
|
|
|
|
# Host Information: |
250
|
83
|
100
|
|
|
|
16786
|
if( $raw_string =~ s/$RE{host}//o ) { |
251
|
80
|
|
|
|
|
211
|
my $hostStr = $1; |
252
|
80
|
|
|
|
|
394
|
my($ip) = ($hostStr =~ /($RE{IPv4})/o); |
253
|
80
|
100
|
66
|
|
|
392
|
if( defined $ip && length $ip ) { |
|
|
50
|
|
|
|
|
|
254
|
21
|
|
|
|
|
55
|
$msg{host_raw} = $hostStr; |
255
|
21
|
|
|
|
|
58
|
$msg{host} = $ip; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
elsif( length $hostStr ) { |
258
|
59
|
|
|
|
|
186
|
my ($host,$domain) = split /\./, $hostStr, 2; |
259
|
59
|
|
|
|
|
138
|
$msg{host_raw} = $hostStr; |
260
|
59
|
|
|
|
|
101
|
$msg{host} = $host; |
261
|
59
|
|
|
|
|
130
|
$msg{domain} = $domain; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
83
|
100
|
|
|
|
372
|
if( $raw_string =~ s/$RE{cisco_hates_you}//o ) { |
265
|
|
|
|
|
|
|
# Yes, Cisco adds a second timestamp to it's messages, because it hates you. |
266
|
23
|
100
|
|
|
|
170
|
if( $raw_string =~ s/$RE{date_long}//o ) { |
267
|
|
|
|
|
|
|
# Cisco encodes the status of NTP in the second datestamp, so let's pass it back |
268
|
17
|
100
|
|
|
|
54
|
if ( my $ntp = $1 ) { |
269
|
6
|
50
|
|
|
|
29
|
$msg{ntp} = $ntp eq '.' ? 'out of sync' |
|
|
100
|
|
|
|
|
|
270
|
|
|
|
|
|
|
: $ntp eq '*' ? 'not configured' |
271
|
|
|
|
|
|
|
: 'unknown'; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
else { |
274
|
11
|
|
|
|
|
29
|
$msg{ntp} = 'ok'; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# |
280
|
|
|
|
|
|
|
# Parse the Program portion |
281
|
83
|
100
|
|
|
|
185
|
if( $ExtractProgram ) { |
282
|
63
|
100
|
|
|
|
310
|
if( $raw_string =~ s/$RE{program_raw}//o ) { |
|
|
50
|
|
|
|
|
|
283
|
57
|
|
|
|
|
159
|
$msg{program_raw} = $1; |
284
|
57
|
|
|
|
|
194
|
my $progStr = join ' ', grep {!exists $INT_PRIORITY{$_}} split /\s+/, $msg{program_raw}; |
|
68
|
|
|
|
|
262
|
|
285
|
57
|
50
|
33
|
|
|
288
|
if( defined $progStr && length $progStr) { |
286
|
57
|
50
|
|
|
|
262
|
if( ($msg{program_name}) = ($progStr =~ /$RE{program_name}/o) ) { |
287
|
57
|
100
|
|
|
|
173
|
if (length $msg{program_name} != length $msg{program_raw} ) { |
288
|
|
|
|
|
|
|
(($msg{program_pid}) = ($progStr =~ /$RE{program_pid}/o)) |
289
|
14
|
100
|
|
|
|
92
|
|| (($msg{program_sub}) = ($progStr =~ /$RE{program_sub}/o)) |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
elsif( $raw_string =~ s/$RE{program_netapp}//o ) { |
295
|
|
|
|
|
|
|
# Check for a [host thing.subthing:level]: tag |
296
|
|
|
|
|
|
|
# or [host:thing.subthing:level]: tag, Thanks NetApp. |
297
|
6
|
|
|
|
|
16
|
my $subStr = $1; |
298
|
6
|
|
|
|
|
15
|
$msg{program_raw} = qq{[$subStr]}; |
299
|
6
|
|
|
|
|
26
|
my ($host,$program,$level) = split /[: ]+/, $subStr; |
300
|
6
|
|
|
|
|
15
|
$msg{program_name} = $program; |
301
|
6
|
0
|
33
|
|
|
17
|
if(!exists $msg{priority} && exists $LOG_PRIORITY{$level}) { |
302
|
0
|
|
|
|
|
0
|
$msg{priority} = $level; |
303
|
0
|
|
|
|
|
0
|
$msg{priority_int} = $LOG_PRIORITY{$level}; |
304
|
|
|
|
|
|
|
} |
305
|
6
|
|
|
|
|
15
|
$raw_string =~ s/^[ :]+//; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
else { |
309
|
20
|
|
|
|
|
58
|
$raw_string =~ s/^\s+//; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# The left overs should be the message |
313
|
83
|
|
|
|
|
176
|
$msg{content} = $raw_string; |
314
|
83
|
|
|
|
|
176
|
chomp $msg{content}; |
315
|
83
|
100
|
|
|
|
269
|
$msg{message} = defined $msg{program_raw} ? "$msg{program_raw}: $msg{content}" : $msg{content}; |
316
|
|
|
|
|
|
|
|
317
|
83
|
100
|
|
|
|
193
|
if( $PruneRaw ) { |
318
|
9
|
|
|
|
|
36
|
delete $msg{$_} for grep { $_ =~ /_raw$/ } keys %msg; |
|
158
|
|
|
|
|
321
|
|
319
|
|
|
|
|
|
|
} |
320
|
83
|
100
|
|
|
|
200
|
if( $PruneEmpty ) { |
321
|
9
|
|
|
|
|
26
|
delete $msg{$_} for grep { !defined $msg{$_} } keys %msg; |
|
122
|
|
|
|
|
205
|
|
322
|
|
|
|
|
|
|
} |
323
|
83
|
50
|
|
|
|
209
|
if( @PruneFields ) { |
324
|
4
|
|
|
4
|
|
10100
|
no warnings; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
2161
|
|
325
|
0
|
|
|
|
|
0
|
delete $msg{$_} for @PruneFields; |
326
|
|
|
|
|
|
|
} |
327
|
83
|
50
|
33
|
|
|
367
|
delete $msg{epoch} if exists $msg{epoch} and !$EpochCreate; |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# |
330
|
|
|
|
|
|
|
# Return our hash reference! |
331
|
83
|
|
|
|
|
250
|
return \%msg; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub preamble_priority { |
336
|
0
|
|
|
0
|
1
|
0
|
my $preamble = int shift; |
337
|
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
0
|
my %hash = ( |
339
|
|
|
|
|
|
|
preamble => $preamble, |
340
|
|
|
|
|
|
|
); |
341
|
|
|
|
|
|
|
|
342
|
0
|
|
|
|
|
0
|
$hash{as_int} = $preamble & $CONV_MASK{priority}; |
343
|
0
|
|
|
|
|
0
|
$hash{as_text} = $LOG_PRIORITY{ $hash{as_int} }; |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
0
|
return \%hash; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub preamble_facility { |
350
|
0
|
|
|
0
|
1
|
0
|
my $preamble = int shift; |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
|
|
0
|
my %hash = ( |
353
|
|
|
|
|
|
|
preamble => $preamble, |
354
|
|
|
|
|
|
|
); |
355
|
|
|
|
|
|
|
|
356
|
0
|
|
|
|
|
0
|
$hash{as_int} = $preamble & $CONV_MASK{facility}; |
357
|
0
|
|
|
|
|
0
|
$hash{as_text} = $LOG_FACILITY{ $hash{as_int} }; |
358
|
|
|
|
|
|
|
|
359
|
0
|
|
|
|
|
0
|
return \%hash; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub set_syslog_timezone { |
364
|
7
|
|
|
7
|
1
|
21419
|
my ( $tz_name ) = @_; |
365
|
|
|
|
|
|
|
|
366
|
7
|
50
|
66
|
|
|
63
|
if( defined $tz_name && (!exists $ENV{TZ} || $tz_name ne $ENV{TZ}) ) { |
|
|
|
66
|
|
|
|
|
367
|
7
|
|
|
|
|
56
|
$ENV{TZ} = $SYSLOG_TIMEZONE = $tz_name; |
368
|
7
|
|
|
|
|
589
|
tzset(); |
369
|
7
|
|
|
|
|
38
|
$OutputTimeZone = 1; |
370
|
|
|
|
|
|
|
# Output some useful debug information |
371
|
|
|
|
|
|
|
printf("set_syslog_timezone('%s') results in a timezone of '%s' with offset: %s\n", |
372
|
|
|
|
|
|
|
$tz_name, |
373
|
|
|
|
|
|
|
strftime('%Z', localtime), |
374
|
|
|
|
|
|
|
strftime('%z', localtime), |
375
|
7
|
50
|
|
|
|
26
|
) if $ENV{DEBUG_PARSE_SYSLOG_LINE}; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
7
|
|
|
|
|
20
|
return $SYSLOG_TIMEZONE; |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub get_syslog_timezone { |
382
|
1
|
|
|
1
|
1
|
738
|
return $SYSLOG_TIMEZONE; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# If you have a syslog which logs dates in UTC, then processing will be much, much faster |
386
|
|
|
|
|
|
|
sub use_utc_syslog { |
387
|
1
|
|
|
1
|
1
|
4118
|
set_syslog_timezone('UTC'); |
388
|
1
|
|
|
|
|
2
|
$NormalizeToUTC = 1; |
389
|
1
|
|
|
|
|
3
|
return; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
1; # End of Parse::Syslog::Line |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
__END__ |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=pod |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=encoding UTF-8 |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head1 NAME |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Parse::Syslog::Line - Simple syslog line parser |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head1 VERSION |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
version 4.0 |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=head1 SYNOPSIS |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
I wanted a very simple log parser for network based syslog input. |
411
|
|
|
|
|
|
|
Nothing existed that simply took a line and returned a hash ref all |
412
|
|
|
|
|
|
|
parsed out. |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
use Parse::Syslog::Line qw(parse_syslog_line); |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
$Parse::Syslog::Line::DateTimeCreate = 1; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
my $href = parse_syslog_line( $msg ); |
419
|
|
|
|
|
|
|
# |
420
|
|
|
|
|
|
|
# $href = { |
421
|
|
|
|
|
|
|
# preamble => '13', |
422
|
|
|
|
|
|
|
# priority => 'notice', |
423
|
|
|
|
|
|
|
# priority_int => 5, |
424
|
|
|
|
|
|
|
# facility => 'user', |
425
|
|
|
|
|
|
|
# facility_int => 8, |
426
|
|
|
|
|
|
|
# date => 'YYYY-MM-DD', |
427
|
|
|
|
|
|
|
# time => 'HH::MM:SS', |
428
|
|
|
|
|
|
|
# epoch => 1361095933, |
429
|
|
|
|
|
|
|
# datetime_str => ISO 8601 datetime, $NormalizeToUTC = 1 then UTC, else local |
430
|
|
|
|
|
|
|
# datetime_obj => undef, # If $DateTimeCreate = 1, else undef |
431
|
|
|
|
|
|
|
# datetime_raw => 'Feb 17 11:12:13' |
432
|
|
|
|
|
|
|
# date_raw => 'Feb 17 11:12:13' |
433
|
|
|
|
|
|
|
# host_raw => 'hostname', # Hostname as it appeared in the message |
434
|
|
|
|
|
|
|
# host => 'hostname', # Hostname without domain |
435
|
|
|
|
|
|
|
# domain => 'blah.com', # if provided |
436
|
|
|
|
|
|
|
# program_raw => 'sshd(blah)[pid]', |
437
|
|
|
|
|
|
|
# program_name => 'sshd', |
438
|
|
|
|
|
|
|
# program_sub => 'pam_unix', |
439
|
|
|
|
|
|
|
# program_pid => 20345, |
440
|
|
|
|
|
|
|
# content => 'the rest of the message' |
441
|
|
|
|
|
|
|
# message => 'program[pid]: the rest of the message', |
442
|
|
|
|
|
|
|
# message_raw => 'The message as it was passed', |
443
|
|
|
|
|
|
|
# ntp => 'ok', # Only set for Cisco messages |
444
|
|
|
|
|
|
|
# }; |
445
|
|
|
|
|
|
|
... |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=head1 EXPORT |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
Exported by default: |
450
|
|
|
|
|
|
|
parse_syslog_line( $one_line_of_syslog_message ); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Optional Exports: |
453
|
|
|
|
|
|
|
:preamble |
454
|
|
|
|
|
|
|
preamble_priority |
455
|
|
|
|
|
|
|
preamble_facility |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
:constants |
458
|
|
|
|
|
|
|
%LOG_FACILITY |
459
|
|
|
|
|
|
|
%LOG_PRIORITY |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
:with_timezones |
462
|
|
|
|
|
|
|
set_syslog_timezone |
463
|
|
|
|
|
|
|
get_syslog_timezone |
464
|
|
|
|
|
|
|
use_utc_syslog |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head1 VARIABLES |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
=head2 ExtractProgram |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
If this variable is set to 1 (the default), parse_syslog_line() will try it's |
471
|
|
|
|
|
|
|
best to extract a "program" field from the input. This is the most expensive |
472
|
|
|
|
|
|
|
set of regex in the module, so if you don't need that pre-parsed, you can speed |
473
|
|
|
|
|
|
|
the module up significantly by setting this variable. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
Vendors who do proprietary non-sense with their syslog formats are to blame for |
476
|
|
|
|
|
|
|
this setting. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Usage: |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
$Parse::Syslog::Line::ExtractProgram = 0; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head2 DateParsing |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
If this variable is set to 0 raw date will not be parsed further into components (datetime_str date time epoch). |
485
|
|
|
|
|
|
|
Default is 1 (parsing enabled). |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Usage: |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
$Parse::Syslog::Line::DateParsing = 0; |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=head2 DateTimeCreate |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
If this variable is set to 1 (the default), a DateTime object will be returned in the |
494
|
|
|
|
|
|
|
$m->{datetime_obj} field. Otherwise, this will be skipped. |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
NOTE: DateTime timezone calculation is fairly slow. Unless you really need to |
497
|
|
|
|
|
|
|
take timezones into account, you're better off using other modes (below). |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Usage: |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$Parse::Syslog::Line::DateTimeCreate = 0; |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=head2 EpochCreate |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
If this variable is set to 1, the default, the number of seconds from UNIX |
506
|
|
|
|
|
|
|
epoch will be returned in the $m->{epoch} field. Setting this to false will |
507
|
|
|
|
|
|
|
only delete the epoch before returning the hash reference. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=head2 NormalizeToUTC |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
When set, the datetime_str will be ISO8601 UTC. |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=head2 OutputTimeZones |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Default is false, but is enabled if you call set_syslog_timezone() or |
516
|
|
|
|
|
|
|
use_utc_syslog(). If enabled, this will append the timezone offset to the |
517
|
|
|
|
|
|
|
datetime_str. |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head2 FmtDate |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
You can pass your own formatter/parser here. Given a raw datetime string it |
522
|
|
|
|
|
|
|
should output a list containing date, time, epoch, datetime_str, |
523
|
|
|
|
|
|
|
in your wanted format. |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
use Parse::Syslog::Line; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
local $Parse::Syslog::Line::FmtDate = sub { |
528
|
|
|
|
|
|
|
my ($raw_datestr) = @_; |
529
|
|
|
|
|
|
|
my @elements = ( |
530
|
|
|
|
|
|
|
#date |
531
|
|
|
|
|
|
|
#time |
532
|
|
|
|
|
|
|
#epoch |
533
|
|
|
|
|
|
|
#datetime_str |
534
|
|
|
|
|
|
|
); |
535
|
|
|
|
|
|
|
return @elements; |
536
|
|
|
|
|
|
|
}; |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
B<NOTE>: No further date processing will be done, you're on your own here. |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head2 PruneRaw |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
This variable defaults to 0, set to 1 to delete all keys in the return hash ending in "_raw" |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Usage: |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
$Parse::Syslog::Line::PruneRaw = 1; |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=head2 PruneEmpty |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
This variable defaults to 0, set to 1 to delete all keys in the return hash which are undefined. |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Usage: |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
$Parse::Syslog::Line::PruneEmpty = 1; |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
=head2 PruneFields |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
This should be an array of fields you'd like to be removed from the hash reference. |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
Usage: |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
@Parse::Syslog::Line::PruneFields = qw(date_raw facility_int priority_int); |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 FUNCTIONS |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head2 parse_syslog_line |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Returns a hash reference of syslog message parsed data. |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
B<NOTE>: Date/time parsing is hard. This module has been optimized to balance |
571
|
|
|
|
|
|
|
common sense and processing speed. Care is taken to ensure that any data input |
572
|
|
|
|
|
|
|
into the system isn't lost, but with the varieties of vendor and admin crafted |
573
|
|
|
|
|
|
|
date formats, we don't always get it right. Feel free to override date |
574
|
|
|
|
|
|
|
processing using by setting the $FmtDate variable or completely disable it with |
575
|
|
|
|
|
|
|
$DateParsing set to 0. |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
=head2 set_syslog_timezone($timezone_name) |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
Sets a timezone $timezone_name for parsed messages. This timezone will be used |
580
|
|
|
|
|
|
|
to calculate offset from UTC if a timezone designation is not present in the |
581
|
|
|
|
|
|
|
message being parsed. This timezone will also serve as the source timezone for |
582
|
|
|
|
|
|
|
the datetime_str field. |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=head2 get_syslog_timezone |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Returns the name of the timezone currently set by set_syslog_timezone. |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=head2 use_utc_syslog |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
A convenient function which sets the syslog timezone to UTC and sets the config |
591
|
|
|
|
|
|
|
variables accordingly. Automatically sets $NormaizeToUTC and datetime_str will |
592
|
|
|
|
|
|
|
be set to the UTC equivalent. |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=head2 preamble_priority |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Takes the Integer portion of the syslog messsage and returns |
597
|
|
|
|
|
|
|
a hash reference as such: |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
$prioRef = { |
600
|
|
|
|
|
|
|
'preamble' => 13 |
601
|
|
|
|
|
|
|
'as_text' => 'notice', |
602
|
|
|
|
|
|
|
'as_int' => 5, |
603
|
|
|
|
|
|
|
}; |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head2 preamble_facility |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
Takes the Integer portion of the syslog messsage and returns |
608
|
|
|
|
|
|
|
a hash reference as such: |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
$facRef = { |
611
|
|
|
|
|
|
|
'preamble' => 13 |
612
|
|
|
|
|
|
|
'as_text' => 'user', |
613
|
|
|
|
|
|
|
'as_int' => 8, |
614
|
|
|
|
|
|
|
}; |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=head1 DEVELOPMENT |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
This module is developed with Dist::Zilla. To build from the repository, use Dist::Zilla: |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
dzil authordeps --missing |cpanm |
621
|
|
|
|
|
|
|
dzil listdeps --missing |cpanm |
622
|
|
|
|
|
|
|
dzil build |
623
|
|
|
|
|
|
|
dzil test |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=head1 AUTHOR |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
Brad Lhotsky <brad@divisionbyzero.net> |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
This software is Copyright (c) 2017 by Brad Lhotsky. |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
This is free software, licensed under: |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
The (three-clause) BSD License |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=for stopwords BartÅomiej Fulanty Csillag Tamas Keedi Kim Mateu X Hunter Neil Bowers Shawn Wilson Tomohiro Hosaka |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=over 4 |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=item * |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
BartÅomiej Fulanty <starlight@cpan.org> |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item * |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
Csillag Tamas <cstamas@digitus.itk.ppke.hu> |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=item * |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Keedi Kim <keedi.k@gmail.com> |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=item * |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
Mateu X Hunter <mhunter@maxmind.com> |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=item * |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
Neil Bowers <neil@bowers.com> |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
=item * |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
Shawn Wilson <swilson@korelogic.com> |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=item * |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Tomohiro Hosaka <bokutin@bokut.in> |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
=back |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=head1 SUPPORT |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 Websites |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
The following websites have more information about this module, and may be of help to you. As always, |
680
|
|
|
|
|
|
|
in addition to those websites please use your favorite search engine to discover more resources. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=over 4 |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=item * |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
MetaCPAN |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
A modern, open-source CPAN search engine, useful to view POD in HTML format. |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
L<http://metacpan.org/release/Parse-Syslog-Line> |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item * |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
RT: CPAN's Bug Tracker |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN. |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
L<https://rt.cpan.org/Public/Dist/Display.html?Name=Parse-Syslog-Line> |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
=back |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=head2 Source Code |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
This module's source code is available by visiting: |
705
|
|
|
|
|
|
|
L<https://github.com/reyjrar/Parse-Syslog-Line> |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
=cut |