semantic_logger 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +55 -8
  3. data/lib/semantic_logger.rb +1 -2
  4. data/lib/semantic_logger/ansi_colors.rb +1 -2
  5. data/lib/semantic_logger/appender.rb +17 -15
  6. data/lib/semantic_logger/appender/bugsnag.rb +5 -4
  7. data/lib/semantic_logger/appender/elasticsearch.rb +102 -16
  8. data/lib/semantic_logger/appender/elasticsearch_http.rb +76 -0
  9. data/lib/semantic_logger/appender/file.rb +9 -25
  10. data/lib/semantic_logger/appender/graylog.rb +43 -38
  11. data/lib/semantic_logger/appender/honeybadger.rb +3 -5
  12. data/lib/semantic_logger/appender/http.rb +12 -15
  13. data/lib/semantic_logger/appender/kafka.rb +183 -0
  14. data/lib/semantic_logger/appender/mongodb.rb +3 -3
  15. data/lib/semantic_logger/appender/new_relic.rb +3 -7
  16. data/lib/semantic_logger/appender/sentry.rb +2 -5
  17. data/lib/semantic_logger/appender/splunk.rb +7 -10
  18. data/lib/semantic_logger/appender/splunk_http.rb +16 -16
  19. data/lib/semantic_logger/appender/syslog.rb +43 -122
  20. data/lib/semantic_logger/appender/tcp.rb +28 -9
  21. data/lib/semantic_logger/appender/udp.rb +4 -7
  22. data/lib/semantic_logger/appender/wrapper.rb +3 -7
  23. data/lib/semantic_logger/base.rb +47 -7
  24. data/lib/semantic_logger/formatters/base.rb +29 -10
  25. data/lib/semantic_logger/formatters/color.rb +75 -45
  26. data/lib/semantic_logger/formatters/default.rb +53 -28
  27. data/lib/semantic_logger/formatters/json.rb +7 -8
  28. data/lib/semantic_logger/formatters/raw.rb +97 -1
  29. data/lib/semantic_logger/formatters/syslog.rb +46 -80
  30. data/lib/semantic_logger/formatters/syslog_cee.rb +57 -0
  31. data/lib/semantic_logger/log.rb +17 -67
  32. data/lib/semantic_logger/logger.rb +17 -27
  33. data/lib/semantic_logger/processor.rb +70 -46
  34. data/lib/semantic_logger/semantic_logger.rb +130 -69
  35. data/lib/semantic_logger/subscriber.rb +18 -32
  36. data/lib/semantic_logger/version.rb +1 -1
  37. data/test/appender/elasticsearch_http_test.rb +75 -0
  38. data/test/appender/elasticsearch_test.rb +34 -27
  39. data/test/appender/file_test.rb +2 -2
  40. data/test/appender/honeybadger_test.rb +1 -1
  41. data/test/appender/kafka_test.rb +36 -0
  42. data/test/appender/new_relic_test.rb +1 -1
  43. data/test/appender/sentry_test.rb +1 -1
  44. data/test/appender/syslog_test.rb +2 -2
  45. data/test/appender/wrapper_test.rb +1 -1
  46. data/test/formatters/color_test.rb +154 -0
  47. data/test/formatters/default_test.rb +176 -0
  48. data/test/loggable_test.rb +1 -1
  49. data/test/logger_test.rb +47 -4
  50. data/test/measure_test.rb +2 -2
  51. data/test/semantic_logger_test.rb +34 -6
  52. data/test/test_helper.rb +8 -0
  53. metadata +14 -3
@@ -7,7 +7,7 @@ module SemanticLogger
7
7
  attr_accessor :formatter
8
8
  attr_writer :application, :host
9
9
 
10
- # Returns the current log level if set, otherwise it logs everything it receives
10
+ # Returns the current log level if set, otherwise it logs everything it receives.
11
11
  def level
12
12
  @level || :trace
13
13
  end
@@ -22,17 +22,17 @@ module SemanticLogger
22
22
  # NOOP
23
23
  end
24
24
 
25
- # Returns [SemanticLogger::Formatters::Default] formatter default for this subscriber
25
+ # Returns [SemanticLogger::Formatters::Default] default formatter for this subscriber.
26
26
  def default_formatter
27
27
  SemanticLogger::Formatters::Default.new
28
28
  end
29
29
 
30
- # Allow application name to be set globally or per subscriber
30
+ # Allow application name to be set globally or on a per subscriber basis.
31
31
  def application
