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 +4 -4
- data/README.mdown +8 -1
- data/VERSION +1 -1
- data/lib/slack_log_device/formatter.rb +94 -0
- data/lib/slack_log_device/rails_exceptions_logging.rb +16 -0
- data/lib/slack_log_device.rb +16 -19
- data/spec/slack_log_device/formatter_spec.rb +277 -0
- data/spec/slack_log_device_spec.rb +31 -44
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7da84e6d177382ef3698ba25b1cafddfd7e41e9d
|
4
|
+
data.tar.gz: 9643a365bff66ea7ea8219d38c73dcad7166185e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
data/lib/slack_log_device.rb
CHANGED
@@ -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
|
62
|
-
@buffer
|
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.
|
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 '
|
16
|
+
describe '.formatter' do
|
17
17
|
|
18
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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('
|
174
|
+
device.write('0123456789')
|
201
175
|
expect {
|
202
|
-
device.instance_variable_get(:@buffer).
|
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('
|
182
|
+
device.write('01234567é')
|
209
183
|
expect {
|
210
|
-
device.instance_variable_get(:@buffer).
|
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(
|
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(
|
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:
|
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-
|
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
|