syslogstash 1.3.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +13 -0
- data/Makefile +14 -0
- data/README.md +165 -50
- data/bin/syslogstash +43 -38
- data/lib/syslogstash.rb +20 -19
- data/lib/syslogstash/config.rb +118 -0
- data/lib/syslogstash/logstash_writer.rb +96 -28
- data/lib/syslogstash/prometheus_exporter.rb +18 -41
- data/lib/syslogstash/syslog_reader.rb +38 -39
- data/syslogstash.gemspec +1 -1
- metadata +8 -6
- data/lib/syslogstash/worker.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dd70d42345ffba77d56d3511d5220e2ab399ce9
|
4
|
+
data.tar.gz: 98f6175cd0cc98d9ca6a51593657b7e3c09f178c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9fe1db70bce8d062dbc84b3c24e5650ed489b9ff89f7502225b2673f848e3c4abde63d6369cd14b3235f1adb9bfb15ceb8b217eaf4392a52badbfdd906ecbf03
|
7
|
+
data.tar.gz: 1e4163fa6ccd9c6f6402be87d571f1d67e80584935e7a7772084f3c3dc968f321acbdd0f2ecf7065f9b5f295825d2dd9f997189f7866cd0b9847dc03152a3224
|
data/Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
FROM ruby:2.3-alpine
|
2
|
+
|
3
|
+
ARG GEM_VERSION="> 0"
|
4
|
+
|
5
|
+
COPY pkg/syslogstash-$GEM_VERSION.gem /tmp/syslogstash.gem
|
6
|
+
|
7
|
+
RUN apk update \
|
8
|
+
&& apk add build-base \
|
9
|
+
&& gem install /tmp/syslogstash.gem \
|
10
|
+
&& apk del build-base \
|
11
|
+
&& rm -f /var/cache/apk/* /tmp/syslogstash.gem
|
12
|
+
|
13
|
+
ENTRYPOINT ["/usr/local/bundle/bin/syslogstash"]
|
data/Makefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
IMAGE := discourse/syslogstash
|
2
|
+
TAG := $(shell date -u +%Y%m%d.%H%M%S)
|
3
|
+
|
4
|
+
.PHONY: default
|
5
|
+
default: push
|
6
|
+
@printf "${IMAGE}:${TAG} ready\n"
|
7
|
+
|
8
|
+
.PHONY: push
|
9
|
+
push: build
|
10
|
+
docker push ${IMAGE}:${TAG}
|
11
|
+
|
12
|
+
.PHONY: build
|
13
|
+
build:
|
14
|
+
docker build --build-arg=http_proxy=${http_proxy} -t ${IMAGE}:${TAG} .
|
data/README.md
CHANGED
@@ -1,4 +1,21 @@
|
|
1
|
-
|
1
|
+
Syslogstash is intended to provide a syslog-compatible socket for one or
|
2
|
+
more applications to send their syslog messages to. The messages are then
|
3
|
+
parsed and sent to a logstash server for posterity. No more needing to run
|
4
|
+
a syslog server that writes to a file, just to have a second program that
|
5
|
+
reads those files again. With syslogstash, everything is in one neat little
|
6
|
+
package.
|
7
|
+
|
8
|
+
If you're running a containerised environment, there's a reasonable chance
|
9
|
+
you've got multiple things that want to log to syslog, but you want to keep
|
10
|
+
them organised and separate. That's easy: just run multiple syslogstash
|
11
|
+
instances, one per "virtual syslog socket" you want to provide. Multiple
|
12
|
+
containers can share the same socket, they'll just share a logstash
|
13
|
+
connection and have the same metadata / extra tags.
|
14
|
+
|
15
|
+
For maximum flexibility, you can optionally feed the syslog messages to one
|
16
|
+
or more other "downstream" sockets, and/or print all the log messages to
|
17
|
+
stdout for ad-hoc "local" debugging.
|
18
|
+
|
2
19
|
|
3
20
|
# Installation
|
4
21
|
|
@@ -17,74 +34,172 @@ If you're the sturdy type that likes to run from git:
|
|
17
34
|
Or, if you've eschewed the convenience of Rubygems entirely, then you
|
18
35
|
presumably know what to do already.
|
19
36
|
|
37
|
+
## Docker
|
38
|
+
|
39
|
+
Published image at https://hub.docker.com/r/discourse/syslogstash/
|
40
|
+
|
41
|
+
To build a new Docker image, run `rake docker:build`. A `rake docker:push`
|
42
|
+
will push out a new release.
|
43
|
+
|
20
44
|
|
21
45
|
# Usage
|
22
46
|
|
23
|
-
|
24
|
-
|
47
|
+
Syslogstash is configured by means of environment variables. At the very
|
48
|
+
least, `syslogstash` needs to know where logstash is (`LOGSTASH_SERVER`),
|
49
|
+
and the socket to listen on for syslog messages (`SYSLOG_SOCKET`). You
|
50
|
+
specify those on the command line, like so:
|
51
|
+
|
52
|
+
LOGSTASH_SERVER=logstash-json \
|
53
|
+
SYSLOG_SOCKET=/dev/log \
|
54
|
+
syslogstash
|
55
|
+
|
56
|
+
The full set of environment variables, and their meaning, is described in
|
57
|
+
the "Syslogstash Configuration" section, below.
|
58
|
+
|
59
|
+
|
60
|
+
## Logstash server setup
|
25
61
|
|
26
|
-
|
62
|
+
The logstash server(s) you send the collected messages to must be configured
|
63
|
+
to listen on a TCP port with the `json_lines` codec. This can be done quite
|
64
|
+
easily as follows:
|
27
65
|
|
28
|
-
|
66
|
+
tcp {
|
67
|
+
port => 5151
|
68
|
+
codec => "json_lines"
|
69
|
+
}
|
29
70
|
|
30
|
-
|
31
|
-
YAML file. It consists of two sections, `sockets` and `servers`, which list
|
32
|
-
the UNIX sockets to listen for syslog messages on, and the URLs of logstash
|
33
|
-
servers to send the resulting log entries to. Optionally, you can specify
|
34
|
-
additional fields to insert into every message received from each syslog
|
35
|
-
socket.
|
71
|
+
Adjust the port number to taste.
|
36
72
|
|
37
|
-
It looks like this:
|
38
73
|
|
39
|
-
|
40
|
-
# These sockets have no additional fields
|
41
|
-
/tmp/sock1:
|
42
|
-
/tmp/sock2:
|
74
|
+
## Signals
|
43
75
|
|
44
|
-
|
45
|
-
|
46
|
-
/tmp/supersock:
|
47
|
-
add_fields:
|
48
|
-
foo: bar
|
49
|
-
baz: wombat
|
50
|
-
relay_to:
|
51
|
-
- /tmp/relaysock1
|
52
|
-
- /tmp/relaysock2
|
76
|
+
There are a few signals that syslogstash recognises, to control various
|
77
|
+
aspects of runtime operation. They are:
|
53
78
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
- tcp://10.0.0.2:5151
|
79
|
+
* **`SIGUSR1`** / **`SIGUSR2`** -- tell syslogstash to increase (`USR1`) or
|
80
|
+
decrease (`USR2`) the verbosity of its own internal logging. This doesn't
|
81
|
+
change in *any* way the nature or volume of syslog messages that are
|
82
|
+
processed and sent to logstash, it is *only* for syslogstash's own internal
|
83
|
+
operational logging.
|
60
84
|
|
85
|
+
* **`SIGURG`** -- toggle whether or not relaying to stdout is enabled or
|
86
|
+
disabled.
|
61
87
|
|
62
|
-
### Socket configuration
|
63
88
|
|
64
|
-
|
65
|
-
configuration, you can add logstash fields to each entry, and configure
|
66
|
-
socket relaying.
|
89
|
+
## Use with Docker
|
67
90
|
|
68
|
-
|
91
|
+
For convenience, `syslogstash` is available in a Docker container,
|
92
|
+
`discourse/syslogstash:v2`. It requires a bit of gymnastics to get the
|
93
|
+
syslog socket from the `syslogstash` container to whatever container you
|
94
|
+
want to capture syslog messages from. Typically, you'll want to share a
|
95
|
+
volume between the two containers, tell `syslogstash` to create its socket
|
96
|
+
there, and then symlink `/dev/log` from the other container to there.
|
69
97
|
|
70
|
-
|
71
|
-
is received on this socket, before it is passed on to logstash.
|
98
|
+
For example, you might start the syslogstash container like this:
|
72
99
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
100
|
+
docker run -v /srv/docker/syslogstash:/syslogstash \
|
101
|
+
-e LOGSTASH_SERVER=logstash-json \
|
102
|
+
-e SYSLOG_SOCKET=/syslogstash/log.sock \
|
103
|
+
discourse/syslogstash:v2
|
77
104
|
|
105
|
+
Then use the same volume in your other container:
|
78
106
|
|
79
|
-
|
107
|
+
docker run -v /srv/docker/syslogstash:/syslogstash something/funny
|
80
108
|
|
81
|
-
|
82
|
-
`syslogstash` to send log entries to. It can look as simple as this:
|
109
|
+
In the other container's startup script, include the following command:
|
83
110
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
111
|
+
ln -sf /syslogstash/log.sock /dev/log
|
112
|
+
|
113
|
+
... and everything will work nicely.
|
114
|
+
|
115
|
+
If you feel like playing on nightmare mode, you can also mount the log
|
116
|
+
socket directly into the other container, like this:
|
117
|
+
|
118
|
+
docker run -v /srv/docker/syslogstash/log.sock:/dev/log something/funny
|
119
|
+
|
120
|
+
This allows you to deal with poorly-implemented containers which run
|
121
|
+
software that logs to syslog but doesn't provide a way to override where
|
122
|
+
`/dev/log` points. *However*, due to the way bind mounts and Unix sockets
|
123
|
+
interact, if the syslogstash container restarts *for any reason*, you also
|
124
|
+
need to restart any containers that have the socket itself as a volume. If
|
125
|
+
you can coax your container management system into satisfying that
|
126
|
+
condition, then you're golden.
|
127
|
+
|
128
|
+
|
129
|
+
# Syslogstash Configuration
|
130
|
+
|
131
|
+
All configuration of syslogstash is done by placing values in environment
|
132
|
+
variables. The environment variables that syslogstash recognises are listed
|
133
|
+
below.
|
134
|
+
|
135
|
+
* **`LOGSTASH_SERVER`** (required) -- the domain name or address of the
|
136
|
+
logstash server(s) you wish to send entries to. This can be any of:
|
137
|
+
|
138
|
+
* An IPv4 address and port, separated by a colon. For example,
|
139
|
+
`192.0.2.42:5151`. The port *must* be specified.
|
140
|
+
|
141
|
+
* An IPv6 address (enclosed in square brackets) and port, separated by a
|
142
|
+
colon. For example, `[2001:db8::42]:5151`. The port *must* be
|
143
|
+
specified.
|
144
|
+
|
145
|
+
* A fully-qualified or relative domain name and port, separated by a
|
146
|
+
colon. The name given will be resolved and all IPv4 and IPv6
|
147
|
+
addresses returned will be tried in random order until a successful
|
148
|
+
connection is made to one of them. The port *must* be specified.
|
149
|
+
|
150
|
+
* A fully-qualified or relative domain name *without a port*. In this
|
151
|
+
case, the name given will be resolved as a SRV record, and the names and
|
152
|
+
ports returned will be used.
|
153
|
+
|
154
|
+
In all cases, syslogstash respects DNS record TTLs and SRV record
|
155
|
+
weight/priority selection rules. We're not monsters.
|
156
|
+
|
157
|
+
* **`SYSLOG_SOCKET`** (required) -- the absolute path to the socket which
|
158
|
+
syslogstash should create and listen on for syslog format messages.
|
159
|
+
|
160
|
+
* **`BACKLOG_SIZE`** (optional; default `"1000000"`) -- the maximum number of
|
161
|
+
messages to queue if the logstash servers are unavailable. Under normal
|
162
|
+
operation, syslog messages are immediately relayed to the logstash server
|
163
|
+
as they are received. However, if no logstash servers are available,
|
164
|
+
syslogstash will maintain a backlog of up to this many syslog messages,
|
165
|
+
and will send the entire backlog once a logstash server becomes available
|
166
|
+
again.
|
167
|
+
|
168
|
+
In the event that the queue size limit is reached, the oldest messages
|
169
|
+
will be dropped to make way for the new ones.
|
170
|
+
|
171
|
+
* **`RELAY_TO_STDOUT`** (optional; default `"no"`) -- if set to a
|
172
|
+
true-ish string (any of `true`, `yes`, `on`, or `1`, compared
|
173
|
+
case-insensitively), then all the syslog messages which are received will
|
174
|
+
be printed to stdout (with the priority/facility prefix removed). This
|
175
|
+
isn't a replacement for a fully-featured syslog server, merely a quick way
|
176
|
+
to dump messages if absolutely required.
|
177
|
+
|
178
|
+
* **`STATS_SERVER`** (optional; default `"no"`) -- if set to a true-ish
|
179
|
+
string (any of `true`, `yes`, `on`, or `1`, compared case-insensitively),
|
180
|
+
then a Prometheus-compatible statistics exporter will be started,
|
181
|
+
listening on all interfaces on port 9159.
|
182
|
+
|
183
|
+
* **`ADD_FIELD_<name>`** (optional) -- if you want to add extra fields to
|
184
|
+
the entries which are forwarded to logstash, you can specify them here,
|
185
|
+
for example:
|
186
|
+
|
187
|
+
ADD_FIELD_foo=bar ADD_FIELD_baz=wombat [...] syslogstash
|
188
|
+
|
189
|
+
This will cause all entries sent to logstash to contain `"foo": "bar"`
|
190
|
+
and `"baz": "wombat"`, in addition to the rest of the fields usually
|
191
|
+
created by syslogstash. Note that nested fields, and value types other
|
192
|
+
than strings, are not supported. Also, if you specify a field name also
|
193
|
+
used by syslogstash, the results are explicitly undefined.
|
194
|
+
|
195
|
+
* **`RELAY_SOCKETS`** (optional; default `""`) -- on the off-chance you want
|
196
|
+
to feed the syslog messages that syslogstash receives to another
|
197
|
+
syslog-compatible consumer (say, an old-school syslogd) you can specify
|
198
|
+
additional filenames to use here. Multiple socket filenames can be
|
199
|
+
specified by separating each file name with a colon. Syslogstash will open
|
200
|
+
each of the specified sockets, if they exist, and write each received
|
201
|
+
message to the socket. If the socket does not exist, or the open or write
|
202
|
+
operations fail, syslogstash **will not** retry.
|
88
203
|
|
89
204
|
|
90
205
|
# Contributing
|
@@ -100,7 +215,7 @@ request](https://github.com/discourse/syslogstash/pulls].
|
|
100
215
|
Unless otherwise stated, everything in this repo is covered by the following
|
101
216
|
copyright notice:
|
102
217
|
|
103
|
-
Copyright (C) 2015 Civilized Discourse Construction Kit Inc.
|
218
|
+
Copyright (C) 2015, 2018 Civilized Discourse Construction Kit Inc.
|
104
219
|
|
105
220
|
This program is free software: you can redistribute it and/or modify it
|
106
221
|
under the terms of the GNU General Public License version 3, as
|
data/bin/syslogstash
CHANGED
@@ -1,51 +1,56 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'syslogstash'
|
4
|
-
require '
|
4
|
+
require 'logger'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
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
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
unless File.exist?(ARGV[0])
|
18
|
-
$stderr.puts "Config file #{ARGV[0]} does not exist"
|
19
|
-
exit 1
|
10
|
+
begin
|
11
|
+
cfg = Syslogstash::Config.new(ENV, logger: logger)
|
12
|
+
rescue Syslogstash::Config::ConfigurationError => ex
|
13
|
+
$stderr.puts "Error in configuration: #{ex.message}"
|
14
|
+
exit 1
|
20
15
|
end
|
21
16
|
|
22
|
-
|
23
|
-
$stderr.puts "Config file #{ARGV[0]} not readable"
|
24
|
-
exit 1
|
25
|
-
end
|
17
|
+
syslogstash = Syslogstash.new(cfg)
|
26
18
|
|
27
|
-
|
19
|
+
sig_r, sig_w = IO.pipe
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
|
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'
|
32
29
|
end
|
33
30
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
31
|
+
Thread.new do
|
32
|
+
loop do
|
33
|
+
begin
|
34
|
+
c = sig_r.getc
|
35
|
+
if c == '1'
|
36
|
+
logger.level -= 1 unless logger.level == Logger::DEBUG
|
37
|
+
logger.info("SignalHandler") { "Received SIGUSR1; log level is now #{Logger::SEV_LABEL[logger.level]}." }
|
38
|
+
elsif c == '2'
|
39
|
+
logger.level += 1 unless logger.level == Logger::ERROR
|
40
|
+
logger.info("SignalHandler") { "Received SIGUSR2; log level is now #{Logger::SEV_LABEL[logger.level]}." }
|
41
|
+
elsif c == 'U'
|
42
|
+
cfg.relay_to_stdout = !cfg.relay_to_stdout
|
43
|
+
logger.info("SignalHandler") { "Received SIGURG; Relaying to stdout is now #{cfg.relay_to_stdout ? "enabled" : "disabled"}" }
|
44
|
+
else
|
45
|
+
logger.error("SignalHandler") { "Got an unrecognised character from signal pipe: #{c.inspect}" }
|
46
|
+
end
|
47
|
+
rescue StandardError => ex
|
48
|
+
logger.error("SignalHandler") { (["Exception raised: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
49
|
+
rescue Exception => ex
|
50
|
+
$stderr.puts (["Fatal exception in syslogstash signal handler: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ")
|
51
|
+
exit 42
|
52
|
+
end
|
53
|
+
end
|
49
54
|
end
|
50
55
|
|
51
|
-
|
56
|
+
syslogstash.run
|
data/lib/syslogstash.rb
CHANGED
@@ -7,44 +7,45 @@ require 'thwait'
|
|
7
7
|
# server.
|
8
8
|
#
|
9
9
|
class Syslogstash
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
|
13
|
-
@writer = LogstashWriter.new(
|
14
|
-
|
15
|
-
@
|
10
|
+
def initialize(cfg)
|
11
|
+
@cfg = cfg
|
12
|
+
@stats = PrometheusExporter.new(cfg)
|
13
|
+
@writer = LogstashWriter.new(cfg, @stats)
|
14
|
+
@reader = SyslogReader.new(cfg, @writer, @stats)
|
15
|
+
@logger = cfg.logger
|
16
16
|
end
|
17
17
|
|
18
18
|
def run
|
19
|
-
@
|
20
|
-
|
21
|
-
|
19
|
+
if @cfg.stats_server
|
20
|
+
@logger.debug("main") { "Running stats server" }
|
21
|
+
@stats.run
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
+
@writer.run
|
25
|
+
@reader.run
|
24
26
|
|
25
|
-
dead_thread =
|
27
|
+
dead_thread = ThreadsWait.new(@reader.thread, @writer.thread).next_wait
|
26
28
|
|
27
29
|
if dead_thread == @writer.thread
|
28
|
-
|
29
|
-
elsif dead_thread == @
|
30
|
-
|
30
|
+
@logger.error("main") { "Writer thread crashed." }
|
31
|
+
elsif dead_thread == @reader.thread
|
32
|
+
@logger.error("main") { "Reader thread crashed." }
|
31
33
|
else
|
32
|
-
|
33
|
-
|
34
|
-
$stderr.puts "[Syslogstash] Reader thread for #{reader.file} crashed."
|
34
|
+
@logger.fatal("main") { "ThreadsWait#next_wait returned unexpected value #{dead_thread.inspect}" }
|
35
|
+
exit 1
|
35
36
|
end
|
36
37
|
|
37
38
|
begin
|
38
39
|
dead_thread.join
|
39
40
|
rescue Exception => ex
|
40
|
-
|
41
|
-
$stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
|
41
|
+
@logger.error("main") { (["Exception in crashed thread was: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
42
42
|
end
|
43
43
|
|
44
44
|
exit 1
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
require_relative 'syslogstash/config'
|
48
49
|
require_relative 'syslogstash/syslog_reader'
|
49
50
|
require_relative 'syslogstash/logstash_writer'
|
50
51
|
require_relative 'syslogstash/prometheus_exporter'
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
class Syslogstash::Config
|
4
|
+
class ConfigurationError < StandardError; end
|
5
|
+
|
6
|
+
# Raised if any problems were found with the config
|
7
|
+
class InvalidEnvironmentError < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :logstash_server,
|
10
|
+
:syslog_socket,
|
11
|
+
:backlog_size,
|
12
|
+
:stats_server,
|
13
|
+
:add_fields,
|
14
|
+
:relay_sockets
|
15
|
+
|
16
|
+
attr_reader :logger
|
17
|
+
|
18
|
+
attr_accessor :relay_to_stdout
|
19
|
+
|
20
|
+
# Create a new syslogstash config based on environment variables.
|
21
|
+
#
|
22
|
+
# Examines the environment passed in, and then creates a new config
|
23
|
+
# object if all is well.
|
24
|
+
#
|
25
|
+
# @param env [Hash] the set of environment variables to use.
|
26
|
+
#
|
27
|
+
# @param logger [Logger] the logger to which all diagnostic and error
|
28
|
+
# data will be sent.
|
29
|
+
#
|
30
|
+
# @raise [ConfigurationError] if any problems are detected with the
|
31
|
+
# environment variables found.
|
32
|
+
#
|
33
|
+
def initialize(env, logger:)
|
34
|
+
@logger = logger
|
35
|
+
|
36
|
+
parse_env(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def parse_env(env)
|
42
|
+
@logger.info("config") { "Parsing environment:\n" + env.map { |k, v| "#{k}=#{v.inspect}" }.join("\n") }
|
43
|
+
|
44
|
+
@logstash_server = pluck_string(env, "LOGSTASH_SERVER")
|
45
|
+
@syslog_socket = pluck_string(env, "SYSLOG_SOCKET")
|
46
|
+
@relay_to_stdout = pluck_boolean(env, "RELAY_TO_STDOUT", default: false)
|
47
|
+
@stats_server = pluck_boolean(env, "STATS_SERVER", default: false)
|
48
|
+
@backlog_size = pluck_integer(env, "BACKLOG_SIZE", valid_range: 0..(2**31 - 1), default: 1_000_000)
|
49
|
+
@add_fields = pluck_prefix_list(env, "ADD_FIELD_")
|
50
|
+
@relay_sockets = pluck_path_list(env, "RELAY_SOCKETS", default: [])
|
51
|
+
end
|
52
|
+
|
53
|
+
def pluck_string(env, key, default: nil)
|
54
|
+
maybe_default(env, key, default) { env[key] }
|
55
|
+
end
|
56
|
+
|
57
|
+
def pluck_boolean(env, key, default: nil)
|
58
|
+
maybe_default(env, key, default) do
|
59
|
+
case env[key]
|
60
|
+
when /\A(no|off|0|false)\z/
|
61
|
+
false
|
62
|
+
when /\A(yes|on|1|true)\z/
|
63
|
+
true
|
64
|
+
else
|
65
|
+
raise ConfigurationError,
|
66
|
+
"Value for #{key} (#{env[key].inspect}) is not a valid boolean"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def pluck_integer(env, key, valid_range: nil, default: nil)
|
72
|
+
maybe_default(env, key, default) do
|
73
|
+
if env[key] !~ /\A\d+\z/
|
74
|
+
raise InvalidEnvironmentError,
|
75
|
+
"Value for #{key} (#{env[key].inspect}) is not an integer"
|
76
|
+
end
|
77
|
+
|
78
|
+
env[key].to_i.tap do |v|
|
79
|
+
unless valid_range.nil? || !valid_range.include?(v)
|
80
|
+
raise InvalidEnvironmentError,
|
81
|
+
"Value for #{key} (#{env[key]}) out of range (must be between #{valid_range.first} and #{valid_range.last} inclusive)"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def pluck_prefix_list(env, prefix)
|
88
|
+
{}.tap do |list|
|
89
|
+
env.each do |k, v|
|
90
|
+
next unless k.start_with? prefix
|
91
|
+
key = k.sub(prefix, '')
|
92
|
+
list[key] = v
|
93
|
+
end
|
94
|
+
|
95
|
+
@logger.debug("config") { "Prefix list for #{prefix.inspect} is #{list.inspect}" }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def pluck_path_list(env, key, default: nil)
|
100
|
+
maybe_default(env, key, default) do
|
101
|
+
env[key].split(":")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def maybe_default(env, key, default)
|
106
|
+
if env[key].nil? || env[key].empty?
|
107
|
+
if default.nil?
|
108
|
+
raise ConfigurationError,
|
109
|
+
"Required environment variable #{key} not specified"
|
110
|
+
else
|
111
|
+
@logger.debug("config") { "Using default value #{default.inspect} for config parameter #{key}" }
|
112
|
+
default
|
113
|
+
end
|
114
|
+
else
|
115
|
+
yield.tap { |v| @logger.debug("config") { "Using plucked value #{v.inspect} for config parameter #{key}" } }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -1,22 +1,21 @@
|
|
1
|
-
|
1
|
+
require 'resolv'
|
2
|
+
require 'ipaddr'
|
2
3
|
|
3
|
-
# Write messages to
|
4
|
+
# Write messages to a logstash server.
|
4
5
|
#
|
5
6
|
class Syslogstash::LogstashWriter
|
6
|
-
|
7
|
+
Target = Struct.new(:hostname, :port)
|
8
|
+
|
9
|
+
attr_reader :thread
|
7
10
|
|
8
11
|
# Create a new logstash writer.
|
9
12
|
#
|
10
|
-
#
|
11
|
-
# No messages will actually be *delivered
|
13
|
+
# Once the object is created, you're ready to give it messages by
|
14
|
+
# calling #send_entry. No messages will actually be *delivered* to
|
15
|
+
# logstash, though, until you call #run.
|
12
16
|
#
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
|
16
|
-
unless @servers.all? { |url| url.scheme == 'tcp' }
|
17
|
-
raise ArgumentError,
|
18
|
-
"Unsupported URL scheme: #{@servers.select { |url| url.scheme != 'tcp' }.join(', ')}"
|
19
|
-
end
|
17
|
+
def initialize(cfg, stats)
|
18
|
+
@server_name, @logger, @backlog, @stats = cfg.logstash_server, cfg.logger, cfg.backlog_size, stats
|
20
19
|
|
21
20
|
@entries = []
|
22
21
|
@entries_mutex = Mutex.new
|
@@ -31,18 +30,19 @@ class Syslogstash::LogstashWriter
|
|
31
30
|
@entries << { content: e, arrival_timestamp: Time.now }
|
32
31
|
while @entries.length > @backlog
|
33
32
|
@entries.shift
|
34
|
-
@
|
33
|
+
@stats.dropped
|
35
34
|
end
|
36
35
|
end
|
37
|
-
|
36
|
+
|
37
|
+
@thread.run if @thread
|
38
38
|
end
|
39
39
|
|
40
40
|
# Start sending messages to logstash servers. This method will return
|
41
41
|
# almost immediately, and actual message sending will occur in a
|
42
|
-
# separate
|
42
|
+
# separate thread.
|
43
43
|
#
|
44
44
|
def run
|
45
|
-
@
|
45
|
+
@thread = Thread.new { send_messages }
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
@@ -57,16 +57,14 @@ class Syslogstash::LogstashWriter
|
|
57
57
|
|
58
58
|
current_server do |s|
|
59
59
|
s.puts entry[:content]
|
60
|
+
@stats.sent(server_id(s), entry[:arrival_timestamp])
|
60
61
|
end
|
61
62
|
|
62
|
-
@metrics.sent(@servers.last, entry[:arrival_timestamp])
|
63
|
-
|
64
63
|
# If we got here, we sent successfully, so we don't want
|
65
64
|
# to put the entry back on the queue in the ensure block
|
66
65
|
entry = nil
|
67
66
|
rescue StandardError => ex
|
68
|
-
|
69
|
-
$stderr.puts ex.backtrace.map { |l| " #{l}" }.join("\n")
|
67
|
+
@logger.error("writer") { (["Unhandled exception while writing entry: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
70
68
|
ensure
|
71
69
|
@entries_mutex.synchronize { @entries.unshift if entry }
|
72
70
|
end
|
@@ -91,30 +89,100 @@ class Syslogstash::LogstashWriter
|
|
91
89
|
until done
|
92
90
|
if @current_server
|
93
91
|
begin
|
94
|
-
debug { "Using current server" }
|
92
|
+
@logger.debug("writer") { "Using current server #{server_id(@current_server)}" }
|
95
93
|
yield @current_server
|
96
94
|
done = true
|
97
95
|
rescue SystemCallError => ex
|
98
96
|
# Something went wrong during the send; disconnect from this
|
99
97
|
# server and recycle
|
100
|
-
debug { "Error while writing to current server: #{ex.message} (#{ex.class})" }
|
98
|
+
@logger.debug("writer") { "Error while writing to current server: #{ex.message} (#{ex.class})" }
|
101
99
|
@current_server.close
|
102
100
|
@current_server = nil
|
103
101
|
sleep 0.1
|
104
102
|
end
|
105
103
|
else
|
104
|
+
candidates = resolve_server_name
|
105
|
+
|
106
106
|
begin
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
107
|
+
next_server = candidates.shift
|
108
|
+
|
109
|
+
if next_server
|
110
|
+
@logger.debug("writer") { "Trying to connect to #{next_server.to_s}" }
|
111
|
+
@current_server = TCPSocket.new(next_server.hostname, next_server.port)
|
112
|
+
else
|
113
|
+
@logger.debug("writer") { "Could not connect to any server; pausing before trying again" }
|
114
|
+
@current_server = nil
|
115
|
+
sleep 5
|
116
|
+
end
|
111
117
|
rescue SystemCallError => ex
|
112
|
-
# Connection failed for any number of reasons; try
|
113
|
-
|
118
|
+
# Connection failed for any number of reasons; try the next one in the list
|
119
|
+
@logger.warn("writer") { "Failed to connect to #{next_server.to_s}: #{ex.message} (#{ex.class})" }
|
114
120
|
sleep 0.1
|
115
121
|
retry
|
116
122
|
end
|
117
123
|
end
|
118
124
|
end
|
119
125
|
end
|
126
|
+
|
127
|
+
def server_id(s)
|
128
|
+
pa = s.peeraddr
|
129
|
+
if pa[0] == "AF_INET6"
|
130
|
+
"[#{pa[3]}]:#{pa[1]}"
|
131
|
+
else
|
132
|
+
"#{pa[3]}:#{pa[1]}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def resolve_server_name
|
137
|
+
return [static_target] if static_target
|
138
|
+
|
139
|
+
# The IPv6 literal case should have been taken care of by
|
140
|
+
# static_target, so the only two cases we have to deal with
|
141
|
+
# here are specified-port (assume A/AAAA) or no port (assume SRV).
|
142
|
+
if @server_name =~ /:/
|
143
|
+
host, port = @server_name.split(":", 2)
|
144
|
+
addrs = Resolv::DNS.new.getaddresses(host)
|
145
|
+
if addrs.empty?
|
146
|
+
@logger.warn("writer") { "No addresses resolved for server_name #{host.inspect}" }
|
147
|
+
end
|
148
|
+
addrs.map { |a| Target.new(a.to_s, port.to_i) }
|
149
|
+
else
|
150
|
+
# SRV records ftw
|
151
|
+
[].tap do |list|
|
152
|
+
left = Resolv::DNS.new.getresources(@server_name, Resolv::DNS::Resource::IN::SRV)
|
153
|
+
if left.empty?
|
154
|
+
@logger.warn("writer") { "No SRV records found for server_name #{@server_name.inspect}" }
|
155
|
+
end
|
156
|
+
until left.empty?
|
157
|
+
prio = left.map { |rr| rr.priority }.uniq.min
|
158
|
+
candidates = left.select { |rr| rr.priority == prio }
|
159
|
+
left -= candidates
|
160
|
+
candidates.sort_by! { |rr| [rr.weight, rr.target.to_s] }
|
161
|
+
until candidates.empty?
|
162
|
+
selector = rand(candidates.inject(1) { |n, rr| n + rr.weight })
|
163
|
+
chosen = candidates.inject(0) do |n, rr|
|
164
|
+
break rr if n + rr.weight >= selector
|
165
|
+
n + rr.weight
|
166
|
+
end
|
167
|
+
candidates.delete(chosen)
|
168
|
+
list << Target.new(chosen.target.to_s, chosen.port)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def static_target
|
176
|
+
@static_target ||= begin
|
177
|
+
if @server_name =~ /\A(.*):(\d+)\z/
|
178
|
+
begin
|
179
|
+
Target.new(IPAddr.new($1).to_s, $2.to_i)
|
180
|
+
rescue ArgumentError
|
181
|
+
# Whatever is on the LHS isn't a recognisable address;
|
182
|
+
# assume hostname and continue
|
183
|
+
nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
120
188
|
end
|
@@ -1,40 +1,33 @@
|
|
1
|
-
require '
|
2
|
-
require 'prometheus/middleware/exporter'
|
3
|
-
require 'rack/handler/webrick'
|
1
|
+
require 'frankenstein/server'
|
4
2
|
require 'logger'
|
5
3
|
|
6
4
|
class Syslogstash::PrometheusExporter
|
7
5
|
attr_reader :thread
|
8
6
|
|
9
|
-
def initialize
|
10
|
-
@
|
11
|
-
|
12
|
-
@
|
13
|
-
@
|
7
|
+
def initialize(cfg)
|
8
|
+
@stats_server = Frankenstein::Server.new(port: 9159, logger: cfg.logger, metrics_prefix: "syslogstash_server")
|
9
|
+
|
10
|
+
@msg_in = prom.counter(:syslogstash_messages_received_total, "The number of syslog messages received from the log socket")
|
11
|
+
@msg_out = prom.counter(:syslogstash_messages_sent_total, "The number of logstash messages sent to each logstash server")
|
12
|
+
@lag = prom.gauge(:syslogstash_last_relayed_message_timestamp, "When the last message that was successfully relayed to logstash was originally received")
|
13
|
+
@queue = prom.gauge(:syslogstash_queue_size, "How many messages are currently in the queue to be sent")
|
14
|
+
@dropped = prom.counter(:syslogstash_messages_dropped, "How many messages have been dropped from the backlog queue")
|
15
|
+
|
14
16
|
@q_mutex = Mutex.new
|
15
|
-
|
17
|
+
|
18
|
+
@lag.set({}, 0)
|
19
|
+
@queue.set({}, 0)
|
16
20
|
end
|
17
21
|
|
18
|
-
def received(socket
|
22
|
+
def received(socket)
|
19
23
|
@msg_in.increment(socket_path: socket)
|
20
|
-
@q_mutex.synchronize { @queue.set({},
|
21
|
-
|
22
|
-
if @most_recent_received.nil? || @most_recent_received < stamp
|
23
|
-
@most_recent_received = stamp
|
24
|
-
|
25
|
-
refresh_lag
|
26
|
-
end
|
24
|
+
@q_mutex.synchronize { @queue.set({}, @queue.get({}) + 1) }
|
27
25
|
end
|
28
26
|
|
29
27
|
def sent(server, stamp)
|
30
28
|
@msg_out.increment(logstash_server: server)
|
31
29
|
@q_mutex.synchronize { @queue.set({}, @queue.get({}) - 1) }
|
32
|
-
|
33
|
-
if @most_recent_sent.nil? || @most_recent_sent < stamp
|
34
|
-
@most_recent_sent = stamp
|
35
|
-
|
36
|
-
refresh_lag
|
37
|
-
end
|
30
|
+
@lag.set({}, stamp.to_f)
|
38
31
|
end
|
39
32
|
|
40
33
|
def dropped
|
@@ -43,28 +36,12 @@ class Syslogstash::PrometheusExporter
|
|
43
36
|
end
|
44
37
|
|
45
38
|
def run
|
46
|
-
@
|
47
|
-
app = Rack::Builder.new
|
48
|
-
app.use Prometheus::Middleware::Exporter
|
49
|
-
app.run ->(env) { [404, {'Content-Type' => 'text/plain'}, ['Nope']] }
|
50
|
-
|
51
|
-
logger = Logger.new($stderr)
|
52
|
-
logger.level = Logger::INFO
|
53
|
-
logger.formatter = proc { |s, t, p, m| "[Syslogstash::PrometheusExporter::WEBrick] #{m}\n" }
|
54
|
-
|
55
|
-
Rack::Handler::WEBrick.run app, Host: '::', Port: 9159, Logger: logger, AccessLog: []
|
56
|
-
end
|
39
|
+
@stats_server.run
|
57
40
|
end
|
58
41
|
|
59
42
|
private
|
60
43
|
|
61
44
|
def prom
|
62
|
-
|
63
|
-
end
|
64
|
-
|
65
|
-
def refresh_lag
|
66
|
-
if @most_recent_received && @most_recent_sent
|
67
|
-
@lag.set({}, ((@most_recent_received.to_f - @most_recent_sent.to_f) * 1000).to_i)
|
68
|
-
end
|
45
|
+
@stats_server.registry
|
69
46
|
end
|
70
47
|
end
|
@@ -1,30 +1,15 @@
|
|
1
|
-
require_relative 'worker'
|
2
|
-
|
3
1
|
# A single socket reader.
|
4
2
|
#
|
5
3
|
class Syslogstash::SyslogReader
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :file
|
9
|
-
|
10
|
-
def initialize(file, config, logstash, metrics)
|
11
|
-
@file, @logstash, @metrics = file, logstash, metrics
|
12
|
-
config ||= {}
|
4
|
+
attr_reader :thread
|
13
5
|
|
14
|
-
|
15
|
-
@
|
16
|
-
|
17
|
-
unless @add_fields.is_a? Hash
|
18
|
-
raise ArgumentError,
|
19
|
-
"add_fields parameter to socket #{file} must be a hash"
|
20
|
-
end
|
6
|
+
def initialize(cfg, logstash, stats)
|
7
|
+
@file, @logstash, @stats = cfg.syslog_socket, logstash, stats
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
log { "initialized syslog socket #{file} with config #{config.inspect}" }
|
9
|
+
@add_fields = cfg.add_fields
|
10
|
+
@relay_to = cfg.relay_sockets
|
11
|
+
@cfg = cfg
|
12
|
+
@logger = cfg.logger
|
28
13
|
end
|
29
14
|
|
30
15
|
# Start reading from the socket file, parsing entries, and flinging
|
@@ -32,33 +17,32 @@ class Syslogstash::SyslogReader
|
|
32
17
|
# continuing in a separate thread.
|
33
18
|
#
|
34
19
|
def run
|
35
|
-
debug { "#run called" }
|
20
|
+
@logger.debug("reader") { "#run called" }
|
36
21
|
|
37
22
|
begin
|
38
23
|
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
|
39
24
|
socket.bind(Socket.pack_sockaddr_un(@file))
|
40
25
|
File.chmod(0666, @file)
|
41
26
|
rescue Errno::EEXIST, Errno::EADDRINUSE
|
42
|
-
|
27
|
+
@logger.info("reader") { "socket file #{@file} already exists; deleting" }
|
43
28
|
File.unlink(@file) rescue nil
|
44
29
|
retry
|
45
|
-
rescue
|
46
|
-
|
47
|
-
raise
|
30
|
+
rescue StandardError => ex
|
31
|
+
raise ex.class, "Error while trying to bind to #{@file}: #{ex.message}", ex.backtrace
|
48
32
|
end
|
49
33
|
|
50
|
-
@
|
34
|
+
@thread = Thread.new do
|
51
35
|
begin
|
52
36
|
loop do
|
53
37
|
msg = socket.recvmsg
|
54
|
-
debug { "Message received: #{msg.inspect}" }
|
55
|
-
@
|
56
|
-
process_message msg.first.chomp
|
38
|
+
@logger.debug("reader") { "Message received: #{msg.inspect}" }
|
39
|
+
@stats.received(@file)
|
57
40
|
relay_message msg.first
|
41
|
+
process_message msg.first.chomp
|
58
42
|
end
|
59
43
|
ensure
|
60
44
|
socket.close
|
61
|
-
|
45
|
+
@logger.debug("reader") { "removing socket file #{@file}" }
|
62
46
|
File.unlink(@file) rescue nil
|
63
47
|
end
|
64
48
|
end
|
@@ -103,7 +87,7 @@ class Syslogstash::SyslogReader
|
|
103
87
|
|
104
88
|
@logstash.send_entry(log_entry)
|
105
89
|
else
|
106
|
-
|
90
|
+
@logger.warn("reader") { "Unparseable message: #{msg.inspect}" }
|
107
91
|
end
|
108
92
|
end
|
109
93
|
|
@@ -118,13 +102,19 @@ class Syslogstash::SyslogReader
|
|
118
102
|
e.merge!(h.delete_if { |k,v| v.nil? })
|
119
103
|
e.merge!(@add_fields)
|
120
104
|
|
121
|
-
debug { "
|
105
|
+
@logger.debug("reader") { "Complete log entry is: #{e.inspect}" }
|
122
106
|
end
|
123
107
|
end
|
124
108
|
|
125
109
|
def relay_message(msg)
|
126
110
|
@currently_failed ||= {}
|
127
111
|
|
112
|
+
if @cfg.relay_to_stdout
|
113
|
+
# This one's easy
|
114
|
+
puts msg.sub(/\A<\d+>/, '')
|
115
|
+
$stdout.flush
|
116
|
+
end
|
117
|
+
|
128
118
|
@relay_to.each do |f|
|
129
119
|
s = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
|
130
120
|
begin
|
@@ -133,25 +123,34 @@ class Syslogstash::SyslogReader
|
|
133
123
|
# Socket doesn't exist; we don't care enough about this to bother
|
134
124
|
# reporting it. People will figure it out themselves soon enough.
|
135
125
|
rescue StandardError => ex
|
136
|
-
|
126
|
+
unless @currently_failed[f]
|
127
|
+
@logger.warn("reader") { "Error while connecting to relay socket #{f}: #{ex.message} (#{ex.class})" }
|
128
|
+
@currently_failed[f] = true
|
129
|
+
end
|
137
130
|
next
|
138
131
|
end
|
139
132
|
|
140
133
|
begin
|
134
|
+
# We really, *really* don't want to block the world just because
|
135
|
+
# whoever's on the other end of the relay socket can't process
|
136
|
+
# messages quick enough.
|
141
137
|
s.sendmsg_nonblock(msg)
|
142
138
|
if @currently_failed[f]
|
143
|
-
|
139
|
+
@logger.info("reader") { "Error on socket #{f} has cleared; messages are being delivered again" }
|
144
140
|
@currently_failed[f] = false
|
145
141
|
end
|
146
142
|
rescue Errno::ENOTCONN
|
147
|
-
|
143
|
+
unless @currently_failed[f]
|
144
|
+
@logger.debug("reader") { "Nothing is listening on socket #{f}" }
|
145
|
+
@currently_failed[f] = true
|
146
|
+
end
|
148
147
|
rescue IO::EAGAINWaitWritable
|
149
148
|
unless @currently_failed[f]
|
150
|
-
|
149
|
+
@logger.warn("reader") { "Socket #{f} is currently backlogged; messages to this socket are now being discarded undelivered" }
|
151
150
|
@currently_failed[f] = true
|
152
151
|
end
|
153
152
|
rescue StandardError => ex
|
154
|
-
|
153
|
+
@logger.warn("reader") { (["Failed to relay message to socket #{f} from #{@file}: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
155
154
|
end
|
156
155
|
end
|
157
156
|
end
|
data/syslogstash.gemspec
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syslogstash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Palmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: frankenstein
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,14 +193,16 @@ extensions: []
|
|
193
193
|
extra_rdoc_files: []
|
194
194
|
files:
|
195
195
|
- ".gitignore"
|
196
|
+
- Dockerfile
|
196
197
|
- LICENCE
|
198
|
+
- Makefile
|
197
199
|
- README.md
|
198
200
|
- bin/syslogstash
|
199
201
|
- lib/syslogstash.rb
|
202
|
+
- lib/syslogstash/config.rb
|
200
203
|
- lib/syslogstash/logstash_writer.rb
|
201
204
|
- lib/syslogstash/prometheus_exporter.rb
|
202
205
|
- lib/syslogstash/syslog_reader.rb
|
203
|
-
- lib/syslogstash/worker.rb
|
204
206
|
- syslogstash.gemspec
|
205
207
|
homepage: https://github.com/discourse/syslogstash
|
206
208
|
licenses: []
|
data/lib/syslogstash/worker.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# Common code shared between both readers and writers.
|
2
|
-
#
|
3
|
-
module Syslogstash::Worker
|
4
|
-
# If you ever want to stop a reader, here's how.
|
5
|
-
def stop
|
6
|
-
if @worker
|
7
|
-
@worker.kill
|
8
|
-
@worker.join
|
9
|
-
@worker = nil
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def thread
|
14
|
-
@worker
|
15
|
-
end
|
16
|
-
|
17
|
-
# If you want to wait for a reader to die, here's how.
|
18
|
-
#
|
19
|
-
def wait
|
20
|
-
@worker.join
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def log
|
26
|
-
$stderr.puts "[#{self.class}] #{yield.to_s}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def debug
|
30
|
-
if ENV['DEBUG_SYSLOGSTASH']
|
31
|
-
$stderr.puts "[#{self.class}] #{yield.to_s}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|