slack_log_device 2.2.3 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dc81637219036304853c58b3abdff5b0461746b6
4
- data.tar.gz: cbd0673773540ba8b65ed7f7ed24a2a508a46df8
3
+ metadata.gz: 7da84e6d177382ef3698ba25b1cafddfd7e41e9d
4
+ data.tar.gz: 9643a365bff66ea7ea8219d38c73dcad7166185e
5
5
  SHA512:
6
- metadata.gz: 58320e47d7af2785ed9cf2c03a8770e5fa32ed4be7e3225491a9c36541693ba5be039a02aacb54e63d7761c9c4f7c9121b6a959eb24436722ec4882bebdbf9ef
7
- data.tar.gz: 9e3d6ac73a9b43877069efc40adb431629131d5a2c99a7b3f7bc759fb443c846f51f2d6eec5b650e48d0adbf3f517a3a1596fb5c2a5b51bb1c83010de91697a2
6
+ metadata.gz: a25cdda1fa68d846b11405ade1d656b0b0d349db2faa3c798290643ae58e78cd878e81eec53cfb204ceececdfc2a31a75b1561d8394d1450cf29f05e0e552f32
7
+ data.tar.gz: 44c2fda8c1c618aee82cd46fc6a6bf48445826290048fd269817da57daa34ddbc54a9e4a954e99069f35c794f628a111e86b26a0a120b2b6cadd4eaf4c751aaa
data/README.mdown CHANGED
@@ -46,7 +46,14 @@ starting with a `@`).
46
46
  It can be configured like this:
47
47
 
48
48
  ```ruby
49
- logger.formatter = SlackLogDevice::FORMATTER
49
+ logger.formatter = SlackLogDevice.formatter
50
+ ```
51
+
52
+ `SlackLogDevice.formatter` method also accepts block to transform logged
53
+ message, example:
54
+
55
+ ```ruby
56
+ logger.formatter = SlackLogDevice.formatter { |message| message.reverse }
50
57
  ```
51
58
 
