semantic_logger 4.17.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +42 -81
- data/Rakefile +8 -1
- data/lib/semantic_logger/appender/async.rb +86 -173
- data/lib/semantic_logger/appender/cloudwatch_logs.rb +4 -4
- data/lib/semantic_logger/appender/elasticsearch.rb +6 -182
- data/lib/semantic_logger/appender/elasticsearch_base.rb +212 -0
- data/lib/semantic_logger/appender/elasticsearch_http.rb +2 -2
- data/lib/semantic_logger/appender/file.rb +20 -4
- data/lib/semantic_logger/appender/graylog.rb +2 -2
- data/lib/semantic_logger/appender/honeybadger_insights.rb +1 -1
- data/lib/semantic_logger/appender/http.rb +27 -2
- data/lib/semantic_logger/appender/io.rb +8 -4
- data/lib/semantic_logger/appender/kafka.rb +2 -2
- data/lib/semantic_logger/appender/loki.rb +2 -4
- data/lib/semantic_logger/appender/mongodb.rb +3 -6
- data/lib/semantic_logger/appender/new_relic_logs.rb +12 -2
- data/lib/semantic_logger/appender/open_telemetry.rb +25 -11
- data/lib/semantic_logger/appender/opensearch.rb +35 -0
- data/lib/semantic_logger/appender/rabbitmq.rb +3 -3
- data/lib/semantic_logger/appender/sentry_ruby.rb +8 -3
- data/lib/semantic_logger/appender/splunk.rb +2 -2
- data/lib/semantic_logger/appender/splunk_http.rb +3 -3
- data/lib/semantic_logger/appender/syslog.rb +5 -4
- data/lib/semantic_logger/appender/tcp.rb +2 -2
- data/lib/semantic_logger/appender/udp.rb +2 -2
- data/lib/semantic_logger/appender/wrapper.rb +4 -4
- data/lib/semantic_logger/appender.rb +30 -19
- data/lib/semantic_logger/appenders.rb +26 -5
- data/lib/semantic_logger/base.rb +113 -21
- data/lib/semantic_logger/concerns/compatibility.rb +2 -2
- data/lib/semantic_logger/core_ext/process.rb +34 -0
- data/lib/semantic_logger/formatters/base.rb +46 -7
- data/lib/semantic_logger/formatters/color.rb +6 -3
- data/lib/semantic_logger/formatters/default.rb +6 -4
- data/lib/semantic_logger/formatters/ecs.rb +151 -0
- data/lib/semantic_logger/formatters/fluentd.rb +15 -4
- data/lib/semantic_logger/formatters/json.rb +6 -1
- data/lib/semantic_logger/formatters/logfmt.rb +2 -2
- data/lib/semantic_logger/formatters/loki.rb +4 -4
- data/lib/semantic_logger/formatters/new_relic_logs.rb +4 -6
- data/lib/semantic_logger/formatters/open_telemetry.rb +40 -2
- data/lib/semantic_logger/formatters/pattern.rb +235 -0
- data/lib/semantic_logger/formatters/raw.rb +2 -2
- data/lib/semantic_logger/formatters/signalfx.rb +2 -2
- data/lib/semantic_logger/formatters/syslog.rb +14 -3
- data/lib/semantic_logger/formatters/syslog_cee.rb +1 -1
- data/lib/semantic_logger/formatters.rb +2 -0
- data/lib/semantic_logger/log.rb +23 -4
- data/lib/semantic_logger/logger.rb +2 -2
- data/lib/semantic_logger/metric/new_relic.rb +2 -2
- data/lib/semantic_logger/metric/signalfx.rb +2 -2
- data/lib/semantic_logger/metric/statsd.rb +2 -2
- data/lib/semantic_logger/processor.rb +22 -1
- data/lib/semantic_logger/queue_processor.rb +369 -0
- data/lib/semantic_logger/reporters/minitest.rb +4 -4
- data/lib/semantic_logger/semantic_logger.rb +103 -11
- data/lib/semantic_logger/subscriber.rb +15 -2
- data/lib/semantic_logger/sync_processor.rb +25 -3
- data/lib/semantic_logger/test/capture_log_events.rb +2 -2
- data/lib/semantic_logger/test/minitest.rb +8 -4
- data/lib/semantic_logger/test/rspec.rb +249 -0
- data/lib/semantic_logger/utils.rb +83 -4
- data/lib/semantic_logger/version.rb +1 -1
- data/lib/semantic_logger.rb +9 -0
- metadata +15 -6
- data/lib/semantic_logger/appender/async_batch.rb +0 -93
|
@@ -23,6 +23,11 @@ require "semantic_logger/formatters/new_relic_logs"
|
|
|
23
23
|
module SemanticLogger
|
|
24
24
|
module Appender
|
|
25
25
|
class NewRelicLogs < SemanticLogger::Subscriber
|
|
26
|
+
CAPTURE_CONTEXT = lambda do |log|
|
|
27
|
+
meta = ::NewRelic::Agent.linking_metadata
|
|
28
|
+
log.set_context(:new_relic_metadata, meta) unless meta.empty?
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
# Create Appender
|
|
27
32
|
#
|
|
28
33
|
# Parameters
|
|
@@ -42,14 +47,19 @@ module SemanticLogger
|
|
|
42
47
|
# The Proc must return true or false.
|
|
43
48
|
def initialize(formatter: SemanticLogger::Formatters::NewRelicLogs.new, **args, &block)
|
|
44
49
|
super
|
|
50
|
+
|
|
51
|
+
# Record NewRelic's "trace.id"/"entity.name"/"hostname"/etc, so we can include them later in the formatted output.
|
|
52
|
+
# These are thread-local, so need to be captured as soon as the log-message is created.
|
|
53
|
+
# https://rubydoc.info/gems/newrelic_rpm/NewRelic/Agent#linking_metadata-instance_method
|
|
54
|
+
SemanticLogger.on_log(CAPTURE_CONTEXT)
|
|
45
55
|
end
|
|
46
56
|
|
|
47
57
|
# Send an error notification to New Relic
|
|
48
58
|
def log(log)
|
|
49
59
|
begin
|
|
50
60
|
message = formatter.call(log, self) # Generate the structured log
|
|
51
|
-
json_message =
|
|
52
|
-
level = log.level.to_s.upcase
|
|
61
|
+
json_message = Utils.to_json(message) # Convert the log to JSON, repairing any non UTF-8 data
|
|
62
|
+
level = log.level.to_s.upcase # Determine the log level
|
|
53
63
|
self.class.log_newrelic(json_message, level)
|
|
54
64
|
rescue JSON::GeneratorError => e
|
|
55
65
|
warn("Failed to serialize log message to JSON: #{e.message}")
|
|
@@ -12,7 +12,7 @@ end
|
|
|
12
12
|
module SemanticLogger
|
|
13
13
|
module Appender
|
|
14
14
|
class OpenTelemetry < SemanticLogger::Subscriber
|
|
15
|
-
attr_reader :name, :version, :logger
|
|
15
|
+
attr_reader :name, :version, :provider, :logger
|
|
16
16
|
|
|
17
17
|
CAPTURE_CONTEXT = ->(log) { log.set_context(:open_telemetry, ::OpenTelemetry::Context.current) }
|
|
18
18
|
|
|
@@ -29,16 +29,19 @@ module SemanticLogger
|
|
|
29
29
|
formatter: SemanticLogger::Formatters::OpenTelemetry.new,
|
|
30
30
|
metrics: true,
|
|
31
31
|
**args,
|
|
32
|
-
&
|
|
33
|
-
@name
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
&)
|
|
33
|
+
# Base#initialize (via Subscriber) overwrites @name with the class name,
|
|
34
|
+
# so call super first and then assign our own attributes.
|
|
35
|
+
super(formatter: formatter, metrics: metrics, **args, &)
|
|
36
|
+
|
|
37
|
+
@name = name
|
|
38
|
+
@version = version
|
|
39
|
+
@provider = ::OpenTelemetry.logger_provider
|
|
40
|
+
@logger = @provider.logger(name: @name, version: @version)
|
|
36
41
|
|
|
37
42
|
# Capture the current Open Telemetry context when a log entry is captured.
|
|
38
43
|
# Prevents duplicate subscribers as long as it is from a constant.
|
|
39
44
|
SemanticLogger.on_log(CAPTURE_CONTEXT)
|
|
40
|
-
|
|
41
|
-
super(formatter: formatter, metrics: metrics, **args, &block)
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
def log(log)
|
|
@@ -56,19 +59,30 @@ module SemanticLogger
|
|
|
56
59
|
timestamp: time,
|
|
57
60
|
body: body.transform_keys!(&:to_s),
|
|
58
61
|
attributes: payload,
|
|
59
|
-
context: log.context[:open_telemetry] || ::OpenTelemetry::Context.current
|
|
62
|
+
context: (log.context && log.context[:open_telemetry]) || ::OpenTelemetry::Context.current
|
|
60
63
|
)
|
|
61
64
|
true
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
# Flush all pending logs.
|
|
65
68
|
def flush
|
|
66
|
-
@
|
|
69
|
+
return unless @provider
|
|
70
|
+
|
|
71
|
+
@provider.force_flush if @provider.respond_to?(:force_flush)
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
# Swallow to avoid noisy shutdown exceptions.
|
|
74
|
+
SemanticLogger::Processor.logger.warn("Flush failed: #{e.class}: #{e.message}")
|
|
67
75
|
end
|
|
68
76
|
|
|
69
|
-
#
|
|
77
|
+
# Close the appender and release resources.
|
|
70
78
|
def close
|
|
71
|
-
@
|
|
79
|
+
return unless @provider
|
|
80
|
+
|
|
81
|
+
@provider.shutdown if @provider.respond_to?(:shutdown)
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
SemanticLogger::Processor.logger.warn("Shutdown failed: #{e.class}: #{e.message}")
|
|
84
|
+
ensure
|
|
85
|
+
@provider = nil
|
|
72
86
|
end
|
|
73
87
|
|
|
74
88
|
# For logging metrics only log events.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require "opensearch-ruby"
|
|
3
|
+
rescue LoadError
|
|
4
|
+
raise LoadError,
|
|
5
|
+
'Gem opensearch-ruby is required for logging to OpenSearch. Please add the gem "opensearch-ruby" to your Gemfile.'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require "semantic_logger/appender/elasticsearch_base"
|
|
9
|
+
|
|
10
|
+
# Forward all log messages to OpenSearch (e.g. AWS OpenSearch).
|
|
11
|
+
#
|
|
12
|
+
# OpenSearch is a fork of Elasticsearch 7.10. The client API and bulk
|
|
13
|
+
# protocol are identical, so this appender reuses the same pipeline as the
|
|
14
|
+
# Elasticsearch appender; only the backing client gem differs. Use this
|
|
15
|
+
# appender (with the `opensearch-ruby` gem) instead of `:elasticsearch`
|
|
16
|
+
# when talking to an OpenSearch server, since modern `elasticsearch` gems
|
|
17
|
+
# reject non-Elasticsearch servers via a product check.
|
|
18
|
+
#
|
|
19
|
+
# Example:
|
|
20
|
+
#
|
|
21
|
+
# SemanticLogger.add_appender(
|
|
22
|
+
# appender: :opensearch,
|
|
23
|
+
# url: 'http://localhost:9200'
|
|
24
|
+
# )
|
|
25
|
+
module SemanticLogger
|
|
26
|
+
module Appender
|
|
27
|
+
class Opensearch < ElasticsearchBase
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def client_class
|
|
31
|
+
::OpenSearch::Client
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -12,7 +12,7 @@ end
|
|
|
12
12
|
# appender: :rabbitmq,
|
|
13
13
|
#
|
|
14
14
|
# # Name of the queue in RabbitMQ where to publish the logs. This queue will be bound to "amqp.direct" exchange.
|
|
15
|
-
#
|
|
15
|
+
# queue_name: 'semantic_logger',
|
|
16
16
|
#
|
|
17
17
|
# # This host will be used for RabbitMQ connection.
|
|
18
18
|
# # NOTE this is different than :host option which is used by the logger directly.
|
|
@@ -82,13 +82,13 @@ module SemanticLogger
|
|
|
82
82
|
# more parameters supported by Bunny: http://rubybunny.info/articles/connecting.html
|
|
83
83
|
def initialize(queue_name: "semantic_logger", rabbitmq_host: nil,
|
|
84
84
|
level: nil, formatter: nil, filter: nil, application: nil, environment: nil, host: nil, metrics: true,
|
|
85
|
-
**args, &
|
|
85
|
+
**args, &)
|
|
86
86
|
@queue_name = queue_name
|
|
87
87
|
@rabbitmq_args = args.dup
|
|
88
88
|
@rabbitmq_args[:host] = rabbitmq_host
|
|
89
89
|
@rabbitmq_args[:logger] = logger
|
|
90
90
|
|
|
91
|
-
super(level: level, formatter: formatter, filter: filter, application: application, environment: environment, host: host, metrics: metrics, &
|
|
91
|
+
super(level: level, formatter: formatter, filter: filter, application: application, environment: environment, host: host, metrics: metrics, &)
|
|
92
92
|
reopen
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -40,7 +40,12 @@ module SemanticLogger
|
|
|
40
40
|
def initialize(level: :error, **args, &block)
|
|
41
41
|
# Replace the Sentry Ruby logger so that we can identify its log
|
|
42
42
|
# messages and not forward them to Sentry
|
|
43
|
-
|
|
43
|
+
unless ::Sentry.initialized?
|
|
44
|
+
::Sentry.init do |config|
|
|
45
|
+
logger = SemanticLogger[::Sentry]
|
|
46
|
+
config.respond_to?(:sdk_logger=) ? config.sdk_logger = logger : config.logger = logger
|
|
47
|
+
end
|
|
48
|
+
end
|
|
44
49
|
super
|
|
45
50
|
end
|
|
46
51
|
|
|
@@ -128,10 +133,10 @@ module SemanticLogger
|
|
|
128
133
|
#
|
|
129
134
|
def extract_tags!(context)
|
|
130
135
|
named_tags = context.delete(:named_tags) || {}
|
|
131
|
-
named_tags = named_tags.
|
|
136
|
+
named_tags = named_tags.to_h { |k, v| [k.to_s, v.to_s] }
|
|
132
137
|
tags = context.delete(:tags)
|
|
133
138
|
named_tags.merge!("tag" => tags.join(", ")) { |_, v1, v2| "#{v1}, #{v2}" } if tags
|
|
134
|
-
named_tags.
|
|
139
|
+
named_tags.to_h { |k, v| [k[0...32], v[0...256]] }
|
|
135
140
|
end
|
|
136
141
|
end
|
|
137
142
|
end
|
|
@@ -89,11 +89,11 @@ module SemanticLogger
|
|
|
89
89
|
# regular expression. All other messages will be ignored.
|
|
90
90
|
# Proc: Only include log messages where the supplied Proc returns true
|
|
91
91
|
# The Proc must return true or false.
|
|
92
|
-
def initialize(index: "main", source_type: nil, **args, &
|
|
92
|
+
def initialize(index: "main", source_type: nil, **args, &)
|
|
93
93
|
@index = index
|
|
94
94
|
@source_type = source_type
|
|
95
95
|
|
|
96
|
-
super(**args, &
|
|
96
|
+
super(**args, &)
|
|
97
97
|
reopen
|
|
98
98
|
end
|
|
99
99
|
|
|
@@ -73,11 +73,11 @@ module SemanticLogger
|
|
|
73
73
|
index: nil,
|
|
74
74
|
compress: true,
|
|
75
75
|
**args,
|
|
76
|
-
&
|
|
76
|
+
&)
|
|
77
77
|
@source_type = source_type
|
|
78
78
|
@index = index
|
|
79
79
|
|
|
80
|
-
super(compress: compress, **args, &
|
|
80
|
+
super(compress: compress, **args, &)
|
|
81
81
|
|
|
82
82
|
# Put splunk auth token in the header of every HTTP post.
|
|
83
83
|
@header["Authorization"] = "Splunk #{token}"
|
|
@@ -98,7 +98,7 @@ module SemanticLogger
|
|
|
98
98
|
}
|
|
99
99
|
message[:sourcetype] = source_type if source_type
|
|
100
100
|
message[:index] = index if index
|
|
101
|
-
|
|
101
|
+
Utils.to_json(message)
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
104
|
end
|
|
@@ -130,7 +130,7 @@ module SemanticLogger
|
|
|
130
130
|
options: ::Syslog::LOG_PID | ::Syslog::LOG_CONS,
|
|
131
131
|
tcp_client: {},
|
|
132
132
|
**args,
|
|
133
|
-
&
|
|
133
|
+
&)
|
|
134
134
|
@options = options
|
|
135
135
|
@facility = facility
|
|
136
136
|
@max_size = max_size
|
|
@@ -165,7 +165,7 @@ module SemanticLogger
|
|
|
165
165
|
end
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
-
super(**args, &
|
|
168
|
+
super(**args, &)
|
|
169
169
|
reopen
|
|
170
170
|
end
|
|
171
171
|
|
|
@@ -213,8 +213,9 @@ module SemanticLogger
|
|
|
213
213
|
# Returns [SemanticLogger::Formatters::Base] default formatter for this Appender depending on the protocal selected
|
|
214
214
|
def default_formatter
|
|
215
215
|
if protocol == :syslog
|
|
216
|
-
# Format is text output without the time
|
|
217
|
-
|
|
216
|
+
# Format is text output without the time.
|
|
217
|
+
# Escape control characters so untrusted log data cannot forge syslog entries.
|
|
218
|
+
SemanticLogger::Formatters::Default.new(time_format: :notime, escape_control_chars: true)
|
|
218
219
|
else
|
|
219
220
|
SemanticLogger::Formatters::Syslog.new(facility: facility, level_map: level_map, max_size: max_size)
|
|
220
221
|
end
|
|
@@ -184,7 +184,7 @@ module SemanticLogger
|
|
|
184
184
|
# )
|
|
185
185
|
def initialize(separator: "\n",
|
|
186
186
|
level: nil, formatter: nil, filter: nil, application: nil, environment: nil, host: nil, metrics: false,
|
|
187
|
-
**tcp_client_args, &
|
|
187
|
+
**tcp_client_args, &)
|
|
188
188
|
@separator = separator
|
|
189
189
|
@tcp_client_args = tcp_client_args
|
|
190
190
|
|
|
@@ -192,7 +192,7 @@ module SemanticLogger
|
|
|
192
192
|
Net::TCPClient.logger = logger
|
|
193
193
|
Net::TCPClient.logger.name = "Net::TCPClient"
|
|
194
194
|
|
|
195
|
-
super(level: level, formatter: formatter, filter: filter, application: application, environment: environment, host: host, metrics: metrics, &
|
|
195
|
+
super(level: level, formatter: formatter, filter: filter, application: application, environment: environment, host: host, metrics: metrics, &)
|
|
196
196
|
reopen
|
|
197
197
|
end
|
|
198
198
|
|
|
@@ -61,11 +61,11 @@ module SemanticLogger
|
|
|
61
61
|
# appender: :udp,
|
|
62
62
|
# server: 'server:3300'
|
|
63
63
|
# )
|
|
64
|
-
def initialize(server:, udp_flags: 0, metrics: true, **args, &
|
|
64
|
+
def initialize(server:, udp_flags: 0, metrics: true, **args, &)
|
|
65
65
|
@server = server
|
|
66
66
|
@udp_flags = udp_flags
|
|
67
67
|
|
|
68
|
-
super(metrics: metrics, **args, &
|
|
68
|
+
super(metrics: metrics, **args, &)
|
|
69
69
|
reopen
|
|
70
70
|
end
|
|
71
71
|
|
|
@@ -38,17 +38,17 @@ module SemanticLogger
|
|
|
38
38
|
# logger.info('Hello World', some: :payload)
|
|
39
39
|
#
|
|
40
40
|
# Install the `rails_semantic_logger` gem to replace the Rails logger with Semantic Logger.
|
|
41
|
-
def initialize(logger:, **args, &
|
|
41
|
+
def initialize(logger:, **args, &)
|
|
42
42
|
@logger = logger
|
|
43
43
|
|
|
44
44
|
# Check if the custom appender responds to all the log levels. For example Ruby ::Logger
|
|
45
|
-
does_not_implement = LEVELS[1
|
|
45
|
+
does_not_implement = LEVELS[1..].find { |i| !@logger.respond_to?(i) }
|
|
46
46
|
if does_not_implement
|
|
47
47
|
raise(ArgumentError,
|
|
48
|
-
"Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1
|
|
48
|
+
"Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..].inspect}")
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
super(**args, &
|
|
51
|
+
super(**args, &)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
|
@@ -2,10 +2,10 @@ module SemanticLogger
|
|
|
2
2
|
module Appender
|
|
3
3
|
# @formatter:off
|
|
4
4
|
autoload :Async, "semantic_logger/appender/async"
|
|
5
|
-
autoload :AsyncBatch, "semantic_logger/appender/async_batch"
|
|
6
5
|
autoload :Bugsnag, "semantic_logger/appender/bugsnag"
|
|
7
6
|
autoload :CloudwatchLogs, "semantic_logger/appender/cloudwatch_logs"
|
|
8
7
|
autoload :Elasticsearch, "semantic_logger/appender/elasticsearch"
|
|
8
|
+
autoload :ElasticsearchBase, "semantic_logger/appender/elasticsearch_base"
|
|
9
9
|
autoload :ElasticsearchHttp, "semantic_logger/appender/elasticsearch_http"
|
|
10
10
|
autoload :File, "semantic_logger/appender/file"
|
|
11
11
|
autoload :Graylog, "semantic_logger/appender/graylog"
|
|
@@ -18,6 +18,7 @@ module SemanticLogger
|
|
|
18
18
|
autoload :MongoDB, "semantic_logger/appender/mongodb"
|
|
19
19
|
autoload :NewRelic, "semantic_logger/appender/new_relic"
|
|
20
20
|
autoload :NewRelicLogs, "semantic_logger/appender/new_relic_logs"
|
|
21
|
+
autoload :Opensearch, "semantic_logger/appender/opensearch"
|
|
21
22
|
autoload :OpenTelemetry, "semantic_logger/appender/open_telemetry"
|
|
22
23
|
autoload :Rabbitmq, "semantic_logger/appender/rabbitmq"
|
|
23
24
|
autoload :Splunk, "semantic_logger/appender/splunk"
|
|
@@ -34,27 +35,37 @@ module SemanticLogger
|
|
|
34
35
|
def self.factory(async: false, batch: nil,
|
|
35
36
|
max_queue_size: 10_000, lag_check_interval: 1_000, lag_threshold_s: 30,
|
|
36
37
|
batch_size: 300, batch_seconds: 5,
|
|
38
|
+
non_blocking: false, dropped_message_report_seconds: 30,
|
|
39
|
+
async_max_retries: 100,
|
|
37
40
|
**args,
|
|
38
|
-
&
|
|
39
|
-
appender = build(**args, &
|
|
41
|
+
&)
|
|
42
|
+
appender = build(**args, &)
|
|
40
43
|
|
|
41
|
-
# If appender implements #batch, then it should use the batch proxy by default
|
|
42
|
-
|
|
44
|
+
# If appender implements #batch, then it should use the batch proxy by default,
|
|
45
|
+
# unless the appender opts out of batching by default (e.g. the HTTP appender).
|
|
46
|
+
batch = true if batch.nil? && appender.respond_to?(:batch) && appender.batch_by_default?
|
|
43
47
|
|
|
44
48
|
if batch == true
|
|
45
|
-
Appender::
|
|
46
|
-
appender:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
Appender::Async.new(
|
|
50
|
+
appender: appender,
|
|
51
|
+
batch: true,
|
|
52
|
+
max_queue_size: max_queue_size,
|
|
53
|
+
lag_threshold_s: lag_threshold_s,
|
|
54
|
+
batch_size: batch_size,
|
|
55
|
+
batch_seconds: batch_seconds,
|
|
56
|
+
non_blocking: non_blocking,
|
|
57
|
+
dropped_message_report_seconds: dropped_message_report_seconds,
|
|
58
|
+
async_max_retries: async_max_retries
|
|
51
59
|
)
|
|
52
60
|
elsif async == true
|
|
53
61
|
Appender::Async.new(
|
|
54
|
-
appender:
|
|
55
|
-
max_queue_size:
|
|
56
|
-
lag_check_interval:
|
|
57
|
-
lag_threshold_s:
|
|
62
|
+
appender: appender,
|
|
63
|
+
max_queue_size: max_queue_size,
|
|
64
|
+
lag_check_interval: lag_check_interval,
|
|
65
|
+
lag_threshold_s: lag_threshold_s,
|
|
66
|
+
non_blocking: non_blocking,
|
|
67
|
+
dropped_message_report_seconds: dropped_message_report_seconds,
|
|
68
|
+
async_max_retries: async_max_retries
|
|
58
69
|
)
|
|
59
70
|
else
|
|
60
71
|
appender
|
|
@@ -62,13 +73,13 @@ module SemanticLogger
|
|
|
62
73
|
end
|
|
63
74
|
|
|
64
75
|
# Returns [Subscriber] instance from the supplied options.
|
|
65
|
-
def self.build(io: nil, file_name: nil, appender: nil, metric: nil, logger: nil, **args, &
|
|
76
|
+
def self.build(io: nil, file_name: nil, appender: nil, metric: nil, logger: nil, **args, &)
|
|
66
77
|
if file_name
|
|
67
|
-
SemanticLogger::Appender::File.new(file_name, **args, &
|
|
78
|
+
SemanticLogger::Appender::File.new(file_name, **args, &)
|
|
68
79
|
elsif io
|
|
69
|
-
SemanticLogger::Appender::IO.new(io, **args, &
|
|
80
|
+
SemanticLogger::Appender::IO.new(io, **args, &)
|
|
70
81
|
elsif logger
|
|
71
|
-
SemanticLogger::Appender::Wrapper.new(logger: logger, **args, &
|
|
82
|
+
SemanticLogger::Appender::Wrapper.new(logger: logger, **args, &)
|
|
72
83
|
elsif appender
|
|
73
84
|
if appender.is_a?(Symbol)
|
|
74
85
|
SemanticLogger::Utils.constantize_symbol(appender).new(**args)
|
|
@@ -9,11 +9,12 @@ module SemanticLogger
|
|
|
9
9
|
super()
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def add(**args, &
|
|
13
|
-
appender = SemanticLogger::Appender.factory(**args, &
|
|
12
|
+
def add(**args, &)
|
|
13
|
+
appender = SemanticLogger::Appender.factory(**args, &)
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
stream = appender.respond_to?(:console_stream) && appender.console_stream
|
|
16
|
+
if stream && console_streams.include?(stream)
|
|
17
|
+
logger.warn "Ignoring attempt to add a second #{stream} console appender since it would result in duplicate console output."
|
|
17
18
|
return
|
|
18
19
|
end
|
|
19
20
|
|
|
@@ -21,10 +22,30 @@ module SemanticLogger
|
|
|
21
22
|
appender
|
|
22
23
|
end
|
|
23
24
|
|
|
25
|
+
# The console streams (:stdout and/or :stderr) already being written to by the existing appenders.
|
|
26
|
+
def console_streams
|
|
27
|
+
filter_map { |appender| appender.console_stream if appender.respond_to?(:console_stream) }
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
# Whether any of the existing appenders already output to the console?
|
|
25
31
|
# I.e. Writes to stdout or stderr.
|
|
26
32
|
def console_output?
|
|
27
|
-
any?
|
|
33
|
+
console_streams.any?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns [Array<Hash>] operational statistics for each appender.
|
|
37
|
+
#
|
|
38
|
+
# Appenders that run asynchronously (see SemanticLogger::Appender::Async#stats) report
|
|
39
|
+
# their queue size and processed/dropped counts. Appenders that log inline on the
|
|
40
|
+
# processor thread report only their name with `async: false`.
|
|
41
|
+
def stats
|
|
42
|
+
map do |appender|
|
|
43
|
+
if appender.respond_to?(:stats)
|
|
44
|
+
appender.stats
|
|
45
|
+
else
|
|
46
|
+
{name: appender.name, async: false}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
28
49
|
end
|
|
29
50
|
|
|
30
51
|
def log(log)
|