semantic_logger 4.1.1 → 4.2.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/lib/semantic_logger.rb +6 -13
  3. data/lib/semantic_logger/ansi_colors.rb +10 -10
  4. data/lib/semantic_logger/appender.rb +42 -26
  5. data/lib/semantic_logger/appender/async.rb +179 -0
  6. data/lib/semantic_logger/appender/async_batch.rb +95 -0
  7. data/lib/semantic_logger/appender/bugsnag.rb +2 -1
  8. data/lib/semantic_logger/appender/elasticsearch.rb +113 -81
  9. data/lib/semantic_logger/appender/elasticsearch_http.rb +1 -3
  10. data/lib/semantic_logger/appender/file.rb +1 -3
  11. data/lib/semantic_logger/appender/graylog.rb +6 -5
  12. data/lib/semantic_logger/appender/honeybadger.rb +0 -2
  13. data/lib/semantic_logger/appender/http.rb +25 -10
  14. data/lib/semantic_logger/appender/kafka.rb +1 -3
  15. data/lib/semantic_logger/appender/mongodb.rb +1 -3
  16. data/lib/semantic_logger/appender/new_relic.rb +7 -3
  17. data/lib/semantic_logger/appender/sentry.rb +6 -7
  18. data/lib/semantic_logger/appender/splunk.rb +1 -2
  19. data/lib/semantic_logger/appender/splunk_http.rb +3 -4
  20. data/lib/semantic_logger/appender/syslog.rb +1 -3
  21. data/lib/semantic_logger/appender/tcp.rb +7 -9
  22. data/lib/semantic_logger/appender/udp.rb +0 -2
  23. data/lib/semantic_logger/appender/wrapper.rb +0 -2
  24. data/lib/semantic_logger/base.rb +76 -19
  25. data/lib/semantic_logger/formatters.rb +37 -0
  26. data/lib/semantic_logger/formatters/base.rb +10 -3
  27. data/lib/semantic_logger/formatters/json.rb +2 -6
  28. data/lib/semantic_logger/formatters/one_line.rb +18 -0
  29. data/lib/semantic_logger/formatters/raw.rb +8 -2
  30. data/lib/semantic_logger/formatters/signalfx.rb +169 -0
  31. data/lib/semantic_logger/log.rb +23 -14
  32. data/lib/semantic_logger/loggable.rb +88 -15
  33. data/lib/semantic_logger/logger.rb +0 -20
  34. data/lib/semantic_logger/metric/new_relic.rb +75 -0
  35. data/lib/semantic_logger/metric/signalfx.rb +123 -0
  36. data/lib/semantic_logger/{metrics → metric}/statsd.rb +20 -8
  37. data/lib/semantic_logger/processor.rb +67 -169
  38. data/lib/semantic_logger/semantic_logger.rb +7 -31
  39. data/lib/semantic_logger/subscriber.rb +32 -36
  40. data/lib/semantic_logger/utils.rb +47 -0
  41. data/lib/semantic_logger/version.rb +1 -1
  42. data/test/appender/async_batch_test.rb +61 -0
  43. data/test/appender/async_test.rb +45 -0
  44. data/test/appender/elasticsearch_http_test.rb +3 -3
  45. data/test/appender/elasticsearch_test.rb +211 -49
  46. data/test/appender/file_test.rb +9 -8
  47. data/test/appender/mongodb_test.rb +3 -3
  48. data/test/appender/newrelic_rpm.rb +6 -0
  49. data/test/appender/sentry_test.rb +3 -1
  50. data/test/appender/wrapper_test.rb +29 -0
  51. data/test/concerns/compatibility_test.rb +64 -60
  52. data/test/debug_as_trace_logger_test.rb +62 -77
  53. data/test/formatters/one_line_test.rb +61 -0
  54. data/test/formatters/signalfx_test.rb +200 -0
  55. data/test/formatters_test.rb +36 -0
  56. data/test/in_memory_appender.rb +9 -0
  57. data/test/in_memory_appender_helper.rb +43 -0
  58. data/test/in_memory_batch_appender.rb +9 -0
  59. data/test/in_memory_metrics_appender.rb +14 -0
  60. data/test/loggable_test.rb +15 -30
  61. data/test/logger_test.rb +181 -135
  62. data/test/measure_test.rb +212 -113
  63. data/test/metric/new_relic_test.rb +36 -0
  64. data/test/metric/signalfx_test.rb +78 -0
  65. data/test/semantic_logger_test.rb +58 -65
  66. data/test/test_helper.rb +19 -2
  67. metadata +33 -7
  68. data/lib/semantic_logger/metrics/new_relic.rb +0 -30
  69. data/lib/semantic_logger/metrics/udp.rb +0 -80
  70. data/test/mock_logger.rb +0 -29