52
59
  ## Rails configuration
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.3
1
+ 3.0.0
@@ -0,0 +1,94 @@
1
+ class SlackLogDevice
2
+
3
+ class Formatter
4
+
5
+ MAX_MESSAGE_LENGTH = 4000
6
+
7
+ attr_reader :extra_metadata
8
+
9
+ def initialize(options = {}, &block)
10
+ options.assert_valid_keys(:extra_metadata)
11
+ @extra_metadata = options.key?(:extra_metadata) ? options[:extra_metadata] : {}
12
+ @message_conveter = block_given? ? Proc.new(&block) : -> (message) { message }
13
+ end
14
+
15
+ def call(severity, datetime, progname, message)
16
+ text = "*`#{severity}`*"
17
+ text << " (*#{progname}*)" if progname.present?
18
+ text << ':'
19
+ if message.is_a?(Exception)
20
+ exception = message
21
+ text << " A `#{message.class}` occurred: #{convert_message(exception.message)}".rstrip
22
+ text = truncate(text)
23
+ text = append_metadata(text, exception)
24
+ text = append_backtrace(text, exception)
25
+ else
26
+ text << " #{convert_message(message)}".rstrip
27
+ text = append_metadata(text, message)
28
+ end
29
+ truncate(text)
30
+ end
31
+
32
+ private
33
+
34
+ def append_backtrace(text, exception)
35
+ backtrace = format_backtrace(exception, MAX_MESSAGE_LENGTH - text.size - 2)
36
+ text << "\n\n" << backtrace if backtrace.present?
37
+ text
38
+ end
39
+
40
+ def append_metadata(text, message)
41
+ metadata = format_metadata(message, MAX_MESSAGE_LENGTH - text.size - 2)
42
+ text << "\n\n" << metadata if metadata.present?
43
+ text
44
+ end
45
+
46
+ def convert_message(message)
47
+ @message_conveter.call(message.to_s.strip).to_s.strip
48
+ end
49
+
50
+ def default_metadata(request)
51
+ return {} if request.blank?
52
+ metadata = {
53
+ 'Method' => request.method,
54
+ 'URL' => request.url,
55
+ 'Remote address' => request.remote_addr,
56
+ 'User-Agent' => request.user_agent,
57
+ }
58
+ metadata.keys.each do |key|
59
+ value = metadata[key]
60
+ metadata[key] = "`#{value.strip}`" if value.present?
61
+ end
62
+ metadata
63
+ end
64
+
65
+ def format_backtrace(exception, size_available)
66
+ backtrace = exception.backtrace.try(:join, "\n")
67
+ return nil if backtrace.blank? || size_available < 7
68
+ "```#{truncate(backtrace, size_available - 6)}```"
69
+ end
70
+
71
+ def format_metadata(message, size_available)
72
+ return nil if size_available < 11
73
+ options = {}
74
+ options[:exception] = message if message.is_a?(Exception)
75
+ request = options[:exception].present? ? options[:exception].instance_variable_get(:@__slack_log_device_request) : nil
76
+ options[:request] = request if request.present?
77
+ text = default_metadata(request).merge(extra_metadata).map do |name, value|
78
+ value = value.call(options) if value.respond_to?(:call)
79
+ value.present? ? "• *#{name.strip}*: #{value.strip}" : nil
80
+ end.compact.join("\n")
81
+ return nil if text.blank?
82
+ truncate(text, size_available)
83
+ end
84
+
85
+ def truncate(message, max_length = MAX_MESSAGE_LENGTH)
86
+ message = message.strip
87
+ return message if message.size <= max_length
88
+ return message[0, max_length] if max_length < 3
89
+ "#{message[0, max_length - 3]}..."
90
+ end
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,16 @@
1
+ class SlackLogDevice
2
+
3
+ module RailsExceptionsLogging
4
+
5
+ def log_error(request, wrapper)
6
+ logger = logger(request)
7
+ return unless logger
8
+ exception = wrapper.exception
9
+ return if defined?(ActionController::RoutingError) && exception.is_a?(ActionController::RoutingError)
10
+ exception.instance_variable_set(:@__slack_log_device_request, request)
11
+ logger.fatal(exception)
12
+ end
13
+
14
+ end
15
+
16
+ end
@@ -5,26 +5,15 @@ require 'logger'
5
5
 
6
6
  class SlackLogDevice
7
7
 
8
- FORMATTER = -> (severity, datetime, progname, message) do
9
- text = "*`#{severity}`*"
10
- text << " (*#{progname}*)" if progname.present?
11
- text << ': '
12
- if message.is_a?(Exception)
13
- text << "A `#{message.class}` occurred: #{message.message}\n\n```"
14
- backtrace = message.backtrace.join("\n")
15
- backtrace = backtrace[0, MAX_MESSAGE_LENGTH - 6 - text.size] << "..." if backtrace.size > MAX_MESSAGE_LENGTH - 3
16
- text << backtrace << '```'
17
- else
18
- text << message
19
- end
20
- end
21
- MAX_MESSAGE_LENGTH = 4000
22
-
23
8
  attr_reader :channel, :flush_delay, :max_buffer_size, :timeout, :username, :webhook_url
24
9
 
10
+ def self.formatter(options = {}, &block)
11
+ Formatter.new(options, &block)
12
+ end
13
+
25
14
  def initialize(options = {})
26
15
  options.assert_valid_keys(:auto_flush, :channel, :flush_delay, :max_buffer_size, :timeout, :username, :webhook_url)
27
- @buffer = []
16
+ @buffer = ''
28
17
  @mutex = Mutex.new
29
18
  @flush_thread
30
19
  self.auto_flush = options[:auto_flush]
@@ -58,8 +47,8 @@ class SlackLogDevice
58
47
  return if @buffer.empty?
59
48
  message = ''
60
49
  @mutex.synchronize do
61
- message = @buffer.join("\n")
62
- @buffer.clear
50
+ message = @buffer
51
+ @buffer = ''
63
52
  end
64
53
  data = { 'text' => message.to_s }
65
54
  data['channel'] = channel if channel.present?
@@ -73,7 +62,7 @@ class SlackLogDevice
73
62
  end
74
63
 
