logstash_writer 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|