32
32
  @application || SemanticLogger.application
33
33
  end
34
34
 
35
- # Allow host name to be set globally or per subscriber
35
+ # Allow host name to be set globally or on a per subscriber basis.
36
36
  def host
37
37
  @host || SemanticLogger.host
38
38
  end
@@ -57,37 +57,32 @@ module SemanticLogger
57
57
  # Proc: Only include log messages where the supplied Proc returns true
58
58
  # The Proc must return true or false.
59
59
  #
60
- # host: [String]
61
- # Name of this host to appear in log messages.
62
- # Default: SemanticLogger.host
63
- #
64
60
  # application: [String]
65
61
  # Name of this application to appear in log messages.
66
62
  # Default: SemanticLogger.application
67
- def initialize(options={}, &block)
68
- # Backward compatibility
69
- options = {level: options} unless options.is_a?(Hash)
70
- options = options.dup
71
- level = options.delete(:level)
72
- filter = options.delete(:filter)
73
- @formatter = extract_formatter(options.delete(:formatter), &block)
74
- @application = options.delete(:application)
75
- @host = options.delete(:host)
76
- raise(ArgumentError, "Unknown options: #{options.inspect}") if options.size > 0
63
+ #
64
+ # host: [String]
65
+ # Name of this host to appear in log messages.
66
+ # Default: SemanticLogger.host
67
+ def initialize(level: nil, formatter: nil, filter: nil, application: nil, host: nil, &block)
68
+ @formatter = extract_formatter(formatter, &block)
69
+ @application = application
70
+ @host = host
77
71
 
78
- # Subscribers don't take a class name, so use this class name if an subscriber
79
- # is logged to directly
72
+ # Subscribers don't take a class name, so use this class name if a subscriber
73
+ # is logged to directly.
80
74
  super(self.class, level, filter)
81
75
  end
82
76
 
83
- # Return the level index for fast comparisons
77
+ # Return the level index for fast comparisons.
84
78
  # Returns the lowest level index if the level has not been explicitly
85
- # set for this instance
79
+ # set for this instance.
86
80
  def level_index
87
81
  @level_index || 0
88
82
  end
89
83
 
90
- # Return formatter that responds to call
84
+ # Return formatter that responds to call.
85
+ #
91
86
  # Supports formatter supplied as:
92
87
  # - Symbol
93
88
  # - Hash ( Symbol => { options })
@@ -114,14 +109,5 @@ module SemanticLogger
114
109
  end
115
110
  end
116
111
 
117
- SUBSCRIBER_OPTIONS = [:level, :formatter, :filter, :application, :host].freeze
118
-
119
- # Returns [Hash] the subscriber common options from the supplied Hash
120
- def extract_subscriber_options!(options)
121
- subscriber_options = {}
122
- SUBSCRIBER_OPTIONS.each { |key| subscriber_options[key] = options.delete(key) if options.has_key?(key) }
123
- subscriber_options
124
- end
125
-
126
112
  end
127
113
  end
@@ -1,3 +1,3 @@
1
1
  module SemanticLogger #:nodoc
2
- VERSION = '4.0.0'
2
+ VERSION = '4.1.0'
3
3
  end