75
64
  def flush?
76
- auto_flush? || flush_delay.zero? || @buffer.join("\n").bytesize > max_buffer_size
65
+ auto_flush? || flush_delay.zero? || @buffer.bytesize > max_buffer_size || @buffer.include?('```')
77
66
  end
78
67
 
79
68
  def flush_delay=(value)
@@ -109,6 +98,7 @@ class SlackLogDevice
109
98
  message = message.to_s.try(:strip)
110
99
  return if message.blank?
111
100
  @mutex.synchronize do
101
+ @buffer << "\n" unless @buffer.empty?
112
102
  @buffer << message
113
103
  end
114
104
  @flush_thread.kill if @flush_thread
@@ -124,3 +114,10 @@ class SlackLogDevice
124
114
  end
125
115
 
126
116
  end
117
+
118
+ require 'slack_log_device/rails_exceptions_logging'
119
+ require 'slack_log_device/formatter'
120
+
121
+ if defined?(ActionDispatch::DebugExceptions)
122
+ ActionDispatch::DebugExceptions.prepend(SlackLogDevice::RailsExceptionsLogging)
123
+ end
@@ -0,0 +1,277 @@
1
+ require 'spec_helper'
2
+
3
+ describe SlackLogDevice::Formatter do
4
+
5
+ let(:max_message_length) { SlackLogDevice::Formatter::MAX_MESSAGE_LENGTH }
6
+
7
+ describe '::MAX_MESSAGE_LENGTH' do
8
+
9
+ it 'is 4000' do
10
+ expect(SlackLogDevice::Formatter::MAX_MESSAGE_LENGTH).to be(4000)
11
+ end
12
+
13
+ end
14
+
15
+ describe '#call' do
16
+
17
+ context "with no block or metadata" do
18
+
19
+ let(:formatter) { SlackLogDevice::Formatter.new }
20
+
21
+ it 'returns a formatted message' do
22
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*: Hello World')
23
+ end
24
+
25
+ it 'message is stripped' do
26
+ expect(formatter.call('DEBUG', Time.now, ' ', " \nHello World ")).to eq('*`DEBUG`*: Hello World')
27
+ end
28
+
29
+ it 'message is converted to string' do
30
+ expect(formatter.call('DEBUG', Time.now, ' ', 42)).to eq('*`DEBUG`*: 42')
31
+ end
32
+
33
+ it 'includes progname if given' do
34
+ expect(formatter.call('DEBUG', Time.now, 'My App', 'Hello World')).to eq('*`DEBUG`* (*My App*): Hello World')
35
+ end
36
+
37
+ it 'formats exception' do
38
+ exception = RuntimeError.new('BAM!')
39
+ exception.set_backtrace(['foo', 'bar'])
40
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: BAM!\n\n```foo\nbar```")
41
+ end
42
+
43
+ it 'formats exception with no message' do
44
+ exception = RuntimeError.new(' ')
45
+ exception.set_backtrace(['foo', 'bar'])
46
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred:\n\n```foo\nbar```")
47
+ end
48
+
49
+ it 'formats exception with no backtrace' do
50
+ exception = RuntimeError.new('BAM!')
51
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: BAM!")
52
+ end
53
+
54
+ it 'strips exception message' do
55
+ exception = RuntimeError.new(" BAM! \n")
56
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: BAM!")
57
+ end
58
+
59
+ it 'message never exceed 4000 chars (without exception)' do
60
+ message = formatter.call('DEBUG', Time.now, nil, 'Bam' * (max_message_length / 2))
61
+ expect(message.size).to eq(max_message_length)
62
+ expect(message).to end_with('BamBa...')
63
+ end
64
+
65
+ it 'message never exceed 4000 chars (with exception)' do
66
+ exception = RuntimeError.new('BAM!')
67
+ exception.set_backtrace(['a' * (max_message_length - 49)])
68
+ message = formatter.call('DEBUG', Time.now, nil, exception)
69
+ expect(message.size).to eq(max_message_length)
70
+ expect(message).to end_with("aaaaaa...```")
71
+ end
72
+
73
+ it 'can be exactly 4000 chars with trace (without three dots)' do
74
+ exception = RuntimeError.new('BAM!')
75
+ exception.set_backtrace(['a' * (max_message_length - 50)])
76
+ message = formatter.call('DEBUG', Time.now, nil, exception)
77
+ expect(message.size).to eq(max_message_length)
78
+ expect(message).to end_with('a```')
79
+ end
80
+
81
+ it 'does not add three dots if less than 4000' do
82
+ exception = RuntimeError.new('BAM!')
83
+ exception.set_backtrace(['a' * (max_message_length - 51)])
84
+ message = formatter.call('DEBUG', Time.now, nil, exception)
85
+ expect(message.size).to eq(max_message_length - 1)
86
+ expect(message).to end_with('a```')
87
+ end
88
+
89
+ it 'does not format backtrace if message is too long' do
90
+ exception = RuntimeError.new('BAM!' * max_message_length)
91
+ exception.set_backtrace(['hello world'])
92
+ message = formatter.call('DEBUG', Time.now, nil, exception)
93
+ expect(message.size).to eq(max_message_length)
94
+ expect(message).not_to include('hello')
95
+ expect(message).not_to include('```')
96
+ end
97
+
98
+ it 'message does not exceed 4000 if there is no backtrace' do
99
+ exception = RuntimeError.new('BAM!' * max_message_length)
100
+ message = formatter.call('DEBUG', Time.now, nil, exception)
101
+ expect(message.size).to eq(max_message_length)
102
+ end
103
+
104
+ it 'does not formats a blank backtrace (due to large message)' do
105
+ exception = RuntimeError.new('a' * (max_message_length - 46))
106
+ exception.set_backtrace(['hello world'])
107
+ message = formatter.call('DEBUG', Time.now, nil, exception)
108
+ expect(message.size).to eq(max_message_length - 8)
109
+ expect(message).not_to include('```')
110
+ expect(message).to end_with('a')
111
+ end
112
+
113
+ it 'can format a backtrace with just one char' do
114
+ exception = RuntimeError.new('a' * (max_message_length - 47))
115
+ exception.set_backtrace(['hello world'])
116
+ message = formatter.call('DEBUG', Time.now, nil, exception)
117
+ expect(message.size).to eq(max_message_length)
118
+ expect(message).to end_with("aaa\n\n```h```")
119
+ end
120
+
121
+ it 'can format a backtrace with just one char and three dots' do
122
+ exception = RuntimeError.new('a' * (max_message_length - 50))
123
+ exception.set_backtrace(['hello world'])
124
+ message = formatter.call('DEBUG', Time.now, nil, exception)
125
+ expect(message.size).to eq(max_message_length)
126
+ expect(message).to end_with("aaa\n\n```h...```")
127
+ end
128
+
129
+ end
130
+
131
+ context 'if a block is given' do
132
+
133
+ let(:formatter) { SlackLogDevice::Formatter.new { |message| "#{message.reverse} Hey hoy" } }
134
+
135
+ it 'returns formatter message with block invoked' do
136
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq("*`DEBUG`*: dlroW olleH Hey hoy")
137
+ end
138
+
139
+ it 'invokes block with stripped message' do
140
+ expect(formatter.call('DEBUG', Time.now, ' ', " Hello World \t")).to eq("*`DEBUG`*: dlroW olleH Hey hoy")
141
+ end
142
+
143
+ it 'does not append block message if blank' do
144
+ formatter = SlackLogDevice.formatter { ' ' }
145
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*:')
146
+ end
147
+
148
+ it 'converts block value to string' do
149
+ formatter = SlackLogDevice.formatter { 42 }
150
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*: 42')
151
+ end
152
+
153
+ it 'is correct if block returns nil' do
154
+ formatter = SlackLogDevice.formatter { nil }
155
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*:')
156
+ end
157
+
158
+ it 'strips block return value' do
159
+ formatter = SlackLogDevice.formatter { " hey \n" }
160
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*: hey')
161
+ end
162
+
163
+ it 'includes progname if given' do
164
+ expect(formatter.call('DEBUG', Time.now, 'MyApp', 'Hello World')).to eq("*`DEBUG`* (*MyApp*): dlroW olleH Hey hoy")
165
+ end
166
+
167
+ it 'formats exception' do
168
+ exception = RuntimeError.new('BAM!')
169
+ exception.set_backtrace(['foo', 'bar'])
170
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: !MAB Hey hoy\n\n```foo\nbar```")
171
+ end
172
+
173
+ it 'is correct with exception if block returns a blank message' do
174
+ formatter = SlackLogDevice.formatter { ' ' }
175
+ exception = RuntimeError.new('BAM!')
176
+ exception.set_backtrace(['foo', 'bar'])
177
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred:\n\n```foo\nbar```")
178
+ end
179
+
180
+ it 'message never exceed 4000 chars (without exception)' do
181
+ message = formatter.call('DEBUG', Time.now, ' ', 'Hello World' * (max_message_length / 3))
182
+ expect(message.size).to eq(max_message_length)
183
+ expect(message).to end_with('olleHdlro...')
184
+ end
185
+
186
+ it 'message never exceed 4000 chars (with exception)' do
187
+ exception = RuntimeError.new('BAM!')
188
+ exception.set_backtrace(['a' * max_message_length])
189
+ message = formatter.call('DEBUG', Time.now, 'My App', exception)
190
+ expect(message.size).to eq(max_message_length)
191
+ expect(message).to end_with("aaaaaa...```")
192
+ end
193
+
194
+ end
195
+
196
+ context 'with extra metadata' do
197
+
198
+ let(:extra_metadata) {{
199
+ 'User ' => " `#{user}` ",
200
+ ' Reversed user' => -> (options) { user.reverse },
201
+ }}
202
+ let(:formatter) { SlackLogDevice::Formatter.new(extra_metadata: extra_metadata) }
203
+ let(:user) { 'John' }
204
+
205
+ it 'returns a message formatted' do
206
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq("*`DEBUG`*: Hello World\n\n• *User*: `John`\n• *Reversed user*: nhoJ")
207
+ end
208
+
209
+ it 'returns a message formatted (with exception)' do
210
+ exception = RuntimeError.new('BAM!')
211
+ exception.set_backtrace(['a', 'b'])
212
+ message = formatter.call('DEBUG', Time.now, 'My App', exception)
213
+ expect(message).to eq("*`DEBUG`* (*My App*): A `RuntimeError` occurred: BAM!\n\n• *User*: `John`\n• *Reversed user*: nhoJ\n\n```a\nb```")
214
+ end
215
+
216
+ it 'extra metadata are not added if message is too long' do
217
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World' * max_message_length)).not_to include('•')
218
+ end
219
+
220
+ it 'extra metadata are not added if message is too long (with exception)' do
221
+ exception = RuntimeError.new('BAM!' * max_message_length)
222
+ exception.set_backtrace(['a'])
223
+ message = formatter.call('DEBUG', Time.now, 'My App', exception)
224
+ expect(message).not_to include('•')
225
+ end
226
+
227
+ it 'blocks of extra metadata is invoked with options containing exception' do
228
+ extra_metadata['Exception class'] = -> (options) { options[:exception].class.name }
229
+ exception = RuntimeError.new('BAM!')
230
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to include('• *Exception class*: RuntimeError')
231
+ end
232
+
233
+ it 'blocks of extra metadata does not add exception option if not present' do
234
+ extra_metadata['Exception?'] = -> (options) { options.key?(:exception) ? 'Yes' : 'No' }
235
+ expect(formatter.call('DEBUG', Time.now, nil, 'Hello World')).to include("• *Exception?*: No")
236
+ end
237
+
238
+ end
239
+
240
+ context 'with an exception with @__slack_log_device_request set' do
241
+
242
+ let(:exception) { RuntimeError.new('BAM!').tap { |e| e.instance_variable_set(:@__slack_log_device_request, request) } }
243
+ let(:formatter) { SlackLogDevice::Formatter.new }
244
+ let(:request) { double(remote_addr: '127.0.0.1', user_agent: 'Mozilla', method: 'GET', url: 'http://google.com') }
245
+
246
+ it 'logs metadata' do
247
+ expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: BAM!\n\n• *Method*: `GET`\n• *URL*: `http://google.com`\n• *Remote address*: `127.0.0.1`\n• *User-Agent*: `Mozilla`")
248
+ end
249
+
250
+ end
251
+
252
+ end
253
+
254
+ describe '#extra_metadata' do
255
+
256
+ it 'is an empty hash by default' do
257
+ expect(SlackLogDevice::Formatter.new.extra_metadata).to eq({})
258
+ end
259
+
260
+ it 'can be specified at constructor' do
261
+ formatter = SlackLogDevice::Formatter.new(extra_metadata: { 'Bar' => 'foo' })
262
+ expect(formatter.extra_metadata).to eq({ 'Bar' => 'foo' })
263
+ end
264
+
265
+ end
266
+
267
+ describe '#initialize' do
268
+
269
+ it 'raise an error if an invalid option is given' do
270
+ expect {
271
+ SlackLogDevice::Formatter.new(foo: 'bar')
272
+ }.to raise_error(ArgumentError, 'Unknown key: :foo. Valid keys are: :extra_metadata')
273
+ end
274
+
275
+ end
276
+
277
+ end
@@ -13,46 +13,20 @@ describe SlackLogDevice do
13
13
  expect(device).not_to be_a(Logger::LogDevice)
