log-courier 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|