@@ -0,0 +1,75 @@
1
+ require_relative '../test_helper'
2
+
3
+ # Unit Test for SemanticLogger::Appender::Elasticsearch
4
+ module Appender
5
+ class ElasticsearchHttpTest < Minitest::Test
6
+ response_mock = Struct.new(:code, :body)
7
+
8
+ describe SemanticLogger::Appender::ElasticsearchHttp do
9
+ before do
10
+ Net::HTTP.stub_any_instance(:start, true) do
11
+ @appender = SemanticLogger::Appender::ElasticsearchHttp.new(
12
+ url: 'http://localhost:9200'
13
+ )
14
+ end
15
+ @message = 'AppenderElasticsearchTest log message'
16
+ end
17
+
18
+ it 'logs to daily indexes' do
19
+ index = nil
20
+ @appender.stub(:post, -> json, ind { index = ind }) do
21
+ @appender.info @message
22
+ end
23
+ assert_equal "/semantic_logger-#{Time.now.utc.strftime('%Y.%m.%d')}/log", index
24
+ end
25
+
26
+ SemanticLogger::LEVELS.each do |level|
27
+ it "send #{level}" do
28
+ request = nil
29
+ @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
30
+ @appender.send(level, @message)
31
+ end
32
+ message = JSON.parse(request.body)
33
+ assert_equal @message, message['message']
34
+ assert_equal level.to_s, message['level']
35
+ refute message['exception']
36
+ end
37
+
38
+ it "sends #{level} exceptions" do
39
+ exc = nil
40
+ begin
41
+ Uh oh
42
+ rescue Exception => e
43
+ exc = e
44
+ end
45
+ request = nil
46
+ @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
47
+ @appender.send(level, 'Reading File', exc)
48
+ end
49
+ hash = JSON.parse(request.body)
50
+ assert 'Reading File', hash['message']
51
+ assert exception = hash['exception']
52
+ assert 'NameError', exception['name']
53
+ assert 'undefined local variable or method', exception['message']
54
+ assert_equal level.to_s, hash['level']
55
+ assert exception['stack_trace'].first.include?(__FILE__), exception
56
+ end
57
+
58
+ it "sends #{level} custom attributes" do
59
+ request = nil
60
+ @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
61
+ @appender.send(level, @message, {key1: 1, key2: 'a'})
62
+ end
63
+ message = JSON.parse(request.body)
64
+ assert_equal @message, message['message']
65
+ assert_equal level.to_s, message['level']
66
+ refute message['stack_trace']
67
+ assert payload = message['payload'], message
68
+ assert_equal 1, payload['key1'], message
69
+ assert_equal 'a', payload['key2'], message
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -3,36 +3,40 @@ require_relative '../test_helper'
3
3
  # Unit Test for SemanticLogger::Appender::Elasticsearch
4
4
  module Appender
5
5
  class ElasticsearchTest < Minitest::Test
6
- response_mock = Struct.new(:code, :body)
7
-
8
6
  describe SemanticLogger::Appender::Elasticsearch do
9
7
  before do
10
- Net::HTTP.stub_any_instance(:start, true) do
8
+ skip('Concurrent::TimerTask issue is preventing the process from stopping.') if defined? JRuby
9
+ Elasticsearch::Transport::Client.stub_any_instance(:bulk, true) do
11
10
  @appender = SemanticLogger::Appender::Elasticsearch.new(
12
- url: 'http://localhost:9200'
11
+ url: 'http://localhost:9200',
12
+ batch_size: 1 # immediate flush
13
13
  )
14
14
  end
15
15
  @message = 'AppenderElasticsearchTest log message'
16
16
  end
17
17
 
18
+ after do
19
+ @appender.close if @appender
20
+ end
21
+
18
22
  it 'logs to daily indexes' do
19
23
  index = nil
20
- @appender.stub(:post, -> json, ind { index = ind }) do
24
+ @appender.stub(:enqueue, ->(ind, json){ index = ind['index']['_index'] } ) do
21
25
  @appender.info @message
22
26
  end
23
- assert_equal "/semantic_logger-#{Time.now.utc.strftime('%Y.%m.%d')}/log", index
27
+ assert_equal "semantic_logger-#{Time.now.strftime('%Y.%m.%d')}", index
24
28
  end
25
29
 
26
30
  SemanticLogger::LEVELS.each do |level|
27
31
  it "send #{level}" do
28
32
  request = nil
29
- @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
33
+ @appender.client.stub(:bulk, -> r { request = r; {"status" => 201 } }) do
30
34
  @appender.send(level, @message)
31
35
  end
32
- message = JSON.parse(request.body)
33
- assert_equal @message, message['message']
34
- assert_equal level.to_s, message['level']
35
- refute message['exception']
36
+
37
+ message = request[:body][1]
38
+ assert_equal @message, message[:message]
39
+ assert_equal level, message[:level]
36
40
  end
37
41
 
38
42
  it "sends #{level} exceptions" do
@@ -43,30 +47,33 @@ module Appender
43
47
  exc = e
44
48
  end
45
49
  request = nil
46
- @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
50
+ @appender.client.stub(:bulk, -> r { request = r; {"status" => 201 } }) do
47
51
  @appender.send(level, 'Reading File', exc)
48
52
  end
49
- hash = JSON.parse(request.body)
50
- assert 'Reading File', hash['message']
51
- assert exception = hash['exception']
52
- assert 'NameError', exception['name']
53
- assert 'undefined local variable or method', exception['message']
54
- assert_equal level.to_s, hash['level']
55
- assert exception['stack_trace'].first.include?(__FILE__), exception
53
+
54
+ hash = request[:body][1]
55
+
56
+ assert 'Reading File', hash[:message]
57
+ assert exception = hash[:exception]
58
+ assert 'NameError', exception[:name]
59
+ assert 'undefined local variable or method', exception[:message]
60
+ assert_equal level, hash[:level]
61
+ assert exception[:stack_trace].first.include?(__FILE__), exception
56
62
  end