14
14
  end
15
15
 
16
- describe '::FORMATTER' do
16
+ describe '.formatter' do
17
17
 
18
- let(:formatter) { SlackLogDevice::FORMATTER }
19
-
20
- it 'returns a proc' do
21
- expect(formatter).to be_a(Proc)
18
+ it 'returns a SlackLogDevice::Formatter' do
19
+ expect(SlackLogDevice.formatter).to be_a(SlackLogDevice::Formatter)
22
20
  end
23
21
 
24
- describe '#call' do
25
-
26
- it 'returns a formatted message' do
27
- expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq('*`DEBUG`*: Hello World')
28
- end
29
-
30
- it 'includes progname if given' do
31
- expect(formatter.call('DEBUG', Time.now, 'My App', 'Hello World')).to eq('*`DEBUG`* (*My App*): Hello World')
32
- end
33
-
34
- it 'formats exception' do
35
- exception = RuntimeError.new('BAM!')
36
- exception.set_backtrace(['foo', 'bar'])
37
- expect(formatter.call('DEBUG', Time.now, nil, exception)).to eq("*`DEBUG`*: A `RuntimeError` occurred: BAM!\n\n```foo\nbar```")
38
- end
39
-
40
- it 'message with trace never exceed 4000 chars' do
41
- exception = RuntimeError.new('BAM!')
42
- exception.set_backtrace(['a' * 4500])
43
- message = formatter.call('DEBUG', Time.now, 'My App', exception)
44
- expect(message.size).to eq(4000)
45
- expect(message).to end_with("aaaaaa...```")
46
- end
47
-
48
- it 'can be exactly 4000 chars (without three dots)' do
49
- exception = RuntimeError.new('BAM!')
50
- exception.set_backtrace(['a' * 3950])
51
- message = formatter.call('DEBUG', Time.now, nil, exception)
52
- expect(message).to end_with('a```')
53
- expect(message.size).to eq(4000)
54
- end
22
+ it 'block is given to formatter constructor' do
23
+ formatter = SlackLogDevice.formatter { |message| message.reverse }
24
+ expect(formatter.call('DEBUG', Time.now, ' ', 'Hello World')).to eq("*`DEBUG`*: dlroW olleH")
25
+ end
55
26
 
