semantic_logger 2.21.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/lib/semantic_logger.rb +13 -9
- data/lib/semantic_logger/appender/base.rb +20 -24
- data/lib/semantic_logger/appender/bugsnag.rb +37 -53
- data/lib/semantic_logger/appender/elasticsearch.rb +53 -0
- data/lib/semantic_logger/appender/file.rb +17 -3
- data/lib/semantic_logger/appender/graylog.rb +123 -0
- data/lib/semantic_logger/appender/http.rb +169 -0
- data/lib/semantic_logger/appender/mongodb.rb +90 -111
- data/lib/semantic_logger/appender/new_relic.rb +38 -72
- data/lib/semantic_logger/appender/splunk.rb +10 -5
- data/lib/semantic_logger/appender/splunk_http.rb +99 -0
- data/lib/semantic_logger/appender/syslog.rb +116 -103
- data/lib/semantic_logger/appender/wrapper.rb +26 -11
- data/lib/semantic_logger/base.rb +13 -16
- data/lib/semantic_logger/log.rb +85 -5
- data/lib/semantic_logger/logger.rb +1 -1
- data/lib/semantic_logger/semantic_logger.rb +24 -0
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender/bugsnag_test.rb +39 -41
- data/test/appender/graylog_test.rb +63 -0
- data/test/appender/http_test.rb +61 -0
- data/test/appender/mongodb_test.rb +20 -20
- data/test/appender/new_relic_test.rb +4 -3
- data/test/appender/splunk_http_test.rb +78 -0
- data/test/logger_test.rb +1 -1
- metadata +27 -3
data/lib/semantic_logger/base.rb
CHANGED
@@ -328,18 +328,23 @@ module SemanticLogger
|
|
328
328
|
end
|
329
329
|
|
330
330
|
# Add caller stack trace
|
331
|
-
backtrace =
|
332
|
-
if index >= SemanticLogger.backtrace_level_index
|
333
|
-
trace = caller
|
334
|
-
# Remove call to this internal method
|
335
|
-
trace.shift(1)
|
336
|
-
trace
|
337
|
-
end
|
331
|
+
backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
|
338
332
|
|
339
333
|
struct = Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception, nil, backtrace)
|
340
334
|
log(struct) if include_message?(struct)
|
341
335
|
end
|
342
336
|
|
337
|
+
SELF_PATTERN = File.join('lib', 'semantic_logger')
|
338
|
+
|
339
|
+
# Extract the callers backtrace leaving out Semantic Logger
|
340
|
+
def extract_backtrace
|
341
|
+
stack = caller
|
342
|
+
while (first = stack.first) && first.include?(SELF_PATTERN)
|
343
|
+
stack.shift
|
344
|
+
end
|
345
|
+
stack
|
346
|
+
end
|
347
|
+
|
343
348
|
# Measure the supplied block and log the message
|
344
349
|
def benchmark_internal(level, index, message, params)
|
345
350
|
start = Time.now
|
@@ -408,15 +413,7 @@ module SemanticLogger
|
|
408
413
|
elsif duration >= min_duration
|
409
414
|
# Only log if the block took longer than 'min_duration' to complete
|
410
415
|
# Add caller stack trace
|
411
|
-
backtrace =
|
412
|
-
if index >= SemanticLogger.backtrace_level_index
|
413
|
-
trace = caller
|
414
|
-
# Remove call to this internal method
|
415
|
-
trace.shift
|
416
|
-
# Ruby 1.9 has additional stack entry for parent that calls this method
|
417
|
-
trace.shift if RUBY_VERSION.to_f <= 2.0
|
418
|
-
trace
|
419
|
-
end
|
416
|
+
backtrace = extract_backtrace if index >= SemanticLogger.backtrace_level_index
|
420
417
|
|
421
418
|
struct = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace)
|
422
419
|
log(struct) if include_message?(struct)
|
data/lib/semantic_logger/log.rb
CHANGED
@@ -64,6 +64,19 @@ module SemanticLogger
|
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
67
|
+
# Returns [String] the exception backtrace including all of the child / caused by exceptions
|
68
|
+
def backtrace_to_s
|
69
|
+
trace = ''
|
70
|
+
each_exception do |exception, i|
|
71
|
+
if i == 0
|
72
|
+
trace = (exception.backtrace || []).join("\n")
|
73
|
+
else
|
74
|
+
trace << "\nCause: #{exception.class.name}: #{exception.message}\n#{(exception.backtrace || []).join("\n")}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
trace
|
78
|
+
end
|
79
|
+
|
67
80
|
# Returns [String] duration of the log entry as a string
|
68
81
|
# Returns nil if their is no duration
|
69
82
|
# Java time precision does not include microseconds
|
@@ -89,7 +102,7 @@ module SemanticLogger
|
|
89
102
|
elsif seconds >= 60.0 # 1 minute
|
90
103
|
Time.at(seconds).strftime('%-Mm %-Ss')
|
91
104
|
elsif seconds >= 1.0 # 1 second
|
92
|
-
|
105
|
+
"#{'%.3f' % seconds}s"
|
93
106
|
else
|
94
107
|
duration_to_s
|
95
108
|
end
|
@@ -104,18 +117,26 @@ module SemanticLogger
|
|
104
117
|
# Example:
|
105
118
|
# 18934:thread 23 test_logging.rb:51
|
106
119
|
def process_info(thread_name_length = 30)
|
107
|
-
file, line = file_name_and_line
|
120
|
+
file, line = file_name_and_line(true)
|
108
121
|
file_name = " #{file}:#{line}" if file
|
109
122
|
|
110
123
|
"#{$$}:#{"%.#{thread_name_length}s" % thread_name}#{file_name}"
|
111
124
|
end
|
112
125
|
|
126
|
+
CALLER_REGEXP = /^(.*):(\d+).*/
|
127
|
+
|
128
|
+
# Extract the filename and line number from the last entry in the supplied backtrace
|
129
|
+
def extract_file_and_line(stack, short_name = false)
|
130
|
+
match = CALLER_REGEXP.match(stack.first)
|
131
|
+
[short_name ? File.basename(match[1]) : match[1], match[2].to_i]
|
132
|
+
end
|
133
|
+
|
113
134
|
# Returns [String, String] the file_name and line_number from the backtrace supplied
|
114
135
|
# in either the backtrace or exception
|
115
|
-
def file_name_and_line
|
136
|
+
def file_name_and_line(short_name = false)
|
116
137
|
if backtrace || (exception && exception.backtrace)
|
117
|
-
|
118
|
-
|
138
|
+
stack = backtrace || exception.backtrace
|
139
|
+
extract_file_and_line(stack, short_name) if stack && stack.size > 0
|
119
140
|
end
|
120
141
|
end
|
121
142
|
|
@@ -148,6 +169,65 @@ module SemanticLogger
|
|
148
169
|
end
|
149
170
|
end
|
150
171
|
|
172
|
+
# Returns [Hash] representation of this log entry
|
173
|
+
def to_h
|
174
|
+
# Header
|
175
|
+
h = {
|
176
|
+
host: SemanticLogger.host,
|
177
|
+
application: SemanticLogger.application,
|
178
|
+
name: name,
|
179
|
+
pid: $$,
|
180
|
+
thread: thread_name,
|
181
|
+
time: time,
|
182
|
+
level: level,
|
183
|
+
level_index: level_index,
|
184
|
+
}
|
185
|
+
file, line = file_name_and_line
|
186
|
+
if file
|
187
|
+
h[:file] = file
|
188
|
+
h[:line] = line.to_i
|
189
|
+
end
|
190
|
+
|
191
|
+
# Tags
|
192
|
+
h[:tags] = tags if tags && (tags.size > 0)
|
193
|
+
|
194
|
+
# Duration
|
195
|
+
if duration
|
196
|
+
h[:duration_ms] = duration
|
197
|
+
h[:duration] = duration_human
|
198
|
+
end
|
199
|
+
|
200
|
+
# Log message
|
201
|
+
h[:message] = cleansed_message if message
|
202
|
+
|
203
|
+
# Payload
|
204
|
+
if payload
|
205
|
+
if payload.is_a?(Hash)
|
206
|
+
h.merge!(payload)
|
207
|
+
else
|
208
|
+
h[:payload] = payload
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Exceptions
|
213
|
+
if exception
|
214
|
+
root = h
|
215
|
+
each_exception do |exception, i|
|
216
|
+
name = i == 0 ? :exception : :cause
|
217
|
+
root[name] = {
|
218
|
+
name: exception.class.name,
|
219
|
+
message: exception.message,
|
220
|
+
stack_trace: exception.backtrace
|
221
|
+
}
|
222
|
+
root = root[name]
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Metric
|
227
|
+
h[:metric] = metric if metric
|
228
|
+
h
|
229
|
+
end
|
230
|
+
|
151
231
|
end
|
152
232
|
|
153
233
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'concurrent'
|
2
|
+
require 'socket'
|
3
|
+
|
2
4
|
module SemanticLogger
|
3
5
|
# Logging levels in order of most detailed to most severe
|
4
6
|
LEVELS = [:trace, :debug, :info, :warn, :error, :fatal]
|
@@ -47,6 +49,28 @@ module SemanticLogger
|
|
47
49
|
@@backtrace_level_index
|
48
50
|
end
|
49
51
|
|
52
|
+
# Returns [String] name of this host for logging purposes
|
53
|
+
# Note: Not all appenders use `host`
|
54
|
+
def self.host
|
55
|
+
@@host ||= Socket.gethostname
|
56
|
+
end
|
57
|
+
|
58
|
+
# Override the default host name
|
59
|
+
def self.host=(host)
|
60
|
+
@@host = host
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns [String] name of this application for logging purposes
|
64
|
+
# Note: Not all appenders use `application`
|
65
|
+
def self.application
|
66
|
+
@@application ||= 'Semantic Logger'
|
67
|
+
end
|
68
|
+
|
69
|
+
# Override the default application
|
70
|
+
def self.application=(application)
|
71
|
+
@@application = application
|
72
|
+
end
|
73
|
+
|
50
74
|
# Add a new logging appender as a new destination for all log messages
|
51
75
|
# emitted from Semantic Logger
|
52
76
|
#
|
@@ -5,61 +5,59 @@ module Appender
|
|
5
5
|
class BugsnagTest < Minitest::Test
|
6
6
|
describe SemanticLogger::Appender::Bugsnag do
|
7
7
|
before do
|
8
|
-
@appender = SemanticLogger::Appender::Bugsnag.new(:
|
8
|
+
@appender = SemanticLogger::Appender::Bugsnag.new(:info)
|
9
9
|
@message = 'AppenderBugsnagTest log message'
|
10
10
|
end
|
11
11
|
|
12
12
|
(SemanticLogger::LEVELS - [:warn, :error]).each do |level|
|
13
|
-
it "
|
13
|
+
it "sends #{level} message" do
|
14
14
|
exception = hash = nil
|
15
15
|
Bugsnag.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
16
|
-
@appender.send(level,
|
16
|
+
@appender.send(level, @message)
|
17
|
+
end
|
18
|
+
if [:trace, :debug].include?(level)
|
19
|
+
assert_equal nil, exception
|
20
|
+
assert_equal nil, hash
|
21
|
+
else
|
22
|
+
assert_equal 'RuntimeError', exception.class.to_s
|
23
|
+
assert_equal @message, exception.message
|
24
|
+
assert_equal level.to_s, hash[:severity]
|
17
25
|
end
|
18
|
-
assert_nil exception
|
19
|
-
assert_nil hash
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'send error notifications to Bugsnag with severity' do
|
24
|
-
exception = hash = nil
|
25
|
-
Bugsnag.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
26
|
-
@appender.error @message
|
27
26
|
end
|
28
|
-
assert_equal 'RuntimeError', exception.class.to_s
|
29
|
-
assert_equal @message, exception.message
|
30
|
-
assert_equal 'error', hash[:severity]
|
31
|
-
end
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
it "sends #{level} custom attributes" do
|
29
|
+
exception = hash = nil
|
30
|
+
Bugsnag.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
31
|
+
@appender.send(level, @message, {key1: 1, key2: 'a'})
|
32
|
+
end
|
33
|
+
if [:trace, :debug].include?(level)
|
34
|
+
assert_equal nil, exception
|
35
|
+
assert_equal nil, hash
|
36
|
+
else
|
37
|
+
assert_equal 'RuntimeError', exception.class.to_s
|
38
|
+
assert_equal @message, exception.message
|
39
|
+
assert_equal 1, hash[:key1], hash
|
40
|
+
assert_equal 'a', hash[:key2], hash
|
41
|
+
end
|
37
42
|
end
|
38
|
-
assert_equal 'RuntimeError', exception.class.to_s
|
39
|
-
assert_equal @message, exception.message
|
40
|
-
assert_equal 'warning', hash[:severity]
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
it "sends #{level} exceptions" do
|
45
|
+
error = RuntimeError.new('Hello World')
|
46
|
+
exception = hash = nil
|
47
|
+
Bugsnag.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
48
|
+
@appender.send(level, @message, error)
|
49
|
+
end
|
50
|
+
if [:trace, :debug].include?(level)
|
51
|
+
assert_equal nil, exception
|
52
|
+
assert_equal nil, hash
|
53
|
+
else
|
54
|
+
assert_equal error.class.to_s, exception.class.to_s
|
55
|
+
assert_equal error.message, exception.message
|
56
|
+
assert_equal @message, hash[:message], hash
|
57
|
+
end
|
47
58
|
end
|
48
|
-
assert_equal 'RuntimeError', exception.class.to_s
|
49
|
-
assert_equal @message, exception.message
|
50
|
-
assert_equal(1, hash[:key1], hash)
|
51
|
-
assert_equal('a', hash[:key2], hash)
|
52
59
|
end
|
53
60
|
|
54
|
-
it 'send notification to Bugsnag with exception' do
|
55
|
-
error = RuntimeError.new('Hello World')
|
56
|
-
exception = hash = nil
|
57
|
-
Bugsnag.stub(:notify, -> exc, h { exception = exc; hash = h }) do
|
58
|
-
@appender.error error
|
59
|
-
end
|
60
|
-
assert_equal error.class.to_s, exception.class.to_s
|
61
|
-
assert_equal error.message, exception.message
|
62
|
-
end
|
63
61
|
end
|
64
62
|
end
|
65
63
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
# Unit Test for SemanticLogger::Appender::Graylog
|
4
|
+
module Appender
|
5
|
+
class GraylogTest < Minitest::Test
|
6
|
+
describe SemanticLogger::Appender::Graylog do
|
7
|
+
before do
|
8
|
+
@appender = SemanticLogger::Appender::Graylog.new(level: :info)
|
9
|
+
@message = 'AppenderGraylogTest log message'
|
10
|
+
end
|
11
|
+
|
12
|
+
(SemanticLogger::LEVELS - [:info, :warn, :error, :fatal]).each do |level|
|
13
|
+
it "not send :#{level} notifications to Graylog" do
|
14
|
+
hash = nil
|
15
|
+
@appender.notifier.stub(:notify!, -> h { hash = h }) do
|
16
|
+
@appender.send(level, "AppenderGraylogTest #{level.to_s} message")
|
17
|
+
end
|
18
|
+
assert_nil hash
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'send exception notifications to Graylog with severity' do
|
23
|
+
hash = nil
|
24
|
+
exc = nil
|
25
|
+
begin
|
26
|
+
Uh oh
|
27
|
+
rescue Exception => e
|
28
|
+
exc = e
|
29
|
+
end
|
30
|
+
@appender.notifier.stub(:notify!, -> h { hash = h }) do
|
31
|
+
@appender.error 'Reading File', exc
|
32
|
+
end
|
33
|
+
assert 'Reading File', hash[:short_message]
|
34
|
+
assert 'NameError', hash[:exception][:name]
|
35
|
+
assert 'undefined local variable or method', hash[:exception][:message]
|
36
|
+
assert_equal 3, hash[:level], 'Should be error level (3)'
|
37
|
+
assert hash[:exception][:stack_trace].first.include?(__FILE__), hash[:exception]
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'send error notifications to Graylog with severity' do
|
41
|
+
hash = nil
|
42
|
+
@appender.notifier.stub(:notify!, -> h { hash = h }) do
|
43
|
+
@appender.error @message
|
44
|
+
end
|
45
|
+
assert_equal @message, hash[:short_message]
|
46
|
+
assert_equal 3, hash[:level]
|
47
|
+
refute hash[:stack_trace]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'send notification to Graylog with custom attributes' do
|
51
|
+
hash = nil
|
52
|
+
@appender.notifier.stub(:notify!, -> h { hash = h }) do
|
53
|
+
@appender.error @message, {key1: 1, key2: 'a'}
|
54
|
+
end
|
55
|
+
assert_equal @message, hash[:short_message]
|
56
|
+
assert_equal 3, hash[:level]
|
57
|
+
refute hash[:stack_trace]
|
58
|
+
assert_equal(1, hash[:key1], hash)
|
59
|
+
assert_equal('a', hash[:key2], hash)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
# Unit Test for SemanticLogger::Appender::Http
|
4
|
+
module Appender
|
5
|
+
class HttpTest < Minitest::Test
|
6
|
+
response_mock = Struct.new(:code, :body)
|
7
|
+
|
8
|
+
describe SemanticLogger::Appender::Http do
|
9
|
+
before do
|
10
|
+
@appender = SemanticLogger::Appender::Http.new(url: 'http://localhost:8088/path')
|
11
|
+
@message = 'AppenderHttpTest log message'
|
12
|
+
end
|
13
|
+
|
14
|
+
SemanticLogger::LEVELS.each do |level|
|
15
|
+
it "send #{level}" do
|
16
|
+
request = nil
|
17
|
+
@appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
|
18
|
+
@appender.send(level, @message)
|
19
|
+
end
|
20
|
+
hash = JSON.parse(request.body)
|
21
|
+
assert_equal @message, hash['message']
|
22
|
+
assert_equal level.to_s, hash['level']
|
23
|
+
refute hash['stack_trace']
|
24
|
+
end
|
25
|
+
|
26
|
+
it "send #{level} exceptions" do
|
27
|
+
exc = nil
|
28
|
+
begin
|
29
|
+
Uh oh
|
30
|
+
rescue Exception => e
|
31
|
+
exc = e
|
32
|
+
end
|
33
|
+
request = nil
|
34
|
+
@appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
|
35
|
+
@appender.send(level, 'Reading File', exc)
|
36
|
+
end
|
37
|
+
hash = JSON.parse(request.body)
|
38
|
+
assert 'Reading File', hash['message']
|
39
|
+
assert 'NameError', hash['exception']['name']
|
40
|
+
assert 'undefined local variable or method', hash['exception']['message']
|
41
|
+
assert_equal level.to_s, hash['level'], 'Should be error level (3)'
|
42
|
+
assert hash['exception']['stack_trace'].first.include?(__FILE__), hash['exception']
|
43
|
+
end
|
44
|
+
|
45
|
+
it "send #{level} custom attributes" do
|
46
|
+
request = nil
|
47
|
+
@appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
|
48
|
+
@appender.send(level, @message, {key1: 1, key2: 'a'})
|
49
|
+
end
|
50
|
+
hash = JSON.parse(request.body)
|
51
|
+
assert_equal @message, hash['message']
|
52
|
+
assert_equal level.to_s, hash['level']
|
53
|
+
refute hash['stack_trace']
|
54
|
+
assert_equal 1, hash['key1'], hash
|
55
|
+
assert_equal 'a', hash['key2'], hash
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|