semantic_logger 4.1.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
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