@@ -0,0 +1,200 @@
1
+ require_relative '../test_helper'
2
+ require 'net/http'
3
+
4
+ module SemanticLogger
5
+ module Formatters
6
+ class SignalfxTest < Minitest::Test
7
+ describe SemanticLogger::Formatters::Signalfx do
8
+ let :average_metric_name do
9
+ 'Application.average'
10
+ end
11
+
12
+ let :counter_metric_name do
13
+ 'Application.counter'
14
+ end
15
+
16
+ let :log do
17
+ metric = '/user/login'
18
+ log = SemanticLogger::Log.new('User', :debug)
19
+ log.metric = metric
20
+ log
21
+ end
22
+
23
+ let :logs do
24
+ 3.times.collect do |i|
25
+ l = log.dup
26
+ l.metric = "/user/login#{i+1}"
27
+ l
28
+ end
29
+ end
30
+
31
+ let :same_logs do
32
+ 3.times.collect do |i|
33
+ l = log.dup
34
+ l.metric = "/user/login"
35
+ l
36
+ end
37
+ end
38
+
39
+ let :dimensions do
40
+ {action: 'hit', user: 'jbloggs', state: 'FL'}
41
+ end
42
+
43
+ let :all_dimensions do
44
+ dims = dimensions.merge(
45
+ host: SemanticLogger.host,
46
+ application: SemanticLogger.application,
47
+ environment: 'test'
48
+ )
49
+ string_keys = {}
50
+ dims.each_pair { |k, v| string_keys[k.to_s] = v }
51
+ string_keys
52
+ end
53
+
54
+ let :appender do
55
+ Net::HTTP.stub_any_instance(:start, true) do
56
+ SemanticLogger::Metric::Signalfx.new(token: 'TEST')
57
+ end
58
+ end
59
+
60
+ let :formatter do
61
+ appender.formatter
62
+ end
63
+
64
+ describe 'format single log' do
65
+ let :result do
66
+ JSON.parse(formatter.call(log, appender))
67
+ end
68
+
69
+ it 'send counter metric when there is no duration' do
70
+ hash = result
71
+ assert counters = hash['counter'], hash
72
+ assert counter = counters.first, hash
73
+ assert_equal counter_metric_name, counter['metric'], counter
74
+ assert_equal 1, counter['value'], counter
75
+ assert_equal (log.time.to_i * 1_000).to_i, counter['timestamp'], counter
76
+ assert counter.has_key?('dimensions')
77
+ end
78
+
79
+ it 'send gauge metric when log includes duration' do
80
+ log.duration = 1234
81
+ hash = result
82
+ assert counters = hash['gauge'], hash
83
+ assert counter = counters.first, hash
84
+ assert_equal average_metric_name, counter['metric'], counter
85
+ assert_equal 1234, counter['value'], counter
86
+ assert_equal (log.time.to_i * 1_000).to_i, counter['timestamp'], counter
87
+ assert counter.has_key?('dimensions')
88
+ end
89
+
90
+ it 'also sends counter metric when gauge metric is sent' do
91
+ log.duration = 1234
92
+ hash = result
93
+ assert counters = hash['counter'], hash
94
+ assert counter = counters.first, hash
95
+ assert_equal counter_metric_name, counter['metric'], counter
96
+ assert_equal 1, counter['value'], counter
97
+ assert_equal (log.time.to_i * 1_000).to_i, counter['timestamp'], counter
98
+ assert counter.has_key?('dimensions')
99
+ end
100
+
101
+ it 'only forwards whitelisted dimensions from named_tags' do
102
+ log.named_tags = {user_id: 47, tracking_number: 7474, session_id: 'hsdhngsd'}
103
+ formatter.dimensions = [:user_id, :application]
104
+ hash = result
105
+ assert counters = hash['counter'], hash
106
+ assert counter = counters.first, hash
107
+ assert_equal({'class' => 'user', 'action' => 'login', 'environment' => 'test', 'user_id' => '47', 'host' => SemanticLogger.host, 'application' => SemanticLogger.application}, counter['dimensions'], counter)
108
+ end
109
+
110
+ it 'raises exception with both a whitelist and blacklist' do
111
+ assert_raises ArgumentError do
112
+ SemanticLogger::Formatters::Signalfx.new(token: 'TEST', dimensions: [:user_id], exclude_dimensions: [:tracking_number])
113
+ end
114
+ end
115
+
116
+ it 'send custom counter metric when there is no duration' do
117
+ log.metric = 'Filter/count'
118
+ log.dimensions = dimensions
119
+ hash = result
120
+
121
+ assert counters = hash['counter'], hash
122
+ assert counter = counters.first, hash
123
+ assert_equal 'Filter.count', counter['metric'], counter
124
+ assert_equal 1, counter['value'], counter
125
+ assert_equal (log.time.to_i * 1_000).to_i, counter['timestamp'], counter
126
+ assert_equal all_dimensions, counter['dimensions']
127
+ end
128
+ end
129
+
130
+ describe 'format batch logs' do
131
+ let :result do
132
+ JSON.parse(formatter.batch(logs, appender))
133
+ end
134
+
135
+ it 'send metrics' do
136
+ hash = result
137
+
138
+ assert counters = hash['counter'], hash
139
+ assert_equal 3, counters.size
140
+ assert_equal counter_metric_name, counters[0]['metric']
141
+ assert_equal 1, counters[0]['value']
142
+ assert_equal counter_metric_name, counters[1]['metric']
143
+ assert_equal counter_metric_name, counters[2]['metric']
144
+ end
145
+
146
+ it 'sends gauge metrics' do
147
+ logs.each { |log| log.duration = 3.5 }
148
+ hash = result
149
+ assert gauges = hash['gauge'], hash
150
+ assert_equal 3, gauges.size
151
+ assert_equal average_metric_name, gauges[0]['metric']
152
+ assert_equal 3.5, gauges[0]['value']
153
+ assert_equal average_metric_name, gauges[1]['metric']
154
+ assert_equal average_metric_name, gauges[2]['metric']
155
+ end
156
+
157
+ describe 'send custom' do
158
+ let :logs do
159
+ 3.times.collect do |i|
160
+ l = log.dup
161
+ l.metric = 'Filter/count'
162
+ l.dimensions = dimensions
163
+ l
164
+ end
165
+ end
166
+
167
+ it 'counter metric when there is no duration' do
168
+ hash = result
169
+
170
+ assert counters = hash['counter'], hash
171
+ assert counter = counters.first, hash
172
+ assert_equal 'Filter.count', counter['metric'], counter
173
+ assert_equal 3, counter['value'], counter
174
+ assert_equal (log.time.to_i * 1_000).to_i, counter['timestamp'], counter
175
+ assert_equal all_dimensions, counter['dimensions']
176
+ end
177
+ end
178
+
179
+ end
180
+
181
+ describe 'format batch logs with aggregation' do
182
+ let :result do
183
+ JSON.parse(formatter.batch(same_logs, appender))
184
+ end
185
+
186
+ it 'sends counter metrics' do
187
+ hash = result
188
+
189
+ assert counters = hash['counter'], hash
190
+ assert_equal 1, counters.size
191
+ assert_equal counter_metric_name, counters[0]['metric']
192
+ assert_equal 3, counters[0]['value']
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'test_helper'
2
+
3
+ class FormattersTest < Minitest::Test
4
+ describe SemanticLogger::Formatters do
5
+ describe '.factory' do
6
+ let :log do
7
+ SemanticLogger::Log.new('Test', :info)
8
+ end
9
+
10
+ let :appender do
11
+ SemanticLogger::Appender::File.new(io: STDOUT)
12
+ end
13
+
14
+ it 'from a symbol' do
15
+ assert formatter = SemanticLogger::Formatters.factory(:raw)
16
+ assert formatter.is_a?(SemanticLogger::Formatters::Raw)
17
+ assert_equal 'Test', formatter.call(log, appender)[:name]
18
+ end
19
+
20
+ it 'from a Hash (Symbol with options)' do
21
+ assert formatter = SemanticLogger::Formatters.factory(raw: {time_format: "%Y%m%d"})
22
+ assert formatter.is_a?(SemanticLogger::Formatters::Raw)
23
+ assert result = formatter.call(log, appender)
24
+ assert_equal 'Test', result[:name]
25
+ assert_equal Time.now.strftime("%Y%m%d"), result[:time]
26
+ end
27
+
28
+ it 'from block' do
29
+ my_formatter = -> log, appender { log.name }
30
+ assert formatter = SemanticLogger::Formatters.factory(my_formatter)
31
+ assert formatter.is_a?(Proc)
32
+ assert_equal 'Test', formatter.call(log, appender)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ # Store in memory the last log message received.
2
+ class InMemoryAppender < SemanticLogger::Subscriber
3
+ attr_accessor :message
4
+
5
+ def log(log)
6
+ self.message = log
7
+ end
8
+ end
9
+
@@ -0,0 +1,43 @@
1
+ require 'minitest/shared_description'
2
+
3
+ InMemoryAppenderHelper = shared_description do
4
+ let :log_message do
5
+ SemanticLogger.flush
6
+ appender.message
7
+ end
8
+
9
+ let :appender do
10
+ InMemoryAppender.new
11
+ end
12
+
13
+ let :thread_name do
14
+ Thread.current.name
15
+ end
16
+
17
+ let :payload do
18
+ {session_id: 'HSSKLEU@JDK767', tracking_number: 12345}
19
+ end
20
+
21
+ let :logger do
22
+ SemanticLogger['TestLogger']
23
+ end
24
+
25
+ let :appender_options do
26
+ {appender: appender}
27
+ end
28
+
29
+ let :added_appender do
30
+ SemanticLogger.add_appender(appender_options)
31
+ end
32
+
33
+ before do
34
+ SemanticLogger.default_level = :trace
35
+ SemanticLogger.backtrace_level = :trace
36
+ SemanticLogger.flush
37
+ added_appender
38
+ end
39
+
40
+ after do
41
+ SemanticLogger.appenders.each { |appender| SemanticLogger.remove_appender(appender) }
42
+ end
43
+ end
@@ -0,0 +1,9 @@
1
+ # Store in memory the last log message received.
2
+ class InMemoryBatchAppender < SemanticLogger::Subscriber
3
+ attr_accessor :message
4
+
5
+ def batch(logs)
6
+ self.message = logs
7
+ end
8
+ end
9
+
@@ -0,0 +1,14 @@
1
+ # Store in memory the last log message received.
2
+ class InMemoryMetricsAppender < SemanticLogger::Subscriber
3
+ attr_accessor :message
4
+
5
+ def log(log)
6
+ self.message = log
7
+ end
8
+
9
+ # Only forward log entries that contain metrics.
10
+ def should_log?(log)
11
+ log.metric && meets_log_level?(log) && !filtered?(log)
12
+ end
13
+ end
14
+
@@ -73,43 +73,28 @@ class AppenderFileTest < Minitest::Test
73
73
  end