57
63
 
58
64
  it "sends #{level} custom attributes" do
59
65
  request = nil
60
- @appender.http.stub(:request, -> r { request = r; response_mock.new('200', 'ok') }) do
66
+ @appender.client.stub(:bulk, -> r { request = r; {"status" => 201 } }) do
61
67
  @appender.send(level, @message, {key1: 1, key2: 'a'})
62
68
  end
63
- message = JSON.parse(request.body)
64
- assert_equal @message, message['message']
65
- assert_equal level.to_s, message['level']
66
- refute message['stack_trace']
67
- assert payload = message['payload'], message
68
- assert_equal 1, payload['key1'], message
69
- assert_equal 'a', payload['key2'], message
69
+
70
+ message = request[:body][1]
71
+ assert_equal @message, message[:message]
72
+ assert_equal level, message[:level]
73
+ refute message[:stack_trace]
74
+ assert payload = message[:payload], message
75
+ assert_equal 1, payload[:key1], message
76
+ assert_equal 'a', payload[:key2], message
70
77
  end
71
78
  end
72
79
 
@@ -10,7 +10,7 @@ module Appender
10
10
  SemanticLogger.default_level = :trace
11
11
  @time = Time.new
12
12
  @io = StringIO.new
13
- @appender = SemanticLogger::Appender::File.new(@io)
13
+ @appender = SemanticLogger::Appender::File.new(io: @io)
14
14
  @hash = {session_id: 'HSSKLEU@JDK767', tracking_number: 12345}
15
15
  @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
16
16
  @thread_name = Thread.current.name
@@ -86,7 +86,7 @@ module Appender
86
86
 
87
87
  describe 'custom formatter' do
88
88
  before do
89
- @appender = SemanticLogger::Appender::File.new(@io) do |log|
89
+ @appender = SemanticLogger::Appender::File.new(io: @io) do |log|
90
90
  tags = log.tags.collect { |tag| "[#{tag}]" }.join(' ') + ' ' if log.tags && (log.tags.size > 0)
91
91
 
92
92
  message = log.message.to_s
@@ -5,7 +5,7 @@ module Appender
5
5
  class HoneybadgerTest < Minitest::Test
6
6
  describe SemanticLogger::Appender::Honeybadger do
7
7
  before do
8
- @appender = SemanticLogger::Appender::Honeybadger.new(:trace)
8
+ @appender = SemanticLogger::Appender::Honeybadger.new(level: :trace)
9
9
  @message = 'AppenderHoneybadgerTest log message'
10
10
  SemanticLogger.backtrace_level = :error
11
11
  end
@@ -0,0 +1,36 @@
1
+ require_relative '../test_helper'
2
+
3
+ module Appender
4
+ class KafkaTest < Minitest::Test
5
+ describe SemanticLogger::Appender::Kafka do
6
+ before do
7
+ @appender = SemanticLogger::Appender::Kafka.new(
8
+ seed_brokers: ['http://localhost:9092']
9
+ )
10
+ @message = 'AppenderKafkaTest log message'
11
+ end
12
+
13
+ after do
14
+ @appender.close if @appender
15
+ end
16
+
17
+ it 'sends log messages in JSON format' do
18
+ message = nil
19
+ options = nil
20
+ ::Kafka::Producer.stub_any_instance(:produce, -> value, **opts { message = value; options = opts }) do
21
+ @appender.info(@message)
22
+ @appender.flush
23
+ end
24
+
25
+ h = JSON.parse(message)
26
+ assert_equal 'info', h['level']
27
+ assert_equal @message, h['message']
28
+ assert_equal "SemanticLogger::Appender::Kafka", h['name']
29
+ assert_equal $$, h['pid']
30
+
31
+ assert_equal 'log_messages', options[:topic]
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -8,7 +8,7 @@ module Appender
8
8
  describe SemanticLogger::Appender::NewRelic do
9
9
 
10
10
  before do
11
- @appender = SemanticLogger::Appender::NewRelic.new(:error)
11
+ @appender = SemanticLogger::Appender::NewRelic.new
12
12
  @message = 'AppenderNewRelicTest log message'
