syslogstash 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +7 -0
- data/.travis.yml +11 -0
- data/Dockerfile +3 -6
- data/README.md +38 -29
- data/bin/syslogstash +3 -55
- data/lib/syslogstash.rb +49 -44
- data/lib/syslogstash/syslog_reader.rb +212 -180
- data/syslogstash.gemspec +27 -26
- metadata +25 -14
- data/Makefile +0 -14
- data/lib/syslogstash/config.rb +0 -118
- data/lib/syslogstash/logstash_writer.rb +0 -202
- data/lib/syslogstash/prometheus_exporter.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24900387c20afb629c78879987193840d830df0973b1a57131306ae01f47a0cf
|
4
|
+
data.tar.gz: 2236f3b6bca37894f160688e0cc7432e4877f753548b6eea93fcc270df0eebfc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e82d6d53e887428310b2c03e6e3d11f5f16f55b62485595f20650b4061c1e59469f9955a03b9c885a1d5f1d843c4687f1b0c8b87b844a8a3dd1892b1d067252d
|
7
|
+
data.tar.gz: b9b16ddc853c60d08302af3eeb4965fdd1c2a29ea91f4ca695b6c30765e4c12bca5bf64de3645a094c39004af181f5db394bbd6f8978704b0c250d42c0286770
|
data/.editorconfig
ADDED
data/.travis.yml
ADDED
data/Dockerfile
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
FROM ruby:2.
|
1
|
+
FROM ruby:2.6
|
2
2
|
|
3
3
|
ARG GEM_VERSION="> 0"
|
4
4
|
|
5
5
|
COPY pkg/syslogstash-$GEM_VERSION.gem /tmp/syslogstash.gem
|
6
6
|
|
7
|
-
RUN
|
8
|
-
&&
|
9
|
-
&& gem install /tmp/syslogstash.gem \
|
10
|
-
&& apk del build-base \
|
11
|
-
&& rm -f /var/cache/apk/* /tmp/syslogstash.gem
|
7
|
+
RUN gem install /tmp/syslogstash.gem \
|
8
|
+
&& rm -f /tmp/syslogstash.gem
|
12
9
|
|
13
10
|
ENTRYPOINT ["/usr/local/bundle/bin/syslogstash"]
|
data/README.md
CHANGED
@@ -49,8 +49,8 @@ least, `syslogstash` needs to know where logstash is (`LOGSTASH_SERVER`),
|
|
49
49
|
and the socket to listen on for syslog messages (`SYSLOG_SOCKET`). You
|
50
50
|
specify those on the command line, like so:
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
SYSLOGSTASH_LOGSTASH_SERVER=logstash-json \
|
53
|
+
SYSLOGSTASH_SYSLOG_SOCKET=/dev/log \
|
54
54
|
syslogstash
|
55
55
|
|
56
56
|
The full set of environment variables, and their meaning, is described in
|
@@ -92,6 +92,12 @@ aspects of runtime operation. They are:
|
|
92
92
|
the logstash server from DNS, then SIGHUP syslogstash to make it switch
|
93
93
|
to another server.
|
94
94
|
|
95
|
+
* **`SIGQUIT`** -- dump thread stacktraces and allocation information to
|
96
|
+
`stderr`.
|
97
|
+
|
98
|
+
* **`SIGINT`** / **`SIGTERM`** -- gracefully terminate. Sending either signal
|
99
|
+
twice will cause shutdown to be done somewhat less gracefully.
|
100
|
+
|
95
101
|
|
96
102
|
## Use with Docker
|
97
103
|
|
@@ -139,8 +145,8 @@ All configuration of syslogstash is done by placing values in environment
|
|
139
145
|
variables. The environment variables that syslogstash recognises are listed
|
140
146
|
below.
|
141
147
|
|
142
|
-
* **`
|
143
|
-
logstash server(s) you wish to send entries to. This can be any of:
|
148
|
+
* **`SYSLOGSTASH_LOGSTASH_SERVER`** (required) -- the domain name or address of
|
149
|
+
the logstash server(s) you wish to send entries to. This can be any of:
|
144
150
|
|
145
151
|
* An IPv4 address and port, separated by a colon. For example,
|
146
152
|
`192.0.2.42:5151`. The port *must* be specified.
|
@@ -161,35 +167,33 @@ below.
|
|
161
167
|
In all cases, syslogstash respects DNS record TTLs and SRV record
|
162
168
|
weight/priority selection rules. We're not monsters.
|
163
169
|
|
164
|
-
* **`
|
165
|
-
syslogstash should create and listen on for syslog format messages.
|
170
|
+
* **`SYSLOGSTASH_SYSLOG_SOCKET`** (required) -- the absolute path to the socket
|
171
|
+
which syslogstash should create and listen on for syslog format messages.
|
166
172
|
|
167
|
-
* **`
|
168
|
-
messages to queue if the logstash servers are unavailable. Under
|
169
|
-
operation, syslog messages are immediately relayed to the logstash
|
170
|
-
as they are received. However, if no logstash servers are available,
|
171
|
-
syslogstash will maintain a backlog of up to this many syslog messages,
|
172
|
-
|
173
|
-
again.
|
173
|
+
* **`SYSLOGSTASH_BACKLOG_SIZE`** (optional; default `"1000000"`) -- the maximum
|
174
|
+
number of messages to queue if the logstash servers are unavailable. Under
|
175
|
+
normal operation, syslog messages are immediately relayed to the logstash
|
176
|
+
server as they are received. However, if no logstash servers are available,
|
177
|
+
syslogstash will maintain a backlog of up to this many syslog messages, and
|
178
|
+
will send the entire backlog once a logstash server becomes available again.
|
174
179
|
|
175
180
|
In the event that the queue size limit is reached, the oldest messages
|
176
181
|
will be dropped to make way for the new ones.
|
177
182
|
|
178
|
-
* **`
|
183
|
+
* **`SYSLOGSTASH_RELAY_TO_STDOUT`** (optional; default `"no"`) -- if set to a
|
179
184
|
true-ish string (any of `true`, `yes`, `on`, or `1`, compared
|
180
185
|
case-insensitively), then all the syslog messages which are received will
|
181
186
|
be printed to stdout (with the priority/facility prefix removed). This
|
182
187
|
isn't a replacement for a fully-featured syslog server, merely a quick way
|
183
188
|
to dump messages if absolutely required.
|
184
189
|
|
185
|
-
* **`
|
186
|
-
|
187
|
-
|
188
|
-
listening on all interfaces on port 9159.
|
190
|
+
* **`SYSLOGSTASH_METRICS_PORT`** (optional; default `""`) -- if set to a
|
191
|
+
valid port number (1-65535), a Prometheus-compatible statistics exporter will be
|
192
|
+
started, listening on all interfaces on the specified port.
|
189
193
|
|
190
|
-
* **`
|
191
|
-
the entries which are forwarded to logstash, you can specify them
|
192
|
-
for example:
|
194
|
+
* **`SYSLOGSTASH_ADD_FIELD_<name>`** (optional) -- if you want to add extra
|
195
|
+
fields to the entries which are forwarded to logstash, you can specify them
|
196
|
+
here, for example:
|
193
197
|
|
194
198
|
ADD_FIELD_foo=bar ADD_FIELD_baz=wombat [...] syslogstash
|
195
199
|
|
@@ -199,14 +203,19 @@ below.
|
|
199
203
|
than strings, are not supported. Also, if you specify a field name also
|
200
204
|
used by syslogstash, the results are explicitly undefined.
|
201
205
|
|
202
|
-
* **`
|
203
|
-
to feed the syslog messages that syslogstash receives to another
|
206
|
+
* **`SYSLOGSTASH_RELAY_SOCKETS`** (optional; default `""`) -- on the off-chance
|
207
|
+
you want to feed the syslog messages that syslogstash receives to another
|
204
208
|
syslog-compatible consumer (say, an old-school syslogd) you can specify
|
205
|
-
additional filenames to use here. Multiple socket filenames can be
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
209
|
+
additional filenames to use here. Multiple socket filenames can be specified
|
210
|
+
by separating each file name with a colon. Syslogstash will open each of the
|
211
|
+
specified sockets, if they exist, and write each received message to the
|
212
|
+
socket. If the socket does not exist, or the open or write operations fail,
|
213
|
+
syslogstash **will not** retry.
|
214
|
+
|
215
|
+
* **`SYSLOGSTASH_DROP_REGEX`** (optional) -- Regular expression to run on
|
216
|
+
input, if it matches then the message will be dropped and not sent to
|
217
|
+
logstash. However, it *will* still be sent to stdout and any relay sockets,
|
218
|
+
if those options are enabled.
|
210
219
|
|
211
220
|
|
212
221
|
# Contributing
|
@@ -222,7 +231,7 @@ request](https://github.com/discourse/syslogstash/pulls].
|
|
222
231
|
Unless otherwise stated, everything in this repo is covered by the following
|
223
232
|
copyright notice:
|
224
233
|
|
225
|
-
Copyright (C) 2015, 2018 Civilized Discourse Construction Kit Inc.
|
234
|
+
Copyright (C) 2015, 2018, 2019 Civilized Discourse Construction Kit Inc.
|
226
235
|
|
227
236
|
This program is free software: you can redistribute it and/or modify it
|
228
237
|
under the terms of the GNU General Public License version 3, as
|
data/bin/syslogstash
CHANGED
@@ -1,62 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'syslogstash'
|
4
|
-
require 'logger'
|
5
|
-
|
6
|
-
logger = Logger.new($stderr)
|
7
|
-
logger.formatter = ->(s, t, p, m) { "#{s[0]} [#{p}] #{m}\n" }
|
8
|
-
logger.level = Logger.const_get(ENV['SYSLOGSTASH_LOG_LEVEL'] || "INFO")
|
9
4
|
|
10
5
|
begin
|
11
|
-
|
12
|
-
rescue
|
13
|
-
$stderr.puts "
|
6
|
+
Syslogstash.new(ENV).start
|
7
|
+
rescue ServiceSkeleton::Error::InvalidEnvironmentError => ex
|
8
|
+
$stderr.puts "Configuration error: #{ex.message}"
|
14
9
|
exit 1
|
15
10
|
end
|
16
|
-
|
17
|
-
syslogstash = Syslogstash.new(cfg)
|
18
|
-
|
19
|
-
sig_r, sig_w = IO.pipe
|
20
|
-
|
21
|
-
Signal.trap("USR1") do
|
22
|
-
sig_w.print '1'
|
23
|
-
end
|
24
|
-
Signal.trap("USR2") do
|
25
|
-
sig_w.print '2'
|
26
|
-
end
|
27
|
-
Signal.trap("URG") do
|
28
|
-
sig_w.print 'U'
|
29
|
-
end
|
30
|
-
Signal.trap("HUP") do
|
31
|
-
sig_w.print 'H'
|
32
|
-
end
|
33
|
-
|
34
|
-
Thread.new do
|
35
|
-
loop do
|
36
|
-
begin
|
37
|
-
c = sig_r.getc
|
38
|
-
if c == '1'
|
39
|
-
logger.level -= 1 unless logger.level == Logger::DEBUG
|
40
|
-
logger.info("SignalHandler") { "Received SIGUSR1; log level is now #{Logger::SEV_LABEL[logger.level]}." }
|
41
|
-
elsif c == '2'
|
42
|
-
logger.level += 1 unless logger.level == Logger::ERROR
|
43
|
-
logger.info("SignalHandler") { "Received SIGUSR2; log level is now #{Logger::SEV_LABEL[logger.level]}." }
|
44
|
-
elsif c == 'U'
|
45
|
-
cfg.relay_to_stdout = !cfg.relay_to_stdout
|
46
|
-
logger.info("SignalHandler") { "Received SIGURG; Relaying to stdout is now #{cfg.relay_to_stdout ? "enabled" : "disabled"}" }
|
47
|
-
elsif c== 'H'
|
48
|
-
logger.info("SignalHandler") { "Received SIGHUP" }
|
49
|
-
syslogstash.force_disconnect!
|
50
|
-
else
|
51
|
-
logger.error("SignalHandler") { "Got an unrecognised character from signal pipe: #{c.inspect}" }
|
52
|
-
end
|
53
|
-
rescue StandardError => ex
|
54
|
-
logger.error("SignalHandler") { (["Exception raised: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
55
|
-
rescue Exception => ex
|
56
|
-
$stderr.puts (["Fatal exception in syslogstash signal handler: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ")
|
57
|
-
exit 42
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
syslogstash.run
|
data/lib/syslogstash.rb
CHANGED
@@ -2,54 +2,59 @@ require 'uri'
|
|
2
2
|
require 'socket'
|
3
3
|
require 'json'
|
4
4
|
require 'thwait'
|
5
|
+
require 'logstash_writer'
|
6
|
+
require 'service_skeleton'
|
5
7
|
|
6
8
|
# Read syslog messages from one or more sockets, and send it to a logstash
|
7
9
|
# server.
|
8
10
|
#
|
9
|
-
class Syslogstash
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
11
|
+
class Syslogstash < ServiceSkeleton
|
12
|
+
string :SYSLOGSTASH_LOGSTASH_SERVER
|
13
|
+
string :SYSLOGSTASH_SYSLOG_SOCKET
|
14
|
+
string :SYSLOGSTASH_RELAY_TO_STDOUT, default: false
|
15
|
+
string :SYSLOGSTASH_DROP_REGEX, default: nil
|
16
|
+
integer :SYSLOGSTASH_BACKLOG_SIZE, default: 1_000_000, range: 0..(2**31-1)
|
17
|
+
path_list :SYSLOGSTASH_RELAY_SOCKETS, default: []
|
18
|
+
kv_list :SYSLOGSTASH_ADD_FIELDS, default: {}, key_pattern: /\ASYSLOGSTASH_ADD_FIELD_(.*)\z/
|
19
|
+
|
20
|
+
def initialize(*_)
|
21
|
+
super
|
22
|
+
|
23
|
+
hook_signal("URG") do
|
24
|
+
config.relay_to_stdout = !config.relay_to_stdout
|
25
|
+
logger.info(logloc) { "SIGURG received; relay_to_stdout is now #{config.relay_to_stdout.inspect}" }
|
26
|
+
end
|
27
|
+
|
28
|
+
@shutdown_reader, @shutdown_writer = IO.pipe
|
29
|
+
|
30
|
+
metrics.counter(:syslogstash_messages_received_total, "The number of syslog messages received from the log socket")
|
31
|
+
metrics.counter(:syslogstash_messages_sent_total, "The number of logstash messages sent to each logstash server")
|
32
|
+
metrics.gauge(:syslogstash_last_relayed_message_timestamp, "When the last message that was successfully relayed to logstash was originally received")
|
33
|
+
metrics.gauge(:syslogstash_queue_size, "How many messages are currently in the queue to be sent")
|
34
|
+
metrics.counter(:syslogstash_dropped_total, "Number of log entries that were not forwarded due to matching the drop regex")
|
35
|
+
|
36
|
+
@writer = LogstashWriter.new(server_name: config.logstash_server, backlog: config.backlog_size, logger: config.logger, metrics_registry: metrics)
|
37
|
+
@reader = SyslogReader.new(config, @writer, metrics)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
@writer.run
|
42
|
+
@reader.start!
|
43
|
+
|
44
|
+
@shutdown_reader.getc
|
45
|
+
@shutdown_reader.close
|
46
|
+
end
|
47
|
+
|
48
|
+
def shutdown
|
49
|
+
@reader.stop!
|
50
|
+
@writer.stop
|
51
|
+
|
52
|
+
@shutdown_writer.close
|
53
|
+
end
|
54
|
+
|
55
|
+
def force_disconnect!
|
56
|
+
@writer.force_disconnect!
|
57
|
+
end
|
50
58
|
end
|
51
59
|
|
52
|
-
require_relative 'syslogstash/config'
|
53
60
|
require_relative 'syslogstash/syslog_reader'
|
54
|
-
require_relative 'syslogstash/logstash_writer'
|
55
|
-
require_relative 'syslogstash/prometheus_exporter'
|
@@ -1,184 +1,216 @@
|
|
1
1
|
# A single socket reader.
|
2
2
|
#
|
3
3
|
class Syslogstash::SyslogReader
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
4
|
+
include ServiceSkeleton::BackgroundWorker
|
5
|
+
|
6
|
+
def initialize(config, logstash, metrics)
|
7
|
+
@config, @logstash, @metrics = config, logstash, metrics
|
8
|
+
|
9
|
+
@logger = config.logger
|
10
|
+
|
11
|
+
@shutdown_reader, @shutdown_writer = IO.pipe
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Start reading from the socket file, parsing entries, and flinging
|
17
|
+
# them at logstash.
|
18
|
+
#
|
19
|
+
def start
|
20
|
+
config.logger.debug(logloc) { "off we go!" }
|
21
|
+
|
22
|
+
begin
|
23
|
+
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
|
24
|
+
socket.bind(Socket.pack_sockaddr_un(config.syslog_socket))
|
25
|
+
File.chmod(0666, config.syslog_socket)
|
26
|
+
rescue Errno::EEXIST, Errno::EADDRINUSE
|
27
|
+
config.logger.info(logloc) { "socket file #{config.syslog_socket} already exists; deleting" }
|
28
|
+
File.unlink(config.syslog_socket) rescue nil
|
29
|
+
retry
|
30
|
+
rescue StandardError => ex
|
31
|
+
raise ex.class, "Error while trying to bind to #{config.syslog_socket}: #{ex.message}", ex.backtrace
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
loop do
|
36
|
+
IO.select([@shutdown_reader, socket]).first.each do |fd|
|
37
|
+
if fd == socket
|
38
|
+
begin
|
39
|
+
msg = socket.recvmsg_nonblock
|
40
|
+
rescue IO::WaitWritable
|
41
|
+
config.logger.debug(logloc) { "select said a message was waiting, but it wasn't. o.O" }
|
42
|
+
else
|
43
|
+
config.logger.debug(logloc) { "Message received: #{msg.inspect}" }
|
44
|
+
@metrics.messages_received_total.increment(socket_path: config.syslog_socket)
|
45
|
+
@metrics.queue_size.increment({})
|
46
|
+
relay_message msg.first
|
47
|
+
process_message msg.first.chomp
|
48
|
+
end
|
49
|
+
elsif fd == @shutdown_reader
|
50
|
+
@shutdown_reader.close
|
51
|
+
config.logger.debug(logloc) { "Tripped over shutdown reader" }
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
socket.close
|
58
|
+
config.logger.debug(logloc) { "removing socket file #{config.syslog_socket}" }
|
59
|
+
File.unlink(config.syslog_socket) rescue nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def shutdown
|
64
|
+
@shutdown_writer.close
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :config, :logger
|
70
|
+
|
71
|
+
def process_message(msg)
|
72
|
+
if msg =~ /^<(\d+)>(\w{3} [ 0-9]{2} [0-9:]{8}) (.*)$/
|
73
|
+
flags = $1.to_i
|
74
|
+
timestamp = $2
|
75
|
+
content = $3
|
76
|
+
|
77
|
+
# Lo! the many ways that syslog messages can be formatted
|
78
|
+
hostname, program, pid, message = case content
|
79
|
+
# the gold standard: hostname, program name with optional PID
|
80
|
+
when /^([a-zA-Z0-9._-]*[^:]) (\S+?)(\[(\d+)\])?: (.*)$/
|
81
|
+
[$1, $2, $4, $5]
|
82
|
+
# hostname, no program name
|
83
|
+
when /^([a-zA-Z0-9._-]+) (\S+[^:] .*)$/
|
84
|
+
[$1, nil, nil, $2]
|
85
|
+
# program name, no hostname (yeah, you heard me, non-RFC compliant!)
|
86
|
+
when /^(\S+?)(\[(\d+)\])?: (.*)$/
|
87
|
+
[nil, $1, $3, $4]
|
88
|
+
else
|
89
|
+
# I have NFI
|
90
|
+
[nil, nil, nil, content]
|
91
|
+
end
|
92
|
+
|
93
|
+
if config.drop_regex && message && message.match?(config.drop_regex)
|
94
|
+
@metrics.dropped_total.increment({})
|
95
|
+
config.logger.debug(logloc) { "dropping message #{message}" }
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
severity = flags % 8
|
100
|
+
facility = flags / 8
|
101
|
+
|
102
|
+
log_entry = log_entry(
|
103
|
+
syslog_timestamp: timestamp,
|
104
|
+
severity: severity,
|
105
|
+
facility: facility,
|
106
|
+
hostname: hostname,
|
107
|
+
program: program,
|
108
|
+
pid: pid.nil? ? nil : pid.to_i,
|
109
|
+
message: message,
|
110
|
+
)
|
111
|
+
|
112
|
+
@logstash.send_event(log_entry)
|
113
|
+
else
|
114
|
+
config.logger.warn(logloc) { "Unparseable message: #{msg.inspect}" }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def log_entry(h)
|
119
|
+
{}.tap do |e|
|
120
|
+
e['@version'] = '1'
|
121
|
+
e['@timestamp'] = Time.now.utc.strftime("%FT%T.%LZ")
|
122
|
+
|
123
|
+
h[:facility_name] = FACILITIES[h[:facility]]
|
124
|
+
h[:severity_name] = SEVERITIES[h[:severity]]
|
125
|
+
|
126
|
+
e.merge!(h.delete_if { |k,v| v.nil? })
|
127
|
+
e.merge!(config.add_fields)
|
128
|
+
|
129
|
+
config.logger.debug(logloc) { "Complete log entry is: #{e.inspect}" }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def relay_message(msg)
|
134
|
+
@currently_failed ||= {}
|
135
|
+
|
136
|
+
if config.relay_to_stdout
|
137
|
+
# This one's easy
|
138
|
+
puts msg.sub(/\A<\d+>/, '')
|
139
|
+
$stdout.flush
|
140
|
+
end
|
141
|
+
|
142
|
+
config.relay_sockets.each do |f|
|
143
|
+
relay_to_socket(f)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def relay_to_socket(f)
|
148
|
+
begin
|
149
|
+
s = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
|
150
|
+
begin
|
151
|
+
s.connect(Socket.pack_sockaddr_un(f))
|
152
|
+
rescue Errno::ENOENT
|
153
|
+
# Socket doesn't exist; we don't care enough about this to bother
|
154
|
+
# reporting it. People will figure it out themselves soon enough.
|
155
|
+
rescue StandardError => ex
|
156
|
+
unless @currently_failed[f]
|
157
|
+
config.logger.warn(logloc) { "Error while connecting to relay socket #{f}: #{ex.message} (#{ex.class})" }
|
158
|
+
@currently_failed[f] = true
|
159
|
+
end
|
160
|
+
return
|
161
|
+
end
|
162
|
+
|
163
|
+
begin
|
164
|
+
# We really, *really* don't want to block the world just because
|
165
|
+
# whoever's on the other end of the relay socket can't process
|
166
|
+
# messages quick enough.
|
167
|
+
s.sendmsg_nonblock(msg)
|
168
|
+
if @currently_failed[f]
|
169
|
+
config.logger.info(logloc) { "Error on socket #{f} has cleared; messages are being delivered again" }
|
170
|
+
@currently_failed[f] = false
|
171
|
+
end
|
172
|
+
rescue Errno::ENOTCONN
|
173
|
+
unless @currently_failed[f]
|
174
|
+
config.logger.debug(logloc) { "Nothing is listening on socket #{f}" }
|
175
|
+
@currently_failed[f] = true
|
176
|
+
end
|
177
|
+
rescue IO::EAGAINWaitWritable
|
178
|
+
unless @currently_failed[f]
|
179
|
+
config.logger.warn(logloc) { "Socket #{f} is currently backlogged; messages to this socket are now being discarded undelivered" }
|
180
|
+
@currently_failed[f] = true
|
181
|
+
end
|
182
|
+
rescue StandardError => ex
|
183
|
+
config.logger.warn(logloc) { (["Failed to relay message to socket #{f} from #{config.syslog_socket}: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
184
|
+
end
|
185
|
+
ensure
|
186
|
+
s.close
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
FACILITIES = %w{
|
191
|
+
kern
|
192
|
+
user
|
193
|
+
mail
|
194
|
+
daemon
|
195
|
+
auth
|
196
|
+
syslog
|
197
|
+
lpr
|
198
|
+
news
|
199
|
+
uucp
|
200
|
+
cron
|
201
|
+
authpriv
|
202
|
+
ftp
|
203
|
+
local0 local1 local2 local3 local4 local5 local6 local7
|
204
|
+
}
|
205
|
+
|
206
|
+
SEVERITIES = %w{
|
207
|
+
emerg
|
208
|
+
alert
|
209
|
+
crit
|
210
|
+
err
|
211
|
+
warning
|
212
|
+
notice
|
213
|
+
info
|
214
|
+
debug
|
215
|
+
}
|
184
216
|
end
|