74
74
 
75
75
  describe 'logger' do
76
- before do
77
- @time = Time.new
78
- @io = StringIO.new
79
- @appender = SemanticLogger::Appender::File.new(io: @io)
80
- SemanticLogger.default_level = :trace
81
- @mock_logger = MockLogger.new
82
- @appender = SemanticLogger.add_appender(logger: @mock_logger)
83
- @hash = {session_id: 'HSSKLEU@JDK767', tracking_number: 12345}
84
- @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
85
- @thread_name = Thread.current.name
86
- end
87
-
88
- after do
89
- SemanticLogger.remove_appender(@appender)
90
- end
76
+ include InMemoryAppenderHelper
91
77
 
92
78
  describe 'for each log level' do
93
79
  # Ensure that any log level can be logged
94
80
  SemanticLogger::LEVELS.each do |level|
95
- it "log #{level} information with class attribute" do
96
- SemanticLogger.stub(:backtrace_level_index, 0) do
97
- SemanticLogger.stub(:appenders, [@appender]) do
98
- TestAttribute.logger.send(level, "hello #{level}", @hash)
99
- SemanticLogger.flush
100
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:#{@thread_name} loggable_test.rb:\d+\] TestAttribute -- hello #{level} -- #{@hash_str}/, @mock_logger.message)
101
- end
102
- end
81
+ it "logs #{level} information with class attribute" do
82
+ TestAttribute.logger.send(level, "hello #{level}", payload)
83
+
84
+ assert log = log_message
85
+ assert_equal "hello #{level}", log.message
86
+ assert_equal level, log.level
87
+ assert_equal 'TestAttribute', log.name
88
+ assert_equal payload, log.payload
103
89
  end
