slack_log_device 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a971af4091c07ffdc8b9ee83c180160eadf73352
4
- data.tar.gz: b6595e5dfd101fedb0fece93e7cd45b25406996a
3
+ metadata.gz: 3567b60b8adf372370ebeff154732ea9c0c85a6b
4
+ data.tar.gz: 1eaa0fea7a5ce04b969ef5ee2c5fd806e558d822
5
5
  SHA512:
6
- metadata.gz: 00c9b7b2007d08c6a2f5471a74d454fd12a52789e4d7b3a64c71bbfa182903ae43200d4349185a10afee3676c2a676a6d5e6cf6751d364e9c0c108e47cc03c48
7
- data.tar.gz: 95513c23e9c1dc1cd09f66a8839c4a26400fea2a1ae146f23041c9e47542707a09a5d48b1b3d330b44b84a63872443a380169a7ac957ce1cc4f1f3ac190752ee
6
+ metadata.gz: 721424872c5fbb4f9a997ef2c10b2c35f917255062fe469db09d7cf417295396ff955c23bfa1b7bcd827c5881a060c6e0c8bc157a633a8f8f9a66df9db88d08f
7
+ data.tar.gz: bd94f833858783c0ea6c4b649ea1a9cb7610302cf36e49cf9e42f2ebf4d0783d1c567061b70c0b282cc7ea8a6f5e51d8041d6ac0bc241a0406c3af3fc600bb9b
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  .DS_Store
2
2
  /.bundle/
3
+ /.byebug_history
3
4
  /.ruby-version
4
5
  /Gemfile.lock
5
6
  /pkg/
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.0.1
1
+ 2.0.0
@@ -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 Webhook URL: #{value.inspect}") if uri.nil? || !uri.is_a?(URI::HTTP)
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
- data = { 'text' => message.to_s }
42
- data['username'] = username if username.present?
43
- HTTParty.post(webhook_url, body: data.to_json, headers: { 'Content-Type': 'application/json' }, timeout: timeout)
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 'is default value if blank' do
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 Webhook URL: "foo"')
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 Webhook URL: "ftp://google.com"')
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 'use specified timeout' do
195
- options[:timeout] = 12
196
- expect(HTTParty).to receive(:post).with(options[:webhook_url], body: { 'text' => 'BAM!', 'username' => options[:username] }.to_json, headers: { 'Content-Type': 'application/json' }, timeout: 12)
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack_log_device
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Toulotte