13
13
  end
14
14
 
@@ -5,7 +5,7 @@ module Appender
5
5
  class SentryTest < Minitest::Test
6
6
  describe SemanticLogger::Appender::Sentry do
7
7
  before do
8
- @appender = SemanticLogger::Appender::Sentry.new(:trace)
8
+ @appender = SemanticLogger::Appender::Sentry.new(level: :trace)
9
9
  @message = 'AppenderRavenTest log message'
10
10
  SemanticLogger.backtrace_level = :error
11
11
  end
@@ -22,7 +22,7 @@ module Appender
22
22
  message = nil
23
23
  Net::TCPClient.stub_any_instance(:closed?, false) do
24
24
  Net::TCPClient.stub_any_instance(:connect, nil) do
25
- syslog_appender = SemanticLogger::Appender::Syslog.new(server: 'tcp://localhost:88888', level: :debug)
25
+ syslog_appender = SemanticLogger::Appender::Syslog.new(url: 'tcp://localhost:88888', level: :debug)
26
26
  syslog_appender.remote_syslog.stub(:write, Proc.new { |data| message = data }) do
27
27
  syslog_appender.debug 'AppenderSyslogTest log message'
28
28
  end
@@ -33,7 +33,7 @@ module Appender
33
33
 
34
34
  it 'handle remote syslog over UDP' do
35
35
  message = nil
36
- syslog_appender = SemanticLogger::Appender::Syslog.new(server: 'udp://localhost:88888', level: :debug)
36
+ syslog_appender = SemanticLogger::Appender::Syslog.new(url: 'udp://localhost:88888', level: :debug)
37
37
  UDPSocket.stub_any_instance(:send, -> msg, num, host, port { message = msg }) do
38
38
  syslog_appender.debug 'AppenderSyslogTest log message'
39
39
  end
@@ -11,7 +11,7 @@ module Appender
11
11
 
12
12
  @time = Time.new
13
13
  @mock_logger = MockLogger.new
14
- @appender = SemanticLogger::Appender::Wrapper.new(@mock_logger)
14
+ @appender = SemanticLogger::Appender::Wrapper.new(logger: @mock_logger)
15
15
  @hash = {session_id: 'HSSKLEU@JDK767', tracking_number: 12345}
16
16
  @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
17
17
  @thread_name = Thread.current.name