104
90
 
105
91
  it "log #{level} information with instance attribute" do
106
- SemanticLogger.stub(:backtrace_level_index, 0) do
107
- SemanticLogger.stub(:appenders, [@appender]) do
108
- TestAttribute.new.logger.send(level, "hello #{level}", @hash)
109
- SemanticLogger.flush
110
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:#{@thread_name} loggable_test.rb:\d+\] TestAttribute -- hello #{level} -- #{@hash_str}/, @mock_logger.message)
111
- end
112
- end
92
+ TestAttribute.new.logger.send(level, "hello #{level}", payload)
93
+ assert log = log_message
94
+ assert_equal "hello #{level}", log.message
95
+ assert_equal level, log.level
96
+ assert_equal 'TestAttribute', log.name
97
+ assert_equal payload, log.payload
113
98
  end
114
99
  end
115
100
  end
data/test/logger_test.rb CHANGED
@@ -3,174 +3,210 @@ require_relative 'test_helper'
3
3
  # Unit Test for SemanticLogger::Logger
4
4
  class LoggerTest < Minitest::Test
5
5
  describe SemanticLogger::Logger do
6
- before do
7
- # Use a mock logger that just keeps the last logged entry in an instance
8
- # variable
9
- SemanticLogger.default_level = :trace
10
- SemanticLogger.backtrace_level = nil
11
- @mock_logger = MockLogger.new
12
- @appender = SemanticLogger.add_appender(logger: @mock_logger)
13
-
14
- # Use this test's class name as the application name in the log output
15
- @logger = SemanticLogger[LoggerTest]
16
- @hash = {session_id: 'HSSKLEU@JDK767', tracking_number: 12345}
17
- @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
18
- @thread_name = Thread.current.name
19
- @file_name_reg_exp = ' logger_test.rb:\d+'
20
-
21
- assert_equal [], SemanticLogger.tags
22
- assert_equal 65535, SemanticLogger.backtrace_level_index
23
- end
6
+ include InMemoryAppenderHelper
24
7
 
