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.
- checksums.yaml +4 -4
- data/README.md +47 -19
- data/lib/semantic_logger/appender/async.rb +1 -1
- data/lib/semantic_logger/appender/elasticsearch.rb +24 -6
- data/lib/semantic_logger/appender/file.rb +249 -68
- data/lib/semantic_logger/appender/io.rb +68 -0
- data/lib/semantic_logger/appender/kafka.rb +4 -0
- data/lib/semantic_logger/appender/splunk.rb +2 -1
- data/lib/semantic_logger/appender/splunk_http.rb +2 -2
- data/lib/semantic_logger/appender/syslog.rb +5 -3
- data/lib/semantic_logger/appender/wrapper.rb +3 -2
- data/lib/semantic_logger/appender.rb +12 -6
- data/lib/semantic_logger/appenders.rb +31 -27
- data/lib/semantic_logger/base.rb +5 -4
- data/lib/semantic_logger/formatters/base.rb +1 -0
- data/lib/semantic_logger/formatters/color.rb +3 -3
- data/lib/semantic_logger/formatters/logfmt.rb +72 -0
- data/lib/semantic_logger/formatters/syslog.rb +2 -1
- data/lib/semantic_logger/formatters/syslog_cee.rb +2 -1
- data/lib/semantic_logger/formatters.rb +10 -11
- data/lib/semantic_logger/log.rb +2 -4
- data/lib/semantic_logger/loggable.rb +8 -1
- data/lib/semantic_logger/logger.rb +16 -6
- data/lib/semantic_logger/processor.rb +3 -3
- data/lib/semantic_logger/semantic_logger.rb +18 -10
- data/lib/semantic_logger/subscriber.rb +10 -0
- data/lib/semantic_logger/sync_processor.rb +5 -5
- data/lib/semantic_logger/test/capture_log_events.rb +34 -0
- data/lib/semantic_logger/utils.rb +29 -10
- data/lib/semantic_logger/version.rb +1 -1
- data/lib/semantic_logger.rb +4 -0
- metadata +8 -6
@@ -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/
|
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,
|
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,
|
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(
|
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,
|
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
|
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
|
60
|
-
SemanticLogger::Appender::File.new(
|
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,
|
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,
|
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,
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
57
|
-
next unless appender.respond_to?(:reopen)
|
62
|
+
next unless appender.respond_to?(:reopen)
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
data/lib/semantic_logger/base.rb
CHANGED
@@ -63,7 +63,7 @@ module SemanticLogger
|
|
63
63
|
# SemanticLogger.default_level = :info
|
64
64
|
#
|
65
65
|
# # Log to screen
|
66
|
-
# SemanticLogger.add_appender(io:
|
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
|
-
|
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
|
380
|
+
log = Log.new(name, level, index)
|
380
381
|
exception ||= params[:exception]
|
381
382
|
message = params[:message] if params[:message]
|
382
383
|
duration =
|
@@ -109,14 +109,14 @@ module SemanticLogger
|
|
109
109
|
def payload
|
110
110
|
return unless log.payload?
|
111
111
|
|
112
|
-
if
|
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,
|
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,
|
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
|
-
|
4
|
-
autoload :
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
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
|
#
|
data/lib/semantic_logger/log.rb
CHANGED
@@ -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:
|
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 ||=
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
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
|
14
|
+
# By default logs to $stderr
|
15
15
|
def self.logger
|
16
16
|
@logger ||=
|
17
17
|
begin
|
18
|
-
l = SemanticLogger::Appender::
|
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
|
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:
|
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:
|
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(
|
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 =
|
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
|
-
|
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
|
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
|
-
|
505
|
+
Logger.sync!
|
498
506
|
end
|
499
507
|
|
500
508
|
# Running in synchronous mode?
|
501
509
|
def self.sync?
|
502
|
-
|
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
|
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
|
23
|
+
# By default logs to $stderr
|
24
24
|
def self.logger
|
25
25
|
@logger ||=
|
26
26
|
begin
|
27
|
-
l = SemanticLogger::Appender::
|
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
|