@@ -0,0 +1,154 @@
1
+ require_relative '../test_helper'
2
+
3
+ module SemanticLogger
4
+ module Formatters
5
+ class ColorTest < Minitest::Test
6
+ describe Color do
7
+ let(:log_time) do
8
+ Time.utc(2017, 1, 14, 8, 32, 5.375276)
9
+ end
10
+
11
+ let(:level) do
12
+ :debug
13
+ end
14
+
15
+ let(:log) do
16
+ # :level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric, :backtrace, :metric_amount, :named_tags
17
+ log = SemanticLogger::Log.new('ColorTest', level)
18
+ log.time = log_time
19
+ log
20
+ end
21
+
22
+ let(:expected_time) do
23
+ SemanticLogger::Formatters::Base::PRECISION == 3 ? '2017-01-14 08:32:05.375' : '2017-01-14 08:32:05.375276'
24
+ end
25
+
26
+ let(:set_exception) do
27
+ begin
28
+ raise 'Oh no'
29
+ rescue Exception => exc
30
+ log.exception = exc
31
+ end
32
+ end
33
+
34
+ let(:backtrace) do
35
+ [
36
+ "test/formatters/default_test.rb:35:in `block (2 levels) in <class:DefaultTest>'",
37
+ "gems/ruby-2.3.3/gems/minitest-5.10.1/lib/minitest/spec.rb:247:in `instance_eval'",
38
+ "gems/ruby-2.3.3/gems/minitest-5.10.1/lib/minitest/spec.rb:247:in `block (2 levels) in let'",
39
+ "gems/ruby-2.3.3/gems/minitest-5.10.1/lib/minitest/spec.rb:247:in `fetch'",
40
+ "ruby-2.3.3/gems/minitest-5.10.1/lib/minitest/spec.rb:247:in `block in let'",
41
+ "test/formatters/default_test.rb:65:in `block (3 levels) in <class:DefaultTest>'",
42
+ "ruby-2.3.3/gems/minitest-5.10.1/lib/minitest/test.rb:105:in `block (3 levels) in run'"
43
+ ]
44
+ end
45
+
46
+ let(:bold) do
47
+ SemanticLogger::AnsiColors::BOLD
48
+ end
49
+
50
+ let(:clear) do
51
+ SemanticLogger::AnsiColors::CLEAR
52
+ end
53
+
54
+ let(:color) do
55
+ SemanticLogger::AnsiColors::GREEN
56
+ end
57
+
58
+ let(:formatter) do
59
+ formatter = SemanticLogger::Formatters::Color.new
60
+ # Does not use the logger instance for formatting purposes
61
+ formatter.call(log, nil)
62
+ formatter
63
+ end
64
+
65
+ describe 'level' do
66
+ it 'logs single character' do
67
+ assert_equal "#{color}D#{clear}", formatter.level
68
+ end
69
+ end
70
+
71
+ describe 'tags' do
72
+ it 'logs tags' do
73
+ log.tags = %w(first second third)
74
+ assert_equal "[#{color}first#{clear}] [#{color}second#{clear}] [#{color}third#{clear}]", formatter.tags
75
+ end
76
+ end
77
+
78
+ describe 'named_tags' do
79
+ it 'logs named tags' do
80
+ log.named_tags = {first: 1, second: 2, third: 3}
81
+ assert_equal "{#{color}first: 1#{clear}, #{color}second: 2#{clear}, #{color}third: 3#{clear}}", formatter.named_tags
82
+ end
83
+ end
84
+
85
+ describe 'duration' do
86
+ it 'logs long duration' do
87
+ log.duration = 1_000_000.34567
88
+ assert_equal "(#{bold}16m 40s#{clear})", formatter.duration
89
+ end
90
+
91
+ it 'logs short duration' do
92
+ log.duration = 1.34567
93
+ duration = SemanticLogger::Formatters::Base::PRECISION == 3 ? "(#{bold}1ms#{clear})" : "(#{bold}1.346ms#{clear})"
94
+ assert_equal duration, formatter.duration
95
+ end
96
+ end
97
+
98
+ describe 'name' do
99
+ it 'logs name' do
100
+ assert_equal "#{color}ColorTest#{clear}", formatter.name
101
+ end
102
+ end
103
+
104
+ describe 'payload' do
105
+ it 'logs hash payload' do
106
+ log.payload = {first: 1, second: 2, third: 3}
107
+ assert_equal "-- #{log.payload.ai(multiline: false)}", formatter.payload
108
+ end
109
+
110
+ it 'skips nil payload' do
111
+ refute formatter.payload
112
+ end
113
+
114
+ it 'skips empty payload' do
115
+ log.payload = {}
116
+ refute formatter.payload
117
+ end
118
+ end
119
+
120
+ describe 'exception' do
121
+ it 'logs exception' do
122
+ set_exception
123
+ str = "-- Exception: #{color}RuntimeError: Oh no#{clear}\n"
124
+ assert_equal str, formatter.exception.lines.first
125
+ end
126
+
127
+ it 'skips nil exception' do
128
+ refute formatter.exception
129
+ end
130
+ end
131
+
132
+ describe 'call' do
133
+ it 'returns minimal elements' do
134
+ assert_equal "#{expected_time} #{color}D#{clear} [#{$$}:#{Thread.current.name}] #{color}ColorTest#{clear}", formatter.call(log, nil)
135
+ end
136
+
137
+ it 'retuns all elements' do
138
+ log.tags = %w(first second third)
139
+ log.named_tags = {first: 1, second: 2, third: 3}
140
+ log.duration = 1.34567
141
+ log.message = "Hello World"
142
+ log.payload = {first: 1, second: 2, third: 3}
143
+ log.backtrace = backtrace
144
+ set_exception
145
+ duration = SemanticLogger::Formatters::Base::PRECISION == 3 ? '1' : '1.346'
146
+ str = "#{expected_time} #{color}D#{clear} [#{$$}:#{Thread.current.name} default_test.rb:35] [#{color}first#{clear}] [#{color}second#{clear}] [#{color}third#{clear}] {#{color}first: 1#{clear}, #{color}second: 2#{clear}, #{color}third: 3#{clear}} (#{bold}#{duration}ms#{clear}) #{color}ColorTest#{clear} -- Hello World -- #{{:first => 1, :second => 2, :third => 3}.ai(multiline: false)} -- Exception: #{color}RuntimeError: Oh no#{clear}\n"
147
+ assert_equal str, formatter.call(log, nil).lines.first
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+ end
154
+ end