25
- after do
26
- SemanticLogger.remove_appender(@appender)
8
+ let :dimensions do
9
+ {action: 'hit', user: 'jbloggs', state: 'FL'}
27
10
  end
28
11
 
29
12
  # Ensure that any log level can be logged
30
13
  SemanticLogger::LEVELS.each do |level|
31
- level_char = level.to_s.upcase[0]
32
14
 
33
15
  describe "##{level}" do
34
16
  describe 'positional parameter' do
35
17
  it 'logs message' do
36
- @logger.send(level, 'hello world')
37
- SemanticLogger.flush
38
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- hello world/, @mock_logger.message)
18
+ logger.send(level, 'hello world')
19
+
20
+ assert log = log_message
21
+ assert_equal 'hello world', log.message
39
22
  end
40
23
 
41
24
  it 'adds message from block' do
42
- @logger.send(level, 'hello world') { 'Calculations' }
43
- SemanticLogger.flush
44
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- hello world -- Calculations/, @mock_logger.message)
25
+ logger.send(level, 'hello world') { 'Calculations' }
26
+
27
+ assert log = log_message
28
+ assert_equal 'hello world -- Calculations', log.message
45
29
  end
46
30
 
47
31
  it 'logs message and payload' do
48
- hash = {tracking_number: '123456', even: 2, more: 'data'}
49
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
50
- @logger.send(level, 'Hello world', hash)
51
- SemanticLogger.flush
52
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message)
53
- end
32
+ logger.send(level, 'hello world', payload)
54
33
 
55
- it 'does not log an empty payload' do
56
- hash = {}
57
- @logger.send(level, 'Hello world', hash)
58
- SemanticLogger.flush
59
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- Hello world/, @mock_logger.message)
34
+ assert log = log_message
35
+ assert_equal 'hello world', log.message
36
+ assert_equal payload, log.payload
60
37
  end
61
38
 
62
39
  it 'logs with backtrace' do
63
40
  SemanticLogger.stub(:backtrace_level_index, 0) do
64
- @logger.send(level, 'hello world', @hash) { 'Calculations' }
65
- SemanticLogger.flush
66
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}#{@file_name_reg_exp}\] LoggerTest -- hello world -- Calculations -- #{@hash_str}/, @mock_logger.message)
41
+ logger.send(level, 'hello world', payload) { 'Calculations' }
42
+
43
+ assert log = log_message
44
+ assert_equal 'hello world -- Calculations', log.message
45
+ assert_equal payload, log.payload
46
+ assert log.backtrace
47
+ assert log.backtrace.size > 0, log.backtrace
67
48
  end
68
49
  end
69
50
 
70
51
  it 'logs with backtrace and exception' do
71
52
  SemanticLogger.stub(:backtrace_level_index, 0) do
72
53
  exc = RuntimeError.new('Test')
73
- @logger.send(level, 'hello world', exc)
74
- SemanticLogger.flush
75
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}#{@file_name_reg_exp}\] LoggerTest -- hello world -- Exception: RuntimeError: Test/, @mock_logger.message)
54
+ logger.send(level, 'hello world', exc)
55
+
56
+ assert log = log_message
57
+ assert_equal 'hello world', log.message
58
+ assert log.backtrace
59
+ assert log.backtrace.size > 0, log.backtrace
60
+
61
+ assert log.exception
62
+ refute log.exception.backtrace
63
+ assert_equal 'RuntimeError', log.exception.class.name
76
64
  end
77
65
  end
78
66
  end
79
67
 
80
- describe 'named parameters' do
68
+ describe 'keyword arguments' do
81
69
  it 'logs message' do
82
- @logger.send(level, message: 'Hello world')
83
- SemanticLogger.flush
84
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- Hello world/, @mock_logger.message)
70
+ logger.send(level, message: 'hello world')
71
+
72
+ assert log = log_message
73
+ assert_equal 'hello world', log.message
85
74
  end
86
75
 
87
76
  it 'logs payload and message' do
