logstash_writer 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/logstash_writer.rb +68 -40
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b3f279724a1874b171c9080fb4aae626e844c13b845bec88a50ae832b660a85
|
4
|
+
data.tar.gz: cb3463bfddf9673808fb874cc1bca6b1b51527f2eb626134bada7cacff91b856
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41611118bbcc2992196a8c2dc49bec74ce00dc3ec0505b9682132c9e6bcf6e7a8dd48355f35f06cd393f8e2c41460a24b28c53b0c0b0f5ee48c8ec6ec9964503
|
7
|
+
data.tar.gz: 5527ef9d6bf2a9ed3de0c3483bf43ed6ffd5e69a4e7b8687bdb9d33d258651bdae95a9b990617007578fb10003533840b6f397d87fa2e955836704f9eb064955
|
data/README.md
CHANGED
@@ -146,9 +146,9 @@ The metrics that are exposed are:
|
|
146
146
|
|
147
147
|
* **`logstash_writer_connected_to_server`** -- this flag timeseries (can be
|
148
148
|
either `1` or `0`) is simply a way for you to quickly determine whether
|
149
|
-
the writer has a server to talk to, if it wants one
|
150
|
-
series will only be `0` if there's an
|
151
|
-
server can be found to write it to.
|
149
|
+
the writer has a server to talk to, if it wants one, and which server it
|
150
|
+
is connected to. That is, this time series will only be `0` if there's an
|
151
|
+
event to write but no logstash server can be found to write it to.
|
152
152
|
|
153
153
|
* **`logstash_writer_connect_exceptions_total`** -- a count of exceptions
|
154
154
|
raised whilst attempting to connect to a logstash server, labelled by the
|
data/lib/logstash_writer.rb
CHANGED
@@ -183,7 +183,7 @@ class LogstashWriter
|
|
183
183
|
@logger.error("LogstashWriter") { (["Worker thread terminated with exception: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
184
184
|
end
|
185
185
|
@worker_thread = nil
|
186
|
-
@socket_mutex.synchronize { (@
|
186
|
+
@socket_mutex.synchronize { (@current_target.close; @current_target = nil) if @current_target }
|
187
187
|
end
|
188
188
|
end
|
189
189
|
|
@@ -201,11 +201,11 @@ class LogstashWriter
|
|
201
201
|
#
|
202
202
|
def force_disconnect!
|
203
203
|
@socket_mutex.synchronize do
|
204
|
-
return if @
|
204
|
+
return if @current_target.nil?
|
205
205
|
|
206
|
-
@logger.info("LogstashWriter") { "Forced disconnect from #{describe_peer
|
207
|
-
@
|
208
|
-
@
|
206
|
+
@logger.info("LogstashWriter") { "Forced disconnect from #{@current_target.describe_peer}" }
|
207
|
+
@current_target.close
|
208
|
+
@current_target = nil
|
209
209
|
end
|
210
210
|
|
211
211
|
nil
|
@@ -237,20 +237,14 @@ class LogstashWriter
|
|
237
237
|
event = @queue.shift
|
238
238
|
end
|
239
239
|
|
240
|
-
|
241
|
-
|
242
|
-
stat_sent(
|
240
|
+
current_target do |t|
|
241
|
+
t.socket.puts event[:content].to_json
|
242
|
+
stat_sent(t.to_s, event[:arrival_timestamp])
|
243
243
|
@metrics[:write_loop_ok].set({}, 1)
|
244
244
|
error_wait = INITIAL_RETRY_WAIT
|
245
245
|
end
|
246
246
|
rescue StandardError => ex
|
247
247
|
@logger.error("LogstashWriter") { (["Exception in write_loop: #{ex.message} (#{ex.class})"] + ex.backtrace).join("\n ") }
|
248
|
-
# If there was some sort of error, there's a non-trivial chance the
|
249
|
-
# socket has gone *boom*, so let's invalidate it and go around again
|
250
|
-
if @current_socket
|
251
|
-
@current_socket.close
|
252
|
-
@current_socket = nil
|
253
|
-
end
|
254
248
|
@queue_mutex.synchronize { @queue.unshift(event) if event }
|
255
249
|
@metrics[:write_loop_exception].increment(class: ex.class.to_s)
|
256
250
|
@metrics[:write_loop_ok].set({}, 0)
|
@@ -265,14 +259,14 @@ class LogstashWriter
|
|
265
259
|
end
|
266
260
|
end
|
267
261
|
|
268
|
-
# Yield a
|
262
|
+
# Yield a Target connected to the server we currently believe to be
|
269
263
|
# accepting log entries, so that something can send log entries to it.
|
270
264
|
#
|
271
265
|
# The yielding allows us to centralise all error detection and handling
|
272
266
|
# within this one method, and retry sending just by calling `yield` again
|
273
267
|
# when we've connected to another server.
|
274
268
|
#
|
275
|
-
def
|
269
|
+
def current_target
|
276
270
|
# This could all be handled more cleanly with recursion, but I don't
|
277
271
|
# want to fill the stack if we have to retry a lot of times. Also
|
278
272
|
# can't just use `retry` because not all of the "go around again"
|
@@ -281,19 +275,25 @@ class LogstashWriter
|
|
281
275
|
|
282
276
|
until done
|
283
277
|
@socket_mutex.synchronize do
|
284
|
-
if @
|
278
|
+
if @current_target
|
285
279
|
begin
|
286
|
-
|
287
|
-
|
280
|
+
# Check that our socket is still good to go; if we don't do this,
|
281
|
+
# the other end can disconnect, and because we're never normally
|
282
|
+
# reading from the socket, we never get the EOFError that normally
|
283
|
+
# results, and so the socket remains in CLOSE_WAIT state *forever*.
|
284
|
+
@current_target.socket.read_nonblock(1)
|
285
|
+
|
286
|
+
yield @current_target
|
287
|
+
@metrics[:connected].set({ server: @current_target.describe_peer }, 1)
|
288
288
|
done = true
|
289
|
-
rescue SystemCallError => ex
|
289
|
+
rescue SystemCallError, IOError => ex
|
290
290
|
# Something went wrong during the send; disconnect from this
|
291
291
|
# server and recycle
|
292
|
-
@metrics[:write_exception].increment(server: describe_peer
|
293
|
-
@
|
294
|
-
@
|
295
|
-
@
|
296
|
-
@
|
292
|
+
@metrics[:write_exception].increment(server: @current_target.describe_peer, class: ex.class.to_s)
|
293
|
+
@metrics[:connected].set({ server: @current_target.describe_peer }, 0)
|
294
|
+
@logger.info("LogstashWriter") { "Error while writing to current server #{@current_target.describe_peer}: #{ex.message} (#{ex.class})" }
|
295
|
+
@current_target.close
|
296
|
+
@current_target = nil
|
297
297
|
|
298
298
|
sleep INITIAL_RETRY_WAIT
|
299
299
|
end
|
@@ -313,10 +313,12 @@ class LogstashWriter
|
|
313
313
|
|
314
314
|
if next_server
|
315
315
|
@logger.debug("LogstashWriter") { "Trying to connect to #{next_server.to_s}" }
|
316
|
-
@
|
316
|
+
@current_target = next_server
|
317
|
+
# Trigger a connection attempt
|
318
|
+
@current_target.socket
|
317
319
|
else
|
318
320
|
@logger.debug("LogstashWriter") { "Could not connect to any server; pausing before trying again" }
|
319
|
-
@
|
321
|
+
@current_target = nil
|
320
322
|
sleep retry_delay
|
321
323
|
|
322
324
|
# Calculate a longer retry delay next time we fail to connect
|
@@ -341,18 +343,6 @@ class LogstashWriter
|
|
341
343
|
end
|
342
344
|
end
|
343
345
|
|
344
|
-
# Generate a human-readable description of the remote end of the given
|
345
|
-
# socket.
|
346
|
-
#
|
347
|
-
def describe_peer(s)
|
348
|
-
pa = s.peeraddr
|
349
|
-
if pa[0] == "AF_INET6"
|
350
|
-
"[#{pa[3]}]:#{pa[1]}"
|
351
|
-
else
|
352
|
-
"#{pa[3]}:#{pa[1]}"
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
346
|
# Turn the server_name given in the constructor into a list of Target
|
357
347
|
# objects, suitable for iterating through to find someone to talk to.
|
358
348
|
#
|
@@ -489,7 +479,17 @@ class LogstashWriter
|
|
489
479
|
# for any reason.
|
490
480
|
#
|
491
481
|
def socket
|
492
|
-
TCPSocket.new(@addr, @port)
|
482
|
+
@socket ||= TCPSocket.new(@addr, @port)
|
483
|
+
end
|
484
|
+
|
485
|
+
# Shut down the connection.
|
486
|
+
#
|
487
|
+
# @return [NilClass]
|
488
|
+
#
|
489
|
+
def close
|
490
|
+
@socket.close if @socket
|
491
|
+
@socket = nil
|
492
|
+
@describe_peer = nil
|
493
493
|
end
|
494
494
|
|
495
495
|
# Simple string representation of the target.
|
@@ -499,6 +499,34 @@ class LogstashWriter
|
|
499
499
|
def to_s
|
500
500
|
"#{@addr}:#{@port}"
|
501
501
|
end
|
502
|
+
|
503
|
+
# Provide as accurate a representation of what we're *actually* connected
|
504
|
+
# to as we can, given the constraints of whether we're connected.
|
505
|
+
#
|
506
|
+
# To prevent unpleasantness when the other end disconnects but we still
|
507
|
+
# want to know who we *were* connected to, we cache the result of our
|
508
|
+
# cogitations. Just in case.
|
509
|
+
#
|
510
|
+
# @return [String]
|
511
|
+
#
|
512
|
+
def describe_peer
|
513
|
+
@describe_peer ||= begin
|
514
|
+
if @socket
|
515
|
+
pa = @socket.peeraddr
|
516
|
+
if pa[0] == "AF_INET6"
|
517
|
+
"[#{pa[3]}]:#{pa[1]}"
|
518
|
+
else
|
519
|
+
"#{pa[3]}:#{pa[1]}"
|
520
|
+
end
|
521
|
+
else
|
522
|
+
nil
|
523
|
+
end
|
524
|
+
rescue Errno::ENOTCONN
|
525
|
+
# Peer disconnected apparently means "I forgot who I was connected
|
526
|
+
# to"... ¯\_(ツ)_/¯
|
527
|
+
nil
|
528
|
+
end || to_s
|
529
|
+
end
|
502
530
|
end
|
503
531
|
|
504
532
|
private_constant :Target
|