27
+ it 'options are forwarded to formatter constructor' do
28
+ formatter = SlackLogDevice.formatter(extra_metadata: { 'bar' => 'foo' })
29
+ expect(formatter.extra_metadata).to eq({ 'bar' => 'foo' })
56
30
  end
57
31
 
58
32
  end
@@ -186,7 +160,7 @@ describe SlackLogDevice do
186
160
  device.write('BIM!')
187
161
  expect {
188
162
  device.flush
189
- }.to change { device.instance_variable_get(:@buffer) }.to([])
163
+ }.to change { device.instance_variable_get(:@buffer) }.to('')
190
164
  expect(HTTParty).not_to receive(:post)
191
165
  device.flush
192
166
  end
@@ -197,17 +171,17 @@ describe SlackLogDevice do
197
171
 
198
172
  it 'is true if max_buffer_size is reached' do
199
173
  options[:max_buffer_size] = 10
200
- device.write('012345678')
174
+ device.write('0123456789')
201
175
  expect {
202
- device.instance_variable_get(:@buffer).push('a')
176
+ device.instance_variable_get(:@buffer).send(:<<, 'a')
203
177
  }.to change { device.flush? }.from(false).to(true)
204
178
  end
205
179
 
206
180
  it 'use byte size to compare max_buffer_size' do
