semantic_logger 4.7.4 → 4.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -57,7 +57,7 @@ module SemanticLogger
57
57
  # Only used with the TCP protocol.
58
58
  # Specify custom parameters to pass into Net::TCPClient.new
59
59
  # For a list of options see the net_tcp_client documentation:
60
- # https://github.com/rocketjob/net_tcp_client/blob/master/lib/net/tcp_client/tcp_client.rb
60
+ # https://github.com/reidmorrison/net_tcp_client/blob/master/lib/net/tcp_client/tcp_client.rb
61
61
  #
62
62
  # level: [:trace | :debug | :info | :warn | :error | :fatal]
63
63
  # Override the log level for this appender.
@@ -151,7 +151,8 @@ module SemanticLogger
151
151
  begin
152
152
  require "syslog_protocol"
153
153
  rescue LoadError
154
- raise LoadError, "Missing gem: syslog_protocol. This gem is required when logging over TCP or UDP. To fix this error: gem install syslog_protocol"
154
+ raise LoadError,
155
+ "Missing gem: syslog_protocol. This gem is required when logging over TCP or UDP. To fix this error: gem install syslog_protocol"
155
156
  end
156
157
 
157
158
  # The net_tcp_client gem is required when logging over TCP.
@@ -159,7 +160,8 @@ module SemanticLogger
159
160
  begin
160
161
  require "net/tcp_client"
161
162
  rescue LoadError
162
- raise LoadError, "Missing gem: net_tcp_client. This gem is required when logging over TCP. To fix this error: gem install net_tcp_client"
163
+ raise LoadError,
164
+ "Missing gem: net_tcp_client. This gem is required when logging over TCP. To fix this error: gem install net_tcp_client"
163
165
  end
164
166
  end
165
167
  end
@@ -32,7 +32,7 @@ module SemanticLogger
32
32
  # require 'logger'
33
33
  # require 'semantic_logger'
34
34
  #
35
- # ruby_logger = Logger.new(STDOUT)
35
+ # ruby_logger = Logger.new($stdout)
36
36
  # SemanticLogger.add_appender(logger: ruby_logger)
37
37
  #
38
38
  # logger = SemanticLogger['test']
@@ -45,7 +45,8 @@ module SemanticLogger
45
45
  # Check if the custom appender responds to all the log levels. For example Ruby ::Logger
46
46
  does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) }
47
47
  if does_not_implement