88
- @logger.send(level, message: 'Hello world', payload: {tracking_number: '123456', even: 2, more: 'data'})
89
- hash = {tracking_number: '123456', even: 2, more: 'data'}
90
- SemanticLogger.flush
91
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
92
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message)
77
+ logger.send(level, message: 'hello world', payload: payload)
78
+
79
+ assert log = log_message
80
+ assert_equal 'hello world', log.message
81
+ assert_equal payload, log.payload
93
82
  end
94
83
 
95
84
  it 'logs payload and message from block' do
96
- @logger.send(level) { {message: 'Hello world', payload: {tracking_number: '123456', even: 2, more: 'data'}} }
97
- hash = {tracking_number: '123456', even: 2, more: 'data'}
98
- SemanticLogger.flush
99
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
100
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message)
85
+ logger.send(level) { {message: 'hello world', payload: payload} }
86
+
87
+ assert log = log_message
88
+ assert_equal 'hello world', log.message
89
+ assert_equal payload, log.payload
101
90
  end
102
91
 
103
92
  it 'logs payload only' do
104
- hash = {tracking_number: '123456', even: 2, more: 'data'}
105
- @logger.send(level, payload: hash)
106
- SemanticLogger.flush
107
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
108
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] LoggerTest -- #{hash_str}/, @mock_logger.message)
93
+ logger.send(level, payload: payload)
94
+
95
+ assert log = log_message
96
+ refute log.message
97
+ assert_equal payload, log.payload
109
98
  end
110
99
 
111
100
  it 'logs duration' do
112
- @logger.send(level, duration: 123.44, message: 'Hello world', payload: {tracking_number: '123456', even: 2, more: 'data'})
113
- hash = {tracking_number: '123456', even: 2, more: 'data'}
114
- SemanticLogger.flush
115
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
116
- duration_match = SemanticLogger::Formatters::Base::PRECISION == 3 ? '\(123ms\)' : '\(123\.4ms\)'
117
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] #{duration_match} LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message)
101
+ logger.send(level, duration: 123.44, message: 'hello world', payload: payload)
102
+
103
+ assert log = log_message
104
+ assert_equal 'hello world', log.message
105
+ assert_equal payload, log.payload
106
+ assert_equal 123.44, log.duration
118
107
  end
119
108
 
120
109
  it 'does not log when below min_duration' do
121
- @logger.send(level, min_duration: 200, duration: 123.45, message: 'Hello world', payload: {tracking_number: '123456', even: 2, more: 'data'})
122
- SemanticLogger.flush
123
- assert_nil @mock_logger.message
110
+ logger.send(level, min_duration: 200, duration: 123.45, message: 'hello world', payload: {tracking_number: '123456', even: 2, more: 'data'})
111
+
112
+ refute log_message
124
113
  end
125
114
 
126
115
  it 'logs metric' do
127
- # Add mock metric subscriber
128
- $last_metric = nil
129
- SemanticLogger.on_metric do |log|
130
- $last_metric = log.dup
116
+ metric_name = '/my/custom/metric'
117
+ logger.send(level, metric: metric_name, duration: 123.44, message: 'hello world', payload: payload)
118
+
119
+ assert log = log_message
120
+ assert_equal 'hello world', log.message
121
+ assert_equal payload, log.payload
122
+ assert_equal 123.44, log.duration
123
+ assert_equal metric_name, log.metric
124
+ end
125
+
126
+ describe 'metrics appender' do
127
+ let :appender do
128
+ InMemoryMetricsAppender.new
131
129
  end
132
130
 
133
- metric_name = '/my/custom/metric'
134
- @logger.send(level, metric: metric_name, duration: 123.44, message: 'Hello world', payload: {tracking_number: '123456', even: 2, more: 'data'})
135
- hash = {tracking_number: '123456', even: 2, more: 'data'}
136
- SemanticLogger.flush
137
- hash_str = hash.inspect.sub('{', '\{').sub('}', '\}')
138
- duration_match = SemanticLogger::Formatters::Base::PRECISION == 3 ? '\(123ms\)' : '\(123\.4ms\)'
139
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ #{level_char} \[\d+:#{@thread_name}\] #{duration_match} LoggerTest -- Hello world -- #{hash_str}/, @mock_logger.message)
140
- assert metric_name, $last_metric.metric
131
+ it 'logs metric only events' do
132
+ metric_name = '/my/custom/metric'
133
+ logger.send(level, metric: metric_name, dimensions: dimensions)
134
+
135
+ assert log = log_message
136
+ assert_equal metric_name, log.metric
137
+ assert_equal dimensions, log.dimensions
138
+ refute log.message
139
+ end
141
140
  end
142
141
 
142
+ it 'for compatibility handles random payload logged as keyword arguments' do
143
+ logger.send(level, payload)
144
+
145
+ assert log = log_message
146
+ refute log.message
147
+ refute log.exception
148
+ refute log.metric
149
+ assert_equal payload, log.payload
150
+ end
143
151
  end