207
181
  options[:max_buffer_size] = 10
208
- device.write('0123456é')
182
+ device.write('01234567é')
209
183
  expect {
210
- device.instance_variable_get(:@buffer).push('a')
184
+ device.instance_variable_get(:@buffer).send(:<<, 'a')
211
185
  }.to change { device.flush? }.from(false).to(true)
212
186
  end
213
187
 
@@ -223,6 +197,12 @@ describe SlackLogDevice do
223
197
  }.to change { device.flush? }.from(false).to(true)
224
198
  end
225
199
 
200
+ it 'is true if it contains markdown code block' do
201
+ expect {
202
+ device.instance_variable_get(:@buffer).send(:<<, '```hello world```')
203
+ }.to change { device.flush? }.from(false).to(true)
204
+ end
205
+
226
206
  end
227
207
 
228
208
  describe '#flush_delay' do
@@ -486,12 +466,19 @@ describe SlackLogDevice do
486
466
 
487
467
  it 'strips message' do
488
468
  device.write(" BAM !\n")
489
- expect(device.instance_variable_get(:@buffer)).to eq(['BAM !'])
469
+ expect(device.instance_variable_get(:@buffer)).to eq('BAM !')
490
470
  end
491
471
 
492
472
  it 'converts message to string' do
493
473
  device.write(42)