48
- raise(ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
48
+ raise(ArgumentError,
49
+ "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
49
50
  end
50
51
 
51
52
  super(**args, &block)
@@ -9,6 +9,7 @@ module SemanticLogger
9
9
  autoload :File, "semantic_logger/appender/file"
10
10
  autoload :Graylog, "semantic_logger/appender/graylog"
11
11
  autoload :Honeybadger, "semantic_logger/appender/honeybadger"
12
+ autoload :IO, "semantic_logger/appender/io"
12
13
  autoload :Kafka, "semantic_logger/appender/kafka"
13
14
  autoload :Sentry, "semantic_logger/appender/sentry"
14
15
  autoload :Http, "semantic_logger/appender/http"
@@ -32,7 +33,7 @@ module SemanticLogger
32
33
  appender = build(**args, &block)
33
34
 
34
35
  # If appender implements #batch, then it should use the batch proxy by default.
35
- batch = true if batch.nil? && appender.respond_to?(:batch)
36
+ batch = true if batch.nil? && appender.respond_to?(:batch)
36
37
 
37
38
  if batch == true
38
39
  Appender::AsyncBatch.new(
@@ -56,8 +57,10 @@ module SemanticLogger
56
57
 
57
58
  # Returns [Subscriber] instance from the supplied options.
58
59
  def self.build(io: nil, file_name: nil, appender: nil, metric: nil, logger: nil, **args, &block)
59
- if io || file_name
60
- SemanticLogger::Appender::File.new(io: io, file_name: file_name, **args, &block)
60
+ if file_name
61
+ SemanticLogger::Appender::File.new(file_name, **args, &block)
62
+ elsif io
63
+ SemanticLogger::Appender::IO.new(io, **args, &block)
61
64
  elsif logger
62
65
  SemanticLogger::Appender::Wrapper.new(logger: logger, **args, &block)
63
66
  elsif appender
@@ -66,7 +69,8 @@ module SemanticLogger
66
69
  elsif appender.is_a?(Subscriber)
67
70
  appender
68
71
  else
69
- raise(ArgumentError, "Parameter :appender must be either a Symbol or an object derived from SemanticLogger::Subscriber, not: #{appender.inspect}")
72
+ raise(ArgumentError,
73
+ "Parameter :appender must be either a Symbol or an object derived from SemanticLogger::Subscriber, not: #{appender.inspect}")
70
74
  end
71
75
  elsif metric
72
76
  if metric.is_a?(Symbol)
@@ -74,10 +78,12 @@ module SemanticLogger
74
78
  elsif metric.is_a?(Subscriber)
75
79
  metric
76
80
  else
77
- raise(ArgumentError, "Parameter :metric must be either a Symbol or an object derived from SemanticLogger::Subscriber, not: #{appender.inspect}")
81
+ raise(ArgumentError,
82
+ "Parameter :metric must be either a Symbol or an object derived from SemanticLogger::Subscriber, not: #{appender.inspect}")
78
83
  end
79
84
  else
80
- raise(ArgumentError, "To create an appender it must supply one of the following: :io, :file_name, :appender, :metric, or :logger")
85
+ raise(ArgumentError,
86
+ "To create an appender it must supply one of the following: :io, :file_name, :appender, :metric, or :logger")
81
87
  end
82
88
  end
83
89
 
@@ -10,42 +10,48 @@ module SemanticLogger
10
10
 
11
11
  def add(**args, &block)
12
12
  appender = SemanticLogger::Appender.factory(**args, &block)
13
+
14
+ if appender.respond_to?(:console_output?) && appender.console_output? && console_output?
15
+ logger.warn "Ignoring attempt to add a second console appender: #{appender.class.name} since it would result in duplicate console output."
16
+ return
17
+ end
18
+
13
19
  self << appender
14
20
  appender
15
21
  end
16
22
 
23
+ # Whether any of the existing appenders already output to the console?
24
+ # I.e. Writes to stdout or stderr.
25
+ def console_output?
26
+ any? { |appender| appender.respond_to?(:console_output?) && appender.console_output? }
27
+ end
28
+
17
29
  def log(log)
18
30
  each do |appender|
19
- begin
20
- appender.log(log) if appender.should_log?(log)
21
- rescue Exception => e
22
- logger.error "Failed to log to appender: #{appender.name}", e
23
- end
31
+ appender.log(log) if appender.should_log?(log)
32
+ rescue Exception => e
33
+ logger.error "Failed to log to appender: #{appender.name}", e
24
34
  end
25
35
  end
26
36
 
27
37
  def flush
28
38
  each do |appender|
29
- begin
30
- logger.trace "Flushing appender: #{appender.name}"
31
- appender.flush
32
- rescue Exception => e
33
- logger.error "Failed to flush appender: #{appender.name}", e
34
- end
39
+ logger.trace "Flushing appender: #{appender.name}"
40
+ appender.flush
41
+ rescue Exception => e
42
+ logger.error "Failed to flush appender: #{appender.name}", e
35
43
  end
36
44
  logger.trace "All appenders flushed"
37
45
  end
38
46
 
39
47
  def close
40
- each do |appender|
41
- begin
42
- logger.trace "Closing appender: #{appender.name}"
43
- appender.flush
44
- appender.close
45
- delete(appender)
46
- rescue Exception => e
47
- logger.error "Failed to close appender: #{appender.name}", e
48
- end
48
+ to_a.each do |appender|
49
+ logger.trace "Closing appender: #{appender.name}"
50
+ delete(appender)
51
+ appender.flush
52
+ appender.close
53
+ rescue Exception => e
54
+ logger.error "Failed to close appender: #{appender.name}", e
49
55
  end
50
56
  logger.trace "All appenders closed and removed from appender list"
51
57
  end
@@ -53,14 +59,12 @@ module SemanticLogger
53
59
  # After a fork the appender thread is not running, start it if it is not running.
54
60
  def reopen
55
61
  each do |appender|
56
- begin
57
- next unless appender.respond_to?(:reopen)
62
+ next unless appender.respond_to?(:reopen)
58
63
 
59
- logger.trace "Reopening appender: #{appender.name}"
60
- appender.reopen
61
- rescue Exception => e
62
- logger.error "Failed to re-open appender: #{appender.name}", e
63
- end
64
+ logger.trace "Reopening appender: #{appender.name}"
65
+ appender.reopen
66
+ rescue Exception => e
67
+ logger.error "Failed to re-open appender: #{appender.name}", e
64
68
  end
65
69
  logger.trace "All appenders re-opened"
66
70
  end
@@ -63,7 +63,7 @@ module SemanticLogger
63
63
  # SemanticLogger.default_level = :info
64
64
  #
65
65
  # # Log to screen
66
- # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
66
+ # SemanticLogger.add_appender(io: $stdout, formatter: :color)
67
67
  #
68
68
  # # And log to a file at the same time
69
69
  # SemanticLogger.add_appender(file_name: 'application.log', formatter: :color)
@@ -136,7 +136,7 @@ module SemanticLogger
136
136
 
137
137
  backtrace =
138
138
  if thread == Thread.current
139
- Utils.extract_backtrace
139
+ Utils.extract_backtrace(caller)
140
140
  else
141
141
  log.thread_name = thread.name
142
142
  log.tags = (thread[:semantic_logger_tags] || []).clone
@@ -188,7 +188,8 @@ module SemanticLogger
188
188
  # - For better performance with clean tags, see `SemanticLogger.tagged`.
189
189
  def tagged(*tags, &block)
190
190
  # Allow named tags to be passed into the logger
191
- if tags.size == 1
191
+ # Rails::Rack::Logger passes logs as an array with a single argument
192
+ if tags.size == 1 && !tags.first.is_a?(Array)
192
193
  tag = tags[0]
193
194
  return yield if tag.nil? || tag == ""
194
195
 
@@ -376,7 +377,7 @@ module SemanticLogger
376
377
  exception = e
377
378
  ensure
378
379
  # Must use ensure block otherwise a `return` in the yield above will skip the log entry
379
- log = Log.new(name, level, index)
380
+ log = Log.new(name, level, index)
380
381
  exception ||= params[:exception]
381
382
  message = params[:message] if params[:message]
382
383
  duration =
@@ -70,6 +70,7 @@ module SemanticLogger
70
70
 
71
71
  # Return the Time as a formatted string
72
72
  def format_time(time)
73
+ time = time.dup
73
74
  case time_format
74
75
  when :rfc_3339
75
76
  time.utc.to_datetime.rfc3339
@@ -109,14 +109,14 @@ module SemanticLogger
109
109
  def payload
110
110
  return unless log.payload?
111
111
 
112
- if !log.payload.respond_to?(:ai)
113
- super
114
- else
112
+ if log.payload.respond_to?(:ai)
115
113
  begin
116
114
  "-- #{log.payload.ai(@ai_options)}"
117
115
  rescue StandardError
118
116
  super
119
117
  end
118
+ else
119
+ super
120
120
  end
121
121
  end
122
122
 
@@ -0,0 +1,72 @@
1
+ require "json"
2
+
3
+ module SemanticLogger
4
+ module Formatters
5
+ # Produces logfmt formatted messages
6
+ #
7
+ # The following fields are extracted from the raw log and included in the formatted message:
8
+ # :timestamp, :level, :name, :message, :duration, :tags, :named_tags
9
+ #
10
+ # E.g.
11
+ # timestamp="2020-07-20T08:32:05.375276Z" level=info name="DefaultTest" base="breakfast" spaces="second breakfast" double_quotes="\"elevensies\"" single_quotes="'lunch'" tag="success"
12
+ #
13
+ # All timestamps are ISO8601 formatteed
14
+ # All user supplied values are escaped and surrounded by double quotes to avoid ambiguious message delimeters
15
+ # `tags` are treated as keys with boolean values. Tag names are not formatted or validated, ensure you use valid logfmt format for tag names.
16
+ # `named_tags` are flattened are merged into the top level message field. Any conflicting fields are overridden.
17
+ # `payload` values take precedence over `tags` and `named_tags`. Any conflicting fields are overridden.
18
+ #
19
+ # Futher Reading https://brandur.org/logfmt
20
+ class Logfmt < Raw
21
+ def initialize(time_format: :iso_8601, time_key: :timestamp, **args)
22
+ super(time_format: time_format, time_key: time_key, **args)
23
+ end
24
+
25
+ def call(log, logger)
26
+ @raw = super(log, logger)
27
+
28
+ raw_to_logfmt
29
+ end
30
+
31
+ private
32
+
33
+ def raw_to_logfmt
34
+ @parsed = @raw.slice(time_key, :level, :name, :message, :duration).merge(tag: "success")
35
+ handle_tags
36
+ handle_payload
37
+ handle_exception
38
+
39
+ flatten_log
40
+ end
41
+
42
+ def handle_tags
43
+ tags = @raw.fetch(:tags){ [] }
44
+ .each_with_object({}){ |tag, accum| accum[tag] = true }
45
+
46
+ @parsed = @parsed.merge(tags)
47
+ .merge(@raw.fetch(:named_tags){ {} })
48
+ end
49
+
50
+ def handle_payload
51
+ return unless @raw.key? :payload
52
+
53
+ @parsed = @parsed.merge(@raw[:payload])
54
+ end
55
+
56
+ def handle_exception
57
+ return unless @raw.key? :exception
58
+
59
+ @parsed[:tag] = "exception"
60
+ @parsed = @parsed.merge(@raw[:exception])
61
+ end
62
+
63
+ def flatten_log
64
+ flattened = @parsed.map do |key, value|
65
+ "#{key}=#{value.to_json}"
66
+ end
67
+
68
+ flattened.join(" ")
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,7 +1,8 @@
1
1
  begin
2
2
  require "syslog_protocol"
3
3
  rescue LoadError
4
- raise LoadError, 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
4
+ raise LoadError,
5
+ 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
5
6
  end
6
7
 
7
8
  module SemanticLogger
@@ -1,7 +1,8 @@
1
1
  begin
2
2
  require "syslog_protocol"
3
3
  rescue LoadError
4
- raise LoadError, 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
4
+ raise LoadError,
5
+ 'Gem syslog_protocol is required for remote logging using the Syslog protocol. Please add the gem "syslog_protocol" to your Gemfile.'
5
6
  end
6
7
 
7
8
  module SemanticLogger
@@ -1,16 +1,15 @@
1
1
  module SemanticLogger
2
2
  module Formatters
3
- # @formatter:off
4
- autoload :Base, "semantic_logger/formatters/base"
5
- autoload :Color, "semantic_logger/formatters/color"
6
- autoload :Default, "semantic_logger/formatters/default"
7
- autoload :Json, "semantic_logger/formatters/json"
8
- autoload :Raw, "semantic_logger/formatters/raw"
9
- autoload :OneLine, "semantic_logger/formatters/one_line"
10
- autoload :Signalfx, "semantic_logger/formatters/signalfx"
11
- autoload :Syslog, "semantic_logger/formatters/syslog"
12
- autoload :Fluentd, "semantic_logger/formatters/fluentd"
13
- # @formatter:on
3
+ autoload :Base, "semantic_logger/formatters/base"
4
+ autoload :Color, "semantic_logger/formatters/color"
5
+ autoload :Default, "semantic_logger/formatters/default"
6
+ autoload :Json, "semantic_logger/formatters/json"
7
+ autoload :Raw, "semantic_logger/formatters/raw"
8
+ autoload :OneLine, "semantic_logger/formatters/one_line"
9
+ autoload :Signalfx, "semantic_logger/formatters/signalfx"
10
+ autoload :Syslog, "semantic_logger/formatters/syslog"
11
+ autoload :Fluentd, "semantic_logger/formatters/fluentd"
12
+ autoload :Logfmt, "semantic_logger/formatters/logfmt"
14
13
 
15
14
  # Return formatter that responds to call.
16
15
  #
@@ -114,14 +114,12 @@ module SemanticLogger
114
114
 
115
115
  # Elastic logging: Log when :duration exceeds :min_duration
116
116
  # Except if there is an exception when it will always be logged
117
- if duration
118
- return false if (duration < min_duration) && exception.nil?
119
- end
117
+ return false if duration && ((duration < min_duration) && exception.nil?)
120
118
 
121
119
  if backtrace
122
120
  self.backtrace = Utils.extract_backtrace(backtrace)
123
121
  elsif level_index >= SemanticLogger.backtrace_level_index
124
- self.backtrace = Utils.extract_backtrace
122
+ self.backtrace = Utils.extract_backtrace(caller)
125
123
  end
126
124
 
127
125
  true
@@ -8,7 +8,7 @@
8
8
  # Example:
9
9
  # require 'semantic_logger'
10
10
  # SemanticLogger.default_level = :debug
11
- # SemanticLogger.add_appender(io: STDOUT, formatter: :color)
11
+ # SemanticLogger.add_appender(io: $stdout, formatter: :color)
12
12
  #
13
13
  # class ExternalSupplier
14
14
  # # Create class and instance logger methods
@@ -32,7 +32,14 @@ module SemanticLogger
32
32
  module Loggable
33
33
  def self.included(base)
34
34
  base.extend ClassMethods
35
+ base.singleton_class.class_eval do
36
+ undef_method :logger if method_defined?(:logger)
37
+ undef_method :logger= if method_defined?(:logger=)
38
+ end
35
39
  base.class_eval do
40
+ undef_method :logger if method_defined?(:logger)
41
+ undef_method :logger= if method_defined?(:logger=)
42
+
36
43
  # Returns [SemanticLogger::Logger] class level logger
37
44
  def self.logger
38
45
  @semantic_logger ||= SemanticLogger[self]
@@ -21,7 +21,19 @@ module SemanticLogger
21
21
  end
22
22
 
23
23
  def self.processor
24
- @processor ||= SemanticLogger.sync? ? SyncProcessor.new : Processor.new
24
+ @processor ||= Processor.new
25
+ end
26
+
27
+ # Switch to the synchronous processor
28
+ def self.sync!
29
+ return if @processor.is_a?(SyncProcessor)
30
+
31
+ @processor = SyncProcessor.new(@processor&.appenders)
32
+ end
33
+
34
+ # Running without the background logging thread?
35
+ def self.sync?
36
+ processor.is_a?(SyncProcessor)
25
37
  end
26
38
 
27
39
  # Returns a Logger instance
@@ -70,11 +82,9 @@ module SemanticLogger
70
82
  return unless @subscribers
71
83
 
72
84
  @subscribers.each do |subscriber|
73
- begin
74
- subscriber.call(log)
75
- rescue Exception => e
76
- processor.logger.error("Exception calling :on_log subscriber", e)
77
- end
85
+ subscriber.call(log)
86
+ rescue Exception => e
87
+ processor.logger.error("Exception calling :on_log subscriber", e)
78
88
  end
79
89
  end
80
90
  end
@@ -1,7 +1,7 @@
1
1
  module SemanticLogger
2
2
  # Thread that submits and processes log requests
3
3
  class Processor < Appender::Async
4
- # Allow the internal logger to be overridden from its default of STDERR
4
+ # Allow the internal logger to be overridden from its default of $stderr
5
5
  # Can be replaced with another Ruby logger or Rails logger, but never to
6
6
  # SemanticLogger::Logger itself since it is for reporting problems
7
7
  # while trying to log to the various appenders
@@ -11,11 +11,11 @@ module SemanticLogger
11
11
 
12
12
  # Internal logger for SemanticLogger
13
13
  # For example when an appender is not working etc..
14
- # By default logs to STDERR
14
+ # By default logs to $stderr
15
15
  def self.logger
16
16
  @logger ||=
17
17
  begin
18
- l = SemanticLogger::Appender::File.new(io: STDERR, level: :warn)
18
+ l = SemanticLogger::Appender::IO.new($stderr, level: :warn)
19
19
  l.name = name
20
20
  l
21
21
  end
@@ -101,7 +101,7 @@ module SemanticLogger
101
101
  # Or,
102
102
  # io: [IO]
103
103
  # An IO Stream to log to.
104
- # For example STDOUT, STDERR, etc.
104
+ # For example $stdout, $stderr, etc.
105
105
  #
106
106
  # Or,
107
107
  # appender: [Symbol|SemanticLogger::Subscriber]
@@ -122,7 +122,7 @@ module SemanticLogger
122
122
  # Default: SemanticLogger.default_level
123
123
  #
124
124
  # formatter: [Symbol|Object|Proc]
125
- # Any of the following symbol values: :default, :color, :json
125
+ # Any of the following symbol values: :default, :color, :json, :logfmt, etc...
126
126
  # Or,
127
127
  # An instance of a class that implements #call
128
128
  # Or,
@@ -138,14 +138,14 @@ module SemanticLogger
138
138
  # Examples:
139
139
  #
140
140
  # # Send all logging output to Standard Out (Screen)
141
- # SemanticLogger.add_appender(io: STDOUT)
141
+ # SemanticLogger.add_appender(io: $stdout)
142
142
  #
143
143
  # # Send all logging output to a file
144
144
  # SemanticLogger.add_appender(file_name: 'logfile.log')
145
145
  #
146
146
  # # Send all logging output to a file and only :info and above to standard output
147
147
  # SemanticLogger.add_appender(file_name: 'logfile.log')
148
- # SemanticLogger.add_appender(io: STDOUT, level: :info)
148
+ # SemanticLogger.add_appender(io: $stdout, level: :info)
149
149
  #
150
150
  # Log to log4r, Logger, etc.:
151
151
  #
@@ -154,7 +154,7 @@ module SemanticLogger
154
154
  # require 'semantic_logger'
155
155
  #
156
156
  # # Built-in Ruby logger
157
- # log = Logger.new(STDOUT)
157
+ # log = Logger.new($stdout)
158
158
  # log.level = Logger::DEBUG
159
159
  #
160
160
  # SemanticLogger.default_level = :debug
@@ -164,7 +164,7 @@ module SemanticLogger
164
164
  # logger.info "Hello World"
165
165
  # logger.debug("Login time", user: 'Joe', duration: 100, ip_address: '127.0.0.1')
166
166
  def self.add_appender(**args, &block)
167
- appender = Logger.processor.appenders.add(**args, &block)
167
+ appender = appenders.add(**args, &block)
168
168
  # Start appender thread if it is not already running
169
169
  Logger.processor.start
170
170
  appender
@@ -173,7 +173,15 @@ module SemanticLogger
173
173
  # Remove an existing appender
174
174
  # Currently only supports appender instances
175
175
  def self.remove_appender(appender)
176
- Logger.processor.appenders.delete(appender)
176
+ return unless appender
177
+
178
+ appenders.delete(appender)
179
+ appender.close
180
+ end
181
+
182
+ # Clear out all previously registered appenders
183
+ def self.clear_appenders!
184
+ Logger.processor.close
177
185
  end
178
186
 
179
187
  # Returns [SemanticLogger::Subscriber] a copy of the list of active
@@ -181,7 +189,7 @@ module SemanticLogger
181
189
  # Use SemanticLogger.add_appender and SemanticLogger.remove_appender
182
190
  # to manipulate the active appenders list
183
191
  def self.appenders
184
- Logger.processor.appenders.to_a
192
+ Logger.processor.appenders
185
193
  end
186
194
 
187
195
  # Flush all queued log entries disk, database, etc.
@@ -494,12 +502,12 @@ module SemanticLogger
494
502
  # I.e. Instead of logging messages in a separate thread for better performance,
495
503
  # log them using the current thread.
496
504
  def self.sync!
497
- @sync = true
505
+ Logger.sync!
498
506
  end
499
507
 
500
508
  # Running in synchronous mode?
501
509
  def self.sync?
502
- @sync
510
+ Logger.sync?
503
511
  end
504
512
 
505
513
  # Initial default Level for all new instances of SemanticLogger::Logger
@@ -22,6 +22,11 @@ module SemanticLogger
22
22
  # NOOP
23
23
  end
24
24
 
25
+ # Method called to log an event
26
+ def log(log)
27
+ raise NotImplementedError
28
+ end
29
+
25
30
  # Returns [SemanticLogger::Formatters::Default] default formatter for this subscriber.
26
31
  def default_formatter
27
32
  SemanticLogger::Formatters::Default.new
@@ -68,6 +73,11 @@ module SemanticLogger
68
73
  super(log) && (log.metric_only? ? metrics? : true)
69
74
  end
70
75
 
76
+ # Whether this appender is logging to stdout or stderror
77
+ def console_output?
78
+ false
79
+ end
80
+
71
81
  private
72
82
 
73
83
  # Initializer for Abstract Class SemanticLogger::Subscriber
@@ -10,7 +10,7 @@ module SemanticLogger
10
10
  def_delegator :@appenders, :close
11
11
  def_delegator :@appenders, :reopen
12
12
 
13
- # Allow the internal logger to be overridden from its default of STDERR
13
+ # Allow the internal logger to be overridden from its default of $stderr
14
14
  # Can be replaced with another Ruby logger or Rails logger, but never to
15
15
  # SemanticLogger::Logger itself since it is for reporting problems
16
16
  # while trying to log to the various appenders
@@ -20,11 +20,11 @@ module SemanticLogger
20
20
 
21
21
  # Internal logger for SemanticLogger
22
22
  # For example when an appender is not working etc..
23
- # By default logs to STDERR
23
+ # By default logs to $stderr
24
24
  def self.logger
25
25
  @logger ||=
26
26
  begin
27
- l = SemanticLogger::Appender::File.new(io: STDERR, level: :warn)
27
+ l = SemanticLogger::Appender::IO.new($stderr, level: :warn)
28
28
  l.name = name
29
29
  l
30
30
  end
@@ -32,8 +32,8 @@ module SemanticLogger
32
32
 
33
33
  attr_reader :appenders
34
34
 
35
- def initialize
36
- @appenders = Appenders.new(self.class.logger.dup)
35
+ def initialize(appenders = nil)
36
+ @appenders = appenders || Appenders.new(self.class.logger.dup)
37
37
  end
38
38
 
39
39
  def start
@@ -0,0 +1,34 @@
1
+ module SemanticLogger
2
+ module Test
3
+ # Logging class to captures all logging events in memory.
4
+ #
5
+ # Example:
6
+ #
7
+ # class UserTest < ActiveSupport::TestCase
8
+ # describe User do
9
+ # let(:capture_logger) { SemanticLogger::Test::CaptureLogEvents.new }
10
+ # let(:user) { User.new }
11
+ #
12
+ # it "logs message" do
13
+ # user.stub(:logger, capture_logger) do
14
+ # user.enable!
15
+ # end
16
+ # assert_equal "Hello World", capture_logger.events.last.message
17
+ # assert_equal :info, capture_logger.events.last.level
18
+ # end
19
+ # end
20
+ # end
21
+ class CaptureLogEvents < SemanticLogger::Subscriber
22
+ attr_accessor :events
23
+
24
+ # By default collect all log levels, and collect metric only log events.
25
+ def initialize(level: :trace, metrics: true)
26
+ super(level: level, metrics: true)
27
+ end
28
+
29
+ def log(log)
30
+ (@events ||= []) << log
31
+ end
32
+ end
33
+ end
34
+ end