slack_log_device 1.0.1 → 2.0.0
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/.gitignore +1 -0
- data/README.mdown +13 -0
- data/VERSION +1 -1
- data/lib/slack_log_device.rb +63 -7
- data/spec/slack_log_device_spec.rb +225 -25
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3567b60b8adf372370ebeff154732ea9c0c85a6b
|
4
|
+
data.tar.gz: 1eaa0fea7a5ce04b969ef5ee2c5fd806e558d822
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 721424872c5fbb4f9a997ef2c10b2c35f917255062fe469db09d7cf417295396ff955c23bfa1b7bcd827c5881a060c6e0c8bc157a633a8f8f9a66df9db88d08f
|
7
|
+
data.tar.gz: bd94f833858783c0ea6c4b649ea1a9cb7610302cf36e49cf9e42f2ebf4d0783d1c567061b70c0b282cc7ea8a6f5e51d8041d6ac0bc241a0406c3af3fc600bb9b
|
data/.gitignore
CHANGED
data/README.mdown
CHANGED
@@ -24,6 +24,19 @@ logger.warn('BAM!')
|
|
24
24
|
|
25
25
|
Then, the logged message will be writen to webhook's configured channel.
|
26
26
|
|
27
|
+
Note that the messages written are buffered in order to avoid consecutive
|
28
|
+
request.
|
29
|
+
|
30
|
+
## Options
|
31
|
+
|
32
|
+
- `auto_flush`: To flush messages directly when a message is written (disabled
|
33
|
+
by default).
|
34
|
+
- `flush_delay`: The delay in seconds to flush messages (1 by default).
|
35
|
+
- `max_buffer_size`: The max buffer size to flush messages (8192 by default).
|
36
|
+
- `timeout`: The timeout in seconds to send message to slack (5 by default).
|
37
|
+
- `username`: The username to post message as (nil by default).
|
38
|
+
- `webhook_url`: The URL of the webhook (mandatory).
|
39
|
+
|
27
40
|
## Executing test suite
|
28
41
|
|
29
42
|
This project is fully tested with [Rspec 3](http://github.com/rspec/rspec).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
data/lib/slack_log_device.rb
CHANGED
@@ -5,22 +5,69 @@ require 'logger'
|
|
5
5
|
|
6
6
|
class SlackLogDevice
|
7
7
|
|
8
|
-
attr_reader :timeout, :username, :webhook_url
|
8
|
+
attr_reader :flush_delay, :max_buffer_size, :timeout, :username, :webhook_url
|
9
9
|
|
10
10
|
def initialize(options = {})
|
11
|
-
options.assert_valid_keys(:timeout, :username, :webhook_url)
|
11
|
+
options.assert_valid_keys(:auto_flush, :flush_delay, :max_buffer_size, :timeout, :username, :webhook_url)
|
12
|
+
@buffer = []
|
13
|
+
@mutex = Mutex.new
|
14
|
+
@flush_thread
|
15
|
+
self.auto_flush = options[:auto_flush]
|
16
|
+
self.flush_delay = options.key?(:flush_delay) ? options[:flush_delay] : 1
|
17
|
+
self.max_buffer_size = options.key?(:max_buffer_size) ? options[:max_buffer_size] : 8192
|
12
18
|
self.timeout = options.key?(:timeout) ? options[:timeout] : 5
|
13
19
|
self.username = options[:username]
|
14
20
|
self.webhook_url = options[:webhook_url]
|
15
21
|
end
|
16
22
|
|
23
|
+
def auto_flush?
|
24
|
+
@auto_flush
|
25
|
+
end
|
26
|
+
|
27
|
+
def auto_flush=(value)
|
28
|
+
@auto_flush = value.present?
|
29
|
+
end
|
30
|
+
|
17
31
|
def close
|
18
32
|
# Does nothing, this method must exist to consider the LogDevice as an IO.
|
19
33
|
end
|
20
34
|
|
35
|
+
def flush
|
36
|
+
return if @buffer.empty?
|
37
|
+
message = ''
|
38
|
+
@mutex.synchronize do
|
39
|
+
message = @buffer.join("\n")
|
40
|
+
@buffer.clear
|
41
|
+
end
|
42
|
+
data = { 'text' => message.to_s }
|
43
|
+
data['username'] = username if username.present?
|
44
|
+
begin
|
45
|
+
HTTParty.post(webhook_url, body: data.to_json, headers: { 'Content-Type': 'application/json' }, timeout: timeout)
|
46
|
+
rescue => e
|
47
|
+
STDERR.puts(e)
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def flush?
|
53
|
+
auto_flush? || flush_delay.zero? || @buffer.join("\n").size > max_buffer_size
|
54
|
+
end
|
55
|
+
|
56
|
+
def flush_delay=(value)
|
57
|
+
delay = Integer(value) rescue nil
|
58
|
+
raise ArgumentError.new("Invalid flush delay: #{value.inspect}") if delay.nil? || delay < 0
|
59
|
+
@flush_delay = delay
|
60
|
+
end
|
61
|
+
|
62
|
+
def max_buffer_size=(value)
|
63
|
+
size = Integer(value) rescue nil
|
64
|
+
raise ArgumentError.new("Invalid max buffer size: #{value.inspect}") if size.nil? || size < 0
|
65
|
+
@max_buffer_size = size
|
66
|
+
end
|
67
|
+
|
21
68
|
def timeout=(value)
|
22
69
|
timeout = Integer(value) rescue nil
|
23
|
-
raise ArgumentError.new("Invalid timeout: #{value.inspect}") if timeout.nil?
|
70
|
+
raise ArgumentError.new("Invalid timeout: #{value.inspect}") if timeout.nil? || timeout <= 0
|
24
71
|
@timeout = timeout
|
25
72
|
end
|
26
73
|
|
@@ -31,16 +78,25 @@ class SlackLogDevice
|
|
31
78
|
def webhook_url=(value)
|
32
79
|
raise ArgumentError.new('Webhook URL must be specified') if value.blank?
|
33
80
|
uri = URI(value.to_s) rescue nil
|
34
|
-
raise ArgumentError.new("Invalid
|
81
|
+
raise ArgumentError.new("Invalid webhook URL: #{value.inspect}") if uri.nil? || !uri.is_a?(URI::HTTP)
|
35
82
|
@webhook_url = uri.to_s
|
36
83
|
end
|
37
84
|
|
38
85
|
def write(message)
|
39
86
|
message = message.to_s.try(:strip)
|
40
87
|
return if message.blank?
|
41
|
-
|
42
|
-
|
43
|
-
|
88
|
+
@mutex.synchronize do
|
89
|
+
@buffer << message
|
90
|
+
end
|
91
|
+
if flush?
|
92
|
+
flush
|
93
|
+
else
|
94
|
+
@flush_thread.kill if @flush_thread
|
95
|
+
@flush_thread = Thread.new do
|
96
|
+
sleep(flush_delay)
|
97
|
+
flush
|
98
|
+
end
|
99
|
+
end
|
44
100
|
nil
|
45
101
|
end
|
46
102
|
|
@@ -6,6 +6,24 @@ describe SlackLogDevice do
|
|
6
6
|
let(:logger) { Logger.new(device).tap { |logger| logger.level = Logger::INFO } }
|
7
7
|
let(:options) { { username: 'MyApp', webhook_url: 'https://hooks.slack.com/services/test' } }
|
8
8
|
|
9
|
+
before :each do
|
10
|
+
allow(HTTParty).to receive(:post)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#auto_flush?' do
|
14
|
+
|
15
|
+
it 'is false by default' do
|
16
|
+
options.delete(:auto_flush)
|
17
|
+
expect(device).not_to be_auto_flush
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can be set to true' do
|
21
|
+
options[:auto_flush] = true
|
22
|
+
expect(device).to be_auto_flush
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
9
27
|
describe '#close' do
|
10
28
|
|
11
29
|
it 'does nothing' do
|
@@ -16,12 +34,128 @@ describe SlackLogDevice do
|
|
16
34
|
|
17
35
|
end
|
18
36
|
|
37
|
+
describe '#flush' do
|
38
|
+
|
39
|
+
it 'sends a post to webhook URL with given given message and specified username' do
|
40
|
+
device.write('BAM!')
|
41
|
+
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!', 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
42
|
+
device.flush
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'does not send username if nil' do
|
46
|
+
options.delete(:username)
|
47
|
+
device.write('BAM!')
|
48
|
+
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!' }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
49
|
+
device.flush
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'use specified timeout' do
|
53
|
+
options[:timeout] = 12
|
54
|
+
device.write('BAM!')
|
55
|
+
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!', 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 12)
|
56
|
+
device.flush
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'flushes all message writen separated by a new line' do
|
60
|
+
device.write('BAM!')
|
61
|
+
device.write('BIM!')
|
62
|
+
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => "BAM!\nBIM!", 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
63
|
+
device.flush
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns nil' do
|
67
|
+
device.write('BIM!')
|
68
|
+
expect(device.flush).to be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#flush?' do
|
74
|
+
|
75
|
+
it 'is true if max_buffer_size is reached' do
|
76
|
+
options[:max_buffer_size] = 10
|
77
|
+
device.write('012345678')
|
78
|
+
expect {
|
79
|
+
device.instance_variable_get(:@buffer).push('a')
|
80
|
+
}.to change { device.flush? }.from(false).to(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'is true if auto_flush option is present' do
|
84
|
+
expect {
|
85
|
+
device.auto_flush = true
|
86
|
+
}.to change { device.flush? }.from(false).to(true)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'is true if flush delay is 0' do
|
90
|
+
expect {
|
91
|
+
device.flush_delay = 0
|
92
|
+
}.to change { device.flush? }.from(false).to(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#flush_delay' do
|
98
|
+
|
99
|
+
it 'is 1 by default' do
|
100
|
+
expect(device.flush_delay).to eq(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'can be specified' do
|
104
|
+
options[:flush_delay] = 10
|
105
|
+
expect(device.flush_delay).to be(10)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'raise an error if invalid' do
|
109
|
+
options[:flush_delay] = 'foo'
|
110
|
+
expect {
|
111
|
+
device
|
112
|
+
}.to raise_error(ArgumentError, 'Invalid flush delay: "foo"')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'raise an error if negative' do
|
116
|
+
options[:flush_delay] = -1
|
117
|
+
expect {
|
118
|
+
device
|
119
|
+
}.to raise_error(ArgumentError, 'Invalid flush delay: -1')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'can be zero' do
|
123
|
+
options[:flush_delay] = 0
|
124
|
+
expect(device.flush_delay).to be(0)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'raise an error if blank' do
|
128
|
+
options[:flush_delay] = ' '
|
129
|
+
expect {
|
130
|
+
device
|
131
|
+
}.to raise_error(ArgumentError, 'Invalid flush delay: " "')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'raise an error if set as nil' do
|
135
|
+
expect {
|
136
|
+
device.flush_delay = nil
|
137
|
+
}.to raise_error(ArgumentError, 'Invalid flush delay: nil')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'can be specified as string' do
|
141
|
+
options[:flush_delay] = '42'
|
142
|
+
expect(device.flush_delay).to eq(42)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'can be set' do
|
146
|
+
expect {
|
147
|
+
device.flush_delay = 15
|
148
|
+
}.to change { device.flush_delay }.from(1).to(15)
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
19
153
|
describe '#initialize' do
|
20
154
|
|
21
155
|
it 'raise an error if an invalid option is given' do
|
22
156
|
expect {
|
23
157
|
SlackLogDevice.new(foo: 'bar')
|
24
|
-
}.to raise_error(ArgumentError, "Unknown key: :foo. Valid keys are: :timeout, :username, :webhook_url")
|
158
|
+
}.to raise_error(ArgumentError, "Unknown key: :foo. Valid keys are: :auto_flush, :flush_delay, :max_buffer_size, :timeout, :username, :webhook_url")
|
25
159
|
end
|
26
160
|
|
27
161
|
it 'raise an error if webhook option is not given' do
|
@@ -32,6 +166,62 @@ describe SlackLogDevice do
|
|
32
166
|
|
33
167
|
end
|
34
168
|
|
169
|
+
describe '#max_buffer_size' do
|
170
|
+
|
171
|
+
it 'is 8192 by default' do
|
172
|
+
expect(device.max_buffer_size).to eq(8192)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'can be specified' do
|
176
|
+
options[:max_buffer_size] = 42
|
177
|
+
expect(device.max_buffer_size).to be(42)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'raise an error if invalid' do
|
181
|
+
options[:max_buffer_size] = 'foo'
|
182
|
+
expect {
|
183
|
+
device
|
184
|
+
}.to raise_error(ArgumentError, 'Invalid max buffer size: "foo"')
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'raise an error if negative' do
|
188
|
+
options[:max_buffer_size] = -1
|
189
|
+
expect {
|
190
|
+
device
|
191
|
+
}.to raise_error(ArgumentError, 'Invalid max buffer size: -1')
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'can be zero' do
|
195
|
+
options[:max_buffer_size] = 0
|
196
|
+
expect(device.max_buffer_size).to be_zero
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'raise an error if blank' do
|
200
|
+
options[:max_buffer_size] = ' '
|
201
|
+
expect {
|
202
|
+
device
|
203
|
+
}.to raise_error(ArgumentError, 'Invalid max buffer size: " "')
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'raise an error if set as nil' do
|
207
|
+
expect {
|
208
|
+
device.max_buffer_size = nil
|
209
|
+
}.to raise_error(ArgumentError, 'Invalid max buffer size: nil')
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'can be specified as string' do
|
213
|
+
options[:max_buffer_size] = '42'
|
214
|
+
expect(device.max_buffer_size).to eq(42)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'can be set' do
|
218
|
+
expect {
|
219
|
+
device.max_buffer_size = 1024
|
220
|
+
}.to change { device.max_buffer_size }.from(8192).to(1024)
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
35
225
|
describe '#timeout' do
|
36
226
|
|
37
227
|
it 'is 5 by default' do
|
@@ -50,7 +240,21 @@ describe SlackLogDevice do
|
|
50
240
|
}.to raise_error(ArgumentError, 'Invalid timeout: "foo"')
|
51
241
|
end
|
52
242
|
|
53
|
-
it '
|
243
|
+
it 'raise an error if negative' do
|
244
|
+
options[:timeout] = -1
|
245
|
+
expect {
|
246
|
+
device
|
247
|
+
}.to raise_error(ArgumentError, 'Invalid timeout: -1')
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'raise an error if zero' do
|
251
|
+
options[:timeout] = 0
|
252
|
+
expect {
|
253
|
+
device
|
254
|
+
}.to raise_error(ArgumentError, 'Invalid timeout: 0')
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'raise an error if blank' do
|
54
258
|
options[:timeout] = ' '
|
55
259
|
expect {
|
56
260
|
device
|
@@ -115,7 +319,7 @@ describe SlackLogDevice do
|
|
115
319
|
options[:webhook_url] = 'foo'
|
116
320
|
expect {
|
117
321
|
device
|
118
|
-
}.to raise_error(ArgumentError, 'Invalid
|
322
|
+
}.to raise_error(ArgumentError, 'Invalid webhook URL: "foo"')
|
119
323
|
end
|
120
324
|
|
121
325
|
it 'can be an HTTP URL' do
|
@@ -132,7 +336,7 @@ describe SlackLogDevice do
|
|
132
336
|
options[:webhook_url] = 'ftp://google.com'
|
133
337
|
expect {
|
134
338
|
device
|
135
|
-
}.to raise_error(ArgumentError, 'Invalid
|
339
|
+
}.to raise_error(ArgumentError, 'Invalid webhook URL: "ftp://google.com"')
|
136
340
|
end
|
137
341
|
|
138
342
|
it 'can be set' do
|
@@ -145,35 +349,18 @@ describe SlackLogDevice do
|
|
145
349
|
|
146
350
|
describe '#write' do
|
147
351
|
|
148
|
-
it 'sends a post to webhook URL with given given message and specified username' do
|
149
|
-
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!', 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
150
|
-
device.write('BAM!')
|
151
|
-
end
|
152
|
-
|
153
352
|
it 'returns nil' do
|
154
|
-
allow(HTTParty).to receive(:post)
|
155
353
|
expect(device.write('BAM!')).to be_nil
|
156
354
|
end
|
157
355
|
|
158
|
-
it 'does not send username if nil' do
|
159
|
-
options.delete(:username)
|
160
|
-
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!' }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
161
|
-
device.write('BAM!')
|
162
|
-
end
|
163
|
-
|
164
356
|
it 'does nothing if log level is lower than specified one' do
|
165
357
|
expect(HTTParty).not_to receive(:post)
|
166
358
|
logger.debug('BIM!')
|
167
359
|
end
|
168
360
|
|
169
|
-
it 'send HTTP request if log level is equal to specified one' do
|
170
|
-
expect(HTTParty).to receive(:post)
|
171
|
-
logger.info('BIM!')
|
172
|
-
end
|
173
|
-
|
174
361
|
it 'strips message' do
|
175
|
-
expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM !', 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 5)
|
176
362
|
device.write(" BAM !\n")
|
363
|
+
expect(device.instance_variable_get(:@buffer)).to eq(['BAM !'])
|
177
364
|
end
|
178
365
|
|
179
366
|
it 'does nothing if message is blank' do
|
@@ -184,16 +371,29 @@ describe SlackLogDevice do
|
|
184
371
|
it 'does nothing if message is nil' do
|
185
372
|
expect(HTTParty).not_to receive(:post)
|
186
373
|
expect(device.write(nil)).to be_nil
|
374
|
+
expect(device.instance_variable_get(:@buffer)).to eq([])
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'send HTTP request if log level is equal to specified one' do
|
378
|
+
expect(HTTParty).to receive(:post)
|
379
|
+
logger.info('BIM!')
|
380
|
+
device.flush
|
187
381
|
end
|
188
382
|
|
189
383
|
it 'send HTTP request if log level is higher to specified one' do
|
190
384
|
expect(HTTParty).to receive(:post)
|
191
385
|
logger.warn('BIM!')
|
386
|
+
device.flush
|
192
387
|
end
|
193
388
|
|
194
|
-
it '
|
195
|
-
options[:
|
196
|
-
expect(HTTParty).to receive(:post)
|
389
|
+
it 'flush if auto flush is true' do
|
390
|
+
options[:auto_flush] = true
|
391
|
+
expect(HTTParty).to receive(:post)
|
392
|
+
device.write('BAM!')
|
393
|
+
end
|
394
|
+
|
395
|
+
it 'does not post HTTP message if auto flush is false' do
|
396
|
+
expect(HTTParty).not_to receive(:post)
|
197
397
|
device.write('BAM!')
|
198
398
|
end
|
199
399
|
|