144
152
 
145
153
  describe '#filter' do
146
- it 'Proc' do
147
- @appender.filter = Proc.new { |l| (/\AExclude/ =~ l.message).nil? }
148
- @logger.send(level, 'Exclude this log message', @hash) { 'Calculations' }
149
- SemanticLogger.flush
150
- assert_nil @mock_logger.message
154
+ describe 'at the appender level' do
155
+ it 'Proc' do
156
+ appender.filter = -> log { (/\AExclude/ =~ log.message).nil? }
157
+ logger.send(level, 'Exclude this log message', @hash) { 'Calculations' }
158
+
159
+ refute log_message
160
+ end
161
+
162
+ it 'RegExp' do
163
+ filter = /\ALogger/
164
+ appender.filter = -> log { (/\AExclude/ =~ log.message).nil? }
165
+ logger.send(level, 'Exclude this log message', @hash) { 'Calculations' }
166
+
167
+ refute log_message
168
+ end
151
169
  end
152
170
 
153
- it 'RegExp' do
154
- filter = /\ALogger/
155
- @appender.filter = filter
156
- logger = SemanticLogger::Logger.new('NotLogger', :trace, filter)
157
- logger.send(level, 'Ignore all log messages from this class', @hash) { 'Calculations' }
158
- SemanticLogger.flush
159
- assert_nil @mock_logger.message
171
+ describe 'at the logger level' do
172
+ it 'Proc' do
173
+ logger.filter = -> log { (/\AExclude/ =~ log.message).nil? }
174
+ logger.send(level, 'Exclude this log message', @hash) { 'Calculations' }
175
+
176
+ refute log_message
177
+ end
178
+
179
+ it 'RegExp' do
180
+ filter = /\ALogger/
181
+ logger.filter = -> log { (/\AExclude/ =~ log.message).nil? }
182
+ logger.send(level, 'Exclude this log message', @hash) { 'Calculations' }
183
+
184
+ refute log_message
185
+ end
160
186
  end
187
+
161
188
  end
162
189
  end
163
190
  end
164
191
 
192
+ describe 'when level is too high' do
193
+ it 'does not log' do
194
+ SemanticLogger.default_level = :error
195
+ logger.info('Exclude this log message')
196
+
197
+ refute log_message
198
+ end
199
+ end
200
+
165
201
  describe 'Compatibility' do
166
202
  # Ensure that any log level can be logged
167
203
  Logger::Severity.constants.each do |level|
168
204
  it "log Ruby logger #{level} info" do
169
- @logger.level = Logger::Severity.const_get(level)
205
+ logger.level = Logger::Severity.const_get(level)
170
206
  if level.to_s == 'UNKNOWN'
171
- assert_equal Logger::Severity.const_get('ERROR')+1, @logger.send(:level_index)
207
+ assert_equal Logger::Severity.const_get('ERROR')+1, logger.send(:level_index)
172
208
  else
173
- assert_equal Logger::Severity.const_get(level)+1, @logger.send(:level_index)
209
+ assert_equal Logger::Severity.const_get(level)+1, logger.send(:level_index)
174
210
  end
175
211
  end
176
212
  end
@@ -179,68 +215,76 @@ class LoggerTest < Minitest::Test
179
215
  describe '#level?' do
180
216
  it 'return true for debug? with :trace level' do
181
217
  SemanticLogger.default_level = :trace
182
- assert_equal :trace, @logger.level
183
- assert_equal true, @logger.debug?
184
- assert_equal true, @logger.trace?
218
+ assert_equal :trace, logger.level
219
+ assert_equal true, logger.debug?
220
+ assert_equal true, logger.trace?
185
221
  end
186
222
 
187
223
  it 'return false for debug? with global :debug level' do
188
224
  SemanticLogger.default_level = :debug
189
- assert_equal :debug, @logger.level, @logger.inspect
190
- assert_equal true, @logger.debug?, @logger.inspect
191
- assert_equal false, @logger.trace?, @logger.inspect
225
+ assert_equal :debug, logger.level, logger.inspect
226
+ assert_equal true, logger.debug?, logger.inspect
227
+ assert_equal false, logger.trace?, logger.inspect
192
228
  end
193
229
 
194
230
  it 'return true for debug? with global :info level' do
195
231
  SemanticLogger.default_level = :info
196
- assert_equal :info, @logger.level, @logger.inspect
197
- assert_equal false, @logger.debug?, @logger.inspect
198
- assert_equal false, @logger.trace?, @logger.inspect
232
+ assert_equal :info, logger.level, logger.inspect
233
+ assert_equal false, logger.debug?, logger.inspect
234
+ assert_equal false, logger.trace?, logger.inspect
199
235
  end
