semantic_logger 2.21.0 → 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/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
|