log-courier 1.1 → 1.2
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/lib/log-courier/client.rb +23 -16
- data/lib/log-courier/client_tls.rb +66 -46
- data/lib/log-courier/event_queue.rb +34 -32
- data/lib/log-courier/server.rb +27 -10
- data/lib/log-courier/server_tcp.rb +95 -54
- data/lib/log-courier/server_zmq.rb +280 -127
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18d64c60c7561492f74be1a92bb7ef4cce3d2a98
|
4
|
+
data.tar.gz: 71f010bef4d7c535e453dab752c83830169952ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f87ec04d3cc8e91c5e4257157f9a90112ca34d210fbf7bdac4ac78c58068c7dd529e14657947e150996ebc6dd85f3047524fe0d937426f656a742ab1c75dfc50
|
7
|
+
data.tar.gz: 6f82c4989b768bf644683b568f4e8fbc572506e0344093c5ed149e06a00848480bb20437ac1749f515709f66b9a1933ff9267f48afaa709496010abe4361e3d3
|
data/lib/log-courier/client.rb
CHANGED
@@ -42,7 +42,7 @@ module LogCourier
|
|
42
42
|
@ack_events = 0
|
43
43
|
|
44
44
|
options.each do |k, v|
|
45
|
-
|
45
|
+
fail ArgumentError unless self.respond_to?(k)
|
46
46
|
instance_variable_set "@#{k}", v
|
47
47
|
end
|
48
48
|
end
|
@@ -58,6 +58,7 @@ module LogCourier
|
|
58
58
|
}.merge!(options)
|
59
59
|
|
60
60
|
@logger = @options[:logger]
|
61
|
+
@logger['plugin'] = 'output/courier'
|
61
62
|
|
62
63
|
require 'log-courier/client_tls'
|
63
64
|
@client = ClientTls.new(@options)
|
@@ -90,6 +91,7 @@ module LogCourier
|
|
90
91
|
def publish(event)
|
91
92
|
# Pass the event into the spooler
|
92
93
|
@event_queue << event
|
94
|
+
return
|
93
95
|
end
|
94
96
|
|
95
97
|
def shutdown
|
@@ -98,8 +100,11 @@ module LogCourier
|
|
98
100
|
@io_thread.raise ShutdownSignal
|
99
101
|
@spooler_thread.join
|
100
102
|
@io_thread.join
|
103
|
+
return
|
101
104
|
end
|
102
105
|
|
106
|
+
private
|
107
|
+
|
103
108
|
def run_spooler
|
104
109
|
loop do
|
105
110
|
spooled = []
|
@@ -124,9 +129,9 @@ module LogCourier
|
|
124
129
|
@io_control << ['E', spooled]
|
125
130
|
end
|
126
131
|
end
|
132
|
+
return
|
127
133
|
rescue ShutdownSignal
|
128
|
-
|
129
|
-
0
|
134
|
+
return
|
130
135
|
end
|
131
136
|
|
132
137
|
def run_io
|
@@ -207,12 +212,12 @@ module LogCourier
|
|
207
212
|
# Keepalive timeout hit, send a PING unless we were awaiting a PONG
|
208
213
|
if @pending_ping
|
209
214
|
# Timed out, break into reconnect
|
210
|
-
|
215
|
+
fail TimeoutError
|
211
216
|
end
|
212
217
|
|
213
218
|
# Is send full? can_send will be false if so
|
214
219
|
# We should've started receiving ACK by now so time out
|
215
|
-
|
220
|
+
fail TimeoutError unless can_send
|
216
221
|
|
217
222
|
# Send PING
|
218
223
|
send_ping
|
@@ -227,17 +232,16 @@ module LogCourier
|
|
227
232
|
end
|
228
233
|
rescue ProtocolError => e
|
229
234
|
# Reconnect required due to a protocol error
|
230
|
-
@logger.warn
|
235
|
+
@logger.warn 'Protocol error', :error => e.message unless @logger.nil?
|
231
236
|
rescue TimeoutError
|
232
237
|
# Reconnect due to timeout
|
233
|
-
@logger.warn
|
238
|
+
@logger.warn 'Timeout occurred' unless @logger.nil?
|
234
239
|
rescue ShutdownSignal
|
235
240
|
# Shutdown, break out
|
236
241
|
break
|
237
|
-
rescue => e
|
242
|
+
rescue StandardError, NativeException => e
|
238
243
|
# Unknown error occurred
|
239
|
-
@logger.warn
|
240
|
-
@logger.warn("[LogCourierClient] #{e.backtrace}: #{e.message} (#{e.class})") unless @logger.nil?
|
244
|
+
@logger.warn e, :hint => 'Unknown error' unless @logger.nil?
|
241
245
|
end
|
242
246
|
|
243
247
|
# Disconnect and retry payloads
|
@@ -249,10 +253,12 @@ module LogCourier
|
|
249
253
|
end
|
250
254
|
|
251
255
|
@client.disconnect
|
256
|
+
return
|
252
257
|
end
|
253
258
|
|
254
259
|
def reset_keepalive
|
255
260
|
@keepalive_next = Time.now.to_i + @keepalive_timeout
|
261
|
+
return
|
256
262
|
end
|
257
263
|
|
258
264
|
def generate_nonce
|
@@ -262,6 +268,7 @@ module LogCourier
|
|
262
268
|
def send_ping
|
263
269
|
# Send it
|
264
270
|
@client.send 'PING', ''
|
271
|
+
return
|
265
272
|
end
|
266
273
|
|
267
274
|
def send_jdat(events)
|
@@ -288,6 +295,7 @@ module LogCourier
|
|
288
295
|
|
289
296
|
# Send it
|
290
297
|
@client.send 'JDAT', payload.data
|
298
|
+
return
|
291
299
|
end
|
292
300
|
|
293
301
|
def buffer_jdat_data(events, nonce)
|
@@ -307,23 +315,21 @@ module LogCourier
|
|
307
315
|
|
308
316
|
# Add length and then the data
|
309
317
|
buffer << [json_data.length].pack('N') << json_data
|
318
|
+
return
|
310
319
|
end
|
311
320
|
|
312
321
|
def process_pong(message)
|
313
322
|
# Sanity
|
314
|
-
if message.length != 0
|
315
|
-
raise ProtocolError, "Unexpected data attached to pong message (#{message.length})"
|
316
|
-
end
|
323
|
+
fail ProtocolError, "Unexpected data attached to pong message (#{message.length})" if message.length != 0
|
317
324
|
|
318
325
|
# No longer pending a PONG
|
319
326
|
@ping_pending = false
|
327
|
+
return
|
320
328
|
end
|
321
329
|
|
322
330
|
def process_ackn(message)
|
323
331
|
# Sanity
|
324
|
-
if message.length != 20
|
325
|
-
raise ProtocolError, "ACKN message size invalid (#{message.length})"
|
326
|
-
end
|
332
|
+
fail ProtocolError, "ACKN message size invalid (#{message.length})" if message.length != 20
|
327
333
|
|
328
334
|
# Grab nonce
|
329
335
|
sequence, nonce = message[0...4].unpack('N').first, message[4..-1]
|
@@ -348,6 +354,7 @@ module LogCourier
|
|
348
354
|
payload.data = nil
|
349
355
|
end
|
350
356
|
end
|
357
|
+
return
|
351
358
|
end
|
352
359
|
end
|
353
360
|
end
|
@@ -38,17 +38,17 @@ module LogCourier
|
|
38
38
|
@logger = @options[:logger]
|
39
39
|
|
40
40
|
[:port, :ssl_ca].each do |k|
|
41
|
-
|
41
|
+
fail "output/courier: '#{k}' is required" if @options[k].nil?
|
42
42
|
end
|
43
43
|
|
44
|
-
|
44
|
+
fail 'output/courier: \'addresses\' must contain at least one address' if @options[:addresses].empty?
|
45
45
|
|
46
46
|
c = 0
|
47
47
|
[:ssl_certificate, :ssl_key].each do
|
48
48
|
c += 1
|
49
49
|
end
|
50
50
|
|
51
|
-
|
51
|
+
fail 'output/courier: \'ssl_certificate\' and \'ssl_key\' must be specified together' if c == 1
|
52
52
|
end
|
53
53
|
|
54
54
|
def connect(io_control)
|
@@ -71,6 +71,7 @@ module LogCourier
|
|
71
71
|
@recv_thread = Thread.new do
|
72
72
|
run_recv io_control
|
73
73
|
end
|
74
|
+
return
|
74
75
|
end
|
75
76
|
|
76
77
|
def disconnect
|
@@ -78,8 +79,17 @@ module LogCourier
|
|
78
79
|
@send_thread.join
|
79
80
|
@recv_thread.raise ShutdownSignal
|
80
81
|
@recv_thread.join
|
82
|
+
return
|
81
83
|
end
|
82
84
|
|
85
|
+
def send(signature, message)
|
86
|
+
# Add to send queue
|
87
|
+
@send_q << [signature, message.length].pack('A4N') + message
|
88
|
+
return
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
83
93
|
def run_send(io_control)
|
84
94
|
# Ask for something to send
|
85
95
|
io_control << ['S']
|
@@ -108,29 +118,31 @@ module LogCourier
|
|
108
118
|
@ssl_client.write message
|
109
119
|
end
|
110
120
|
end
|
121
|
+
return
|
111
122
|
rescue OpenSSL::SSL::SSLError, IOError, Errno::ECONNRESET => e
|
112
|
-
@logger.warn
|
123
|
+
@logger.warn 'SSL write error', :error => e.message unless @logger.nil?
|
113
124
|
io_control << ['F']
|
125
|
+
return
|
114
126
|
rescue ShutdownSignal
|
115
|
-
|
116
|
-
rescue => e
|
117
|
-
@logger.warn
|
118
|
-
@logger.warn("[LogCourierClient] #{e.backtrace}: #{e.message} (#{e.class})") unless @logger.nil?
|
127
|
+
return
|
128
|
+
rescue StandardError, NativeException => e
|
129
|
+
@logger.warn e, :hint => 'Unknown SSL write error' unless @logger.nil?
|
119
130
|
io_control << ['F']
|
131
|
+
return
|
120
132
|
end
|
121
133
|
|
122
134
|
def run_recv(io_control)
|
123
135
|
loop do
|
124
136
|
# Grab a header
|
125
137
|
header = @ssl_client.read(8)
|
126
|
-
|
138
|
+
fail EOFError if header.nil?
|
127
139
|
|
128
140
|
# Decode signature and length
|
129
141
|
signature, length = header.unpack('A4N')
|
130
142
|
|
131
143
|
if length > 1048576
|
132
144
|
# Too big raise error
|
133
|
-
@logger.warn
|
145
|
+
@logger.warn 'Invalid message: data too big', :data_length => length unless @logger.nil?
|
134
146
|
io_control << ['F']
|
135
147
|
break
|
136
148
|
end
|
@@ -141,29 +153,28 @@ module LogCourier
|
|
141
153
|
# Pass through to receive
|
142
154
|
io_control << ['R', signature, message]
|
143
155
|
end
|
156
|
+
return
|
144
157
|
rescue OpenSSL::SSL::SSLError, IOError, Errno::ECONNRESET => e
|
145
|
-
@logger.warn
|
158
|
+
@logger.warn 'SSL read error', :error => e.message unless @logger.nil?
|
146
159
|
io_control << ['F']
|
160
|
+
return
|
147
161
|
rescue EOFError
|
148
|
-
@logger.warn
|
162
|
+
@logger.warn 'Connection closed by server' unless @logger.nil?
|
149
163
|
io_control << ['F']
|
164
|
+
return
|
150
165
|
rescue ShutdownSignal
|
151
|
-
|
166
|
+
return
|
152
167
|
rescue => e
|
153
|
-
@logger.warn
|
154
|
-
@logger.warn("[LogCourierClient] #{e.backtrace}: #{e.message} (#{e.class})") unless @logger.nil?
|
168
|
+
@logger.warn e, :hint => 'Unknown SSL read error' unless @logger.nil?
|
155
169
|
io_control << ['F']
|
156
|
-
|
157
|
-
|
158
|
-
def send(signature, message)
|
159
|
-
# Add to send queue
|
160
|
-
@send_q << [signature, message.length].pack('A4N') + message
|
170
|
+
return
|
161
171
|
end
|
162
172
|
|
163
173
|
def pause_send
|
164
174
|
return if @send_paused
|
165
175
|
@send_paused = true
|
166
176
|
@send_q << nil
|
177
|
+
return
|
167
178
|
end
|
168
179
|
|
169
180
|
def send_paused
|
@@ -175,44 +186,53 @@ module LogCourier
|
|
175
186
|
@send_paused = false
|
176
187
|
@send_q << nil
|
177
188
|
end
|
189
|
+
return
|
178
190
|
end
|
179
191
|
|
180
192
|
def tls_connect
|
181
193
|
# TODO: Implement random selection - and don't use separate :port - remember to update post_connection_check too
|
182
|
-
|
183
|
-
|
194
|
+
address = @options[:addresses][0]
|
195
|
+
port = @options[:port]
|
184
196
|
|
185
|
-
|
197
|
+
@logger.info 'Connecting', :address => address, :port => port unless @logger.nil?
|
186
198
|
|
187
|
-
|
188
|
-
|
189
|
-
ssl.key = OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]), @options[:ssl_key_passphrase])
|
190
|
-
end
|
199
|
+
begin
|
200
|
+
tcp_socket = TCPSocket.new(address, port)
|
191
201
|
|
192
|
-
|
193
|
-
cert_store.add_file(@options[:ssl_ca])
|
194
|
-
#ssl.cert_store = cert_store
|
195
|
-
ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
202
|
+
ssl = OpenSSL::SSL::SSLContext.new
|
196
203
|
|
197
|
-
|
204
|
+
unless @options[:ssl_certificate].nil?
|
205
|
+
ssl.cert = OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
|
206
|
+
ssl.key = OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]), @options[:ssl_key_passphrase])
|
207
|
+
end
|
198
208
|
|
199
|
-
|
209
|
+
cert_store = OpenSSL::X509::Store.new
|
210
|
+
cert_store.add_file(@options[:ssl_ca])
|
211
|
+
#ssl.cert_store = cert_store
|
212
|
+
ssl.verify_mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
200
213
|
|
201
|
-
|
202
|
-
socket.post_connection_check(@options[:addresses][0])
|
214
|
+
@ssl_client = OpenSSL::SSL::SSLSocket.new(tcp_socket)
|
203
215
|
|
204
|
-
|
216
|
+
socket = @ssl_client.connect
|
205
217
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
218
|
+
# Verify certificate
|
219
|
+
socket.post_connection_check(address)
|
220
|
+
|
221
|
+
# Add extra logging data now we're connected
|
222
|
+
@logger['address'] = address
|
223
|
+
@logger['port'] = port
|
224
|
+
|
225
|
+
@logger.info 'Connected successfully' unless @logger.nil?
|
226
|
+
return
|
227
|
+
rescue OpenSSL::SSL::SSLError, IOError, Errno::ECONNRESET => e
|
228
|
+
@logger.warn 'Connection failed', :error => e.message, :address => address, :port => port unless @logger.nil?
|
229
|
+
return
|
230
|
+
rescue ShutdownSignal
|
231
|
+
return
|
232
|
+
rescue StandardError, NativeException => e
|
233
|
+
@logger.warn e, :hint => 'Unknown connection failure', :address => address, :port => port unless @logger.nil?
|
234
|
+
raise e
|
235
|
+
end
|
216
236
|
end
|
217
237
|
end
|
218
238
|
end
|
@@ -33,7 +33,7 @@ module LogCourier
|
|
33
33
|
# Creates a fixed-length queue with a maximum size of +max+.
|
34
34
|
#
|
35
35
|
def initialize(max)
|
36
|
-
|
36
|
+
fail ArgumentError, "queue size must be positive" unless max > 0
|
37
37
|
@max = max
|
38
38
|
@enque_cond = ConditionVariable.new
|
39
39
|
@num_enqueue_waiting = 0
|
@@ -44,20 +44,19 @@ module LogCourier
|
|
44
44
|
self.taint
|
45
45
|
@mutex = Mutex.new
|
46
46
|
@cond = ConditionVariable.new
|
47
|
+
return
|
47
48
|
end
|
48
49
|
|
49
50
|
#
|
50
51
|
# Returns the maximum size of the queue.
|
51
52
|
#
|
52
|
-
|
53
|
-
@max
|
54
|
-
end
|
53
|
+
attr_reader :max
|
55
54
|
|
56
55
|
#
|
57
56
|
# Sets the maximum size of the queue.
|
58
57
|
#
|
59
58
|
def max=(max)
|
60
|
-
|
59
|
+
fail ArgumentError, "queue size must be positive" unless max > 0
|
61
60
|
|
62
61
|
@mutex.synchronize do
|
63
62
|
if max <= @max
|
@@ -90,7 +89,7 @@ module LogCourier
|
|
90
89
|
ensure
|
91
90
|
@num_enqueue_waiting -= 1
|
92
91
|
end
|
93
|
-
|
92
|
+
fail TimeoutError if !timeout.nil? and Time.now - start >= timeout
|
94
93
|
end
|
95
94
|
|
96
95
|
@que.push obj
|
@@ -113,7 +112,7 @@ module LogCourier
|
|
113
112
|
# Retrieves data from the queue and runs a waiting thread, if any.
|
114
113
|
#
|
115
114
|
def pop(*args)
|
116
|
-
retval =
|
115
|
+
retval = pop_timeout *args
|
117
116
|
@mutex.synchronize do
|
118
117
|
if @que.length < @max
|
119
118
|
@enque_cond.signal
|
@@ -122,31 +121,6 @@ module LogCourier
|
|
122
121
|
retval
|
123
122
|
end
|
124
123
|
|
125
|
-
#
|
126
|
-
# Retrieves data from the queue. If the queue is empty, the calling thread is
|
127
|
-
# suspended until data is pushed onto the queue or, if set, +timeout+ seconds
|
128
|
-
# passes. If +timeout+ is 0, the thread isn't suspended, and an exception is
|
129
|
-
# raised.
|
130
|
-
#
|
131
|
-
def _pop_timeout(timeout = nil)
|
132
|
-
unless timeout.nil?
|
133
|
-
start = Time.now
|
134
|
-
end
|
135
|
-
@mutex.synchronize do
|
136
|
-
loop do
|
137
|
-
return @que.shift unless @que.empty?
|
138
|
-
raise TimeoutError if timeout == 0
|
139
|
-
begin
|
140
|
-
@num_waiting += 1
|
141
|
-
@cond.wait @mutex, timeout
|
142
|
-
ensure
|
143
|
-
@num_waiting -= 1
|
144
|
-
end
|
145
|
-
raise TimeoutError if !timeout.nil? and Time.now - start >= timeout
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
124
|
#
|
151
125
|
# Alias of pop
|
152
126
|
#
|
@@ -190,5 +164,33 @@ module LogCourier
|
|
190
164
|
def num_waiting
|
191
165
|
@num_waiting + @num_enqueue_waiting
|
192
166
|
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
#
|
171
|
+
# Retrieves data from the queue. If the queue is empty, the calling thread is
|
172
|
+
# suspended until data is pushed onto the queue or, if set, +timeout+ seconds
|
173
|
+
# passes. If +timeout+ is 0, the thread isn't suspended, and an exception is
|
174
|
+
# raised.
|
175
|
+
#
|
176
|
+
def pop_timeout(timeout = nil)
|
177
|
+
unless timeout.nil?
|
178
|
+
start = Time.now
|
179
|
+
end
|
180
|
+
@mutex.synchronize do
|
181
|
+
loop do
|
182
|
+
return @que.shift unless @que.empty?
|
183
|
+
fail TimeoutError if timeout == 0
|
184
|
+
begin
|
185
|
+
@num_waiting += 1
|
186
|
+
@cond.wait @mutex, timeout
|
187
|
+
ensure
|
188
|
+
@num_waiting -= 1
|
189
|
+
end
|
190
|
+
fail TimeoutError if !timeout.nil? and Time.now - start >= timeout
|
191
|
+
end
|
192
|
+
end
|
193
|
+
return
|
194
|
+
end
|
193
195
|
end
|
194
196
|
end
|