494
- expect(device.instance_variable_get(:@buffer)).to eq(['42'])
474
+ expect(device.instance_variable_get(:@buffer)).to eq('42')
475
+ end
476
+
477
+ it 'adds given string to existing buffer with a \n' do
478
+ device.write('BAM!')
479
+ expect {
480
+ device.write('BIM!')
481
+ }.to change { device.instance_variable_get(:@buffer) }.from('BAM!').to("BAM!\nBIM!")
495
482
  end
496
483
 
497
484
  it 'does nothing if message is blank' do
@@ -504,7 +491,7 @@ describe SlackLogDevice do
504
491
  it 'does nothing if message is nil' do
505
492
  expect(HTTParty).not_to receive(:post)
506
493
  expect(device.write(nil)).to be_nil
507
- expect(device.instance_variable_get(:@buffer)).to eq([])
494
+ expect(device.instance_variable_get(:@buffer)).to eq('')
508
495
  end
509
496
 
510
497
  it 'does not post HTTP message if auto flush is false' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack_log_device
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Toulotte
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-24 00:00:00.000000000 Z
11
+ date: 2017-01-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -124,7 +124,10 @@ files:
124
124
  - Rakefile
125
125
  - VERSION
126
126
  - lib/slack_log_device.rb
127
+ - lib/slack_log_device/formatter.rb
128
+ - lib/slack_log_device/rails_exceptions_logging.rb
127
129
  - slack_log_device.gemspec
130
+ - spec/slack_log_device/formatter_spec.rb
128
131
  - spec/slack_log_device_spec.rb
129
132
  - spec/spec_helper.rb
130
133
  homepage: https://github.com/alexistoulotte/slack_log_device
@@ -152,5 +155,6 @@ signing_key:
152
155
  specification_version: 4
153
156
  summary: LogDevice for Slack
154
157
  test_files:
158
+ - spec/slack_log_device/formatter_spec.rb
155
159
  - spec/slack_log_device_spec.rb
156
160
  - spec/spec_helper.rb