200
236
 
201
237
  it 'return false for debug? with instance :debug level' do
202
- @logger.level = :debug
203
- assert_equal :debug, @logger.level, @logger.inspect
204
- assert_equal true, @logger.debug?, @logger.inspect
205
- assert_equal false, @logger.trace?, @logger.inspect
238
+ logger.level = :debug
239
+ assert_equal :debug, logger.level, logger.inspect
240
+ assert_equal true, logger.debug?, logger.inspect
241
+ assert_equal false, logger.trace?, logger.inspect
206
242
  end
207
243
 
208
244
  it 'return true for debug? with instance :info level' do
209
- @logger.level = :info
210
- assert_equal :info, @logger.level, @logger.inspect
211
- assert_equal false, @logger.debug?, @logger.inspect
212
- assert_equal false, @logger.trace?, @logger.inspect
245
+ logger.level = :info
246
+ assert_equal :info, logger.level, logger.inspect
247
+ assert_equal false, logger.debug?, logger.inspect
248
+ assert_equal false, logger.trace?, logger.inspect
213
249
  end
214
250
  end
215
251
 
216
252
  describe '.tagged' do
253
+ it 'sets global defaults' do
254
+ assert_equal [], SemanticLogger.tags
255
+ assert_equal 0, SemanticLogger.backtrace_level_index
256
+ end
257
+
217
258
  it 'add tags to log entries' do
218
- @logger.tagged('12345', 'DJHSFK') do
219
- @logger.info('Hello world')
220
- SemanticLogger.flush
221
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ I \[\d+:#{@thread_name}\] \[12345\] \[DJHSFK\] LoggerTest -- Hello world/, @mock_logger.message)
259
+ logger.tagged('12345', 'DJHSFK') do
260
+ logger.info('hello world')
261
+
262
+ assert log = log_message
263
+ assert_equal 'hello world', log.message
264
+ assert_equal %w(12345 DJHSFK), log.tags
222
265
  end
223
266
  end
224
267
 
225
268
  it 'add embedded tags to log entries' do
226
- @logger.tagged('First Level', 'tags') do
227
- @logger.tagged('Second Level') do
228
- @logger.info('Hello world')
229
- SemanticLogger.flush
230
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ I \[\d+:#{@thread_name}\] \[First Level\] \[tags\] \[Second Level\] LoggerTest -- Hello world/, @mock_logger.message)
269
+ logger.tagged('First Level', 'tags') do
270
+ logger.tagged('Second Level') do
271
+ assert_equal ['First Level', 'tags', 'Second Level'], logger.tags
272
+ logger.info('hello world')
273
+
274
+ assert log = log_message
275
+ assert_equal 'hello world', log.message
276
+ assert_equal ['First Level', 'tags', 'Second Level'], log.tags
231
277
  end
232
- assert_equal 2, @logger.tags.count, @logger.tags
233
- assert_equal 'First Level', @logger.tags.first
234
- assert_equal 'tags', @logger.tags.last
278
+ assert_equal ['First Level', 'tags'], logger.tags
235
279
  end
236
280
  end
237
281
 
238
282
  it 'also supports named tagging' do
239
- @logger.tagged(level1: 1) do
283
+ logger.tagged(level1: 1) do
240
284
  assert_equal({level1: 1}, SemanticLogger.named_tags)
241
- @logger.tagged(level2: 2, more: 'data') do
285
+ logger.tagged(level2: 2, more: 'data') do
242
286
  assert_equal({level1: 1, level2: 2, more: 'data'}, SemanticLogger.named_tags)
243
- @logger.tagged(level3: 3) do
287
+ logger.tagged(level3: 3) do
244
288
  assert_equal({level1: 1, level2: 2, more: 'data', level3: 3}, SemanticLogger.named_tags)
245
289
  end
246
290
  end
@@ -248,10 +292,12 @@ class LoggerTest < Minitest::Test
248
292
  end
249
293
 
250
294
  it 'is compatible with rails logging that uses arrays and nils' do
251
- @logger.tagged('', ['12345', 'DJHSFK'], nil) do
252
- @logger.info('Hello world')
253
- SemanticLogger.flush
254
- assert_match(/\d+-\d+-\d+ \d+:\d+:\d+.\d+ I \[\d+:#{@thread_name}\] \[12345\] \[DJHSFK\] LoggerTest -- Hello world/, @mock_logger.message)
295
+ logger.tagged('', ['12345', 'DJHSFK'], nil) do
296
+ logger.info('hello world')
297
+
298
+ assert log = log_message
299
+ assert_equal 'hello world', log.message
300
+ assert_equal %w(12345 DJHSFK), log.tags
255
301
  end
256
302
  end
257
303
  end