semantic_logger 5.1.0 → 5.2.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 +13 -12
- data/Rakefile +15 -4
- data/docs/api.md +37 -11
- data/docs/appenders.md +123 -39
- data/docs/config.md +115 -13
- data/docs/index.md +31 -5
- data/docs/log.md +19 -11
- data/docs/metrics.md +5 -2
- data/docs/operations.md +63 -17
- data/docs/rails.md +77 -3
- data/docs/security.md +183 -16
- data/docs/testing.md +5 -3
- data/docs/upgrading.md +295 -7
- data/lib/semantic_logger/appender/async.rb +18 -5
- data/lib/semantic_logger/appender/bugsnag.rb +1 -1
- data/lib/semantic_logger/appender/cloudwatch_logs.rb +122 -27
- data/lib/semantic_logger/appender/elasticsearch_base.rb +31 -6
- data/lib/semantic_logger/appender/elasticsearch_http.rb +1 -1
- data/lib/semantic_logger/appender/file.rb +138 -30
- data/lib/semantic_logger/appender/graylog.rb +22 -6
- data/lib/semantic_logger/appender/honeybadger.rb +1 -1
- data/lib/semantic_logger/appender/honeybadger_insights.rb +1 -1
- data/lib/semantic_logger/appender/http.rb +70 -11
- data/lib/semantic_logger/appender/io.rb +1 -1
- data/lib/semantic_logger/appender/kafka.rb +26 -12
- data/lib/semantic_logger/appender/loki.rb +13 -3
- data/lib/semantic_logger/appender/mongodb.rb +20 -1
- data/lib/semantic_logger/appender/new_relic.rb +1 -1
- data/lib/semantic_logger/appender/new_relic_logs.rb +9 -11
- data/lib/semantic_logger/appender/open_telemetry.rb +2 -2
- data/lib/semantic_logger/appender/rabbitmq.rb +6 -1
- data/lib/semantic_logger/appender/sentry.rb +1 -1
- data/lib/semantic_logger/appender/sentry_ruby.rb +91 -17
- data/lib/semantic_logger/appender/splunk.rb +10 -1
- data/lib/semantic_logger/appender/syslog.rb +64 -26
- data/lib/semantic_logger/appender/tcp.rb +6 -1
- data/lib/semantic_logger/appender/udp.rb +11 -1
- data/lib/semantic_logger/appender/wrapper.rb +1 -1
- data/lib/semantic_logger/appenders.rb +9 -1
- data/lib/semantic_logger/base.rb +82 -30
- data/lib/semantic_logger/exception_details.rb +50 -0
- data/lib/semantic_logger/formatters/base.rb +52 -6
- data/lib/semantic_logger/formatters/color.rb +18 -2
- data/lib/semantic_logger/formatters/default.rb +22 -5
- data/lib/semantic_logger/formatters/logfmt.rb +153 -21
- data/lib/semantic_logger/formatters/loki.rb +26 -22
- data/lib/semantic_logger/formatters/one_line.rb +67 -3
- data/lib/semantic_logger/formatters/open_telemetry.rb +16 -17
- data/lib/semantic_logger/formatters/pattern.rb +8 -4
- data/lib/semantic_logger/formatters/raw.rb +4 -5
- data/lib/semantic_logger/formatters/syslog.rb +110 -18
- data/lib/semantic_logger/formatters/syslog_cee.rb +29 -16
- data/lib/semantic_logger/levels.rb +10 -1
- data/lib/semantic_logger/log.rb +183 -37
- data/lib/semantic_logger/loggable.rb +26 -3
- data/lib/semantic_logger/metric/new_relic.rb +1 -1
- data/lib/semantic_logger/metric/signalfx.rb +8 -6
- data/lib/semantic_logger/metric/statsd.rb +15 -7
- data/lib/semantic_logger/queue_processor/request.rb +55 -0
- data/lib/semantic_logger/queue_processor/throttled_counter.rb +46 -0
- data/lib/semantic_logger/queue_processor.rb +165 -66
- data/lib/semantic_logger/semantic_logger.rb +15 -2
- data/lib/semantic_logger/subscriber.rb +96 -10
- data/lib/semantic_logger/sync_processor.rb +11 -0
- data/lib/semantic_logger/test/capture_log_events.rb +8 -5
- data/lib/semantic_logger/utils.rb +244 -16
- data/lib/semantic_logger/version.rb +1 -1
- data/lib/semantic_logger.rb +1 -1
- metadata +21 -5
- data/lib/semantic_logger/core_ext/thread.rb +0 -30
data/docs/security.md
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
|
+
title: Security
|
|
4
|
+
description: >-
|
|
5
|
+
Keeping logs trustworthy: log injection and forging, redacting sensitive
|
|
6
|
+
data, transport encryption, log file permissions, and the gem's dependency
|
|
7
|
+
footprint.
|
|
3
8
|
---
|
|
4
9
|
|
|
5
|
-
## Security
|
|
6
|
-
{:.no_toc}
|
|
7
10
|
|
|
8
11
|
**Contents**
|
|
9
12
|
|
|
@@ -52,6 +55,45 @@ The record-framed network appenders are already safe by default:
|
|
|
52
55
|
If you replace the formatter on any of these appenders with a text formatter, enable
|
|
53
56
|
`escape_control_chars` as shown above.
|
|
54
57
|
|
|
58
|
+
### Logging untrusted hashes
|
|
59
|
+
|
|
60
|
+
A hash passed after the message is the payload, but a few of its keys are also log arguments:
|
|
61
|
+
`duration`, `min_duration`, `metric`, `metric_amount`, `exception`, `log_exception`, and so on.
|
|
62
|
+
That is convenient for code you write, but when the hash holds untrusted data, such as parsed
|
|
63
|
+
request parameters or a webhook body with Symbol keys, it can set those arguments too. For
|
|
64
|
+
example `{duration: 0, min_duration: 1}` would suppress the entry.
|
|
65
|
+
|
|
66
|
+
Semantic Logger only uses such a key as an argument when its value is valid for it: for example
|
|
67
|
+
`duration` must be a finite real number, `backtrace` an Array of Strings, and `on_exception_level` a
|
|
68
|
+
valid level. Any other value stays in the payload as data, and a `nil` value is ignored, so an
|
|
69
|
+
untrusted hash passed to a level method such as `info` can never make the logging call raise.
|
|
70
|
+
|
|
71
|
+
To make sure none of its keys are treated as arguments, pass it as the explicit `payload:`. Any other
|
|
72
|
+
keys supplied alongside an explicit `payload:` are merged into it, and win over the explicit
|
|
73
|
+
payload's values of the same name, so that the untrusted data cannot replace a value your code
|
|
74
|
+
supplies. A String key, as `JSON.parse` returns without `symbolize_names`, has the same name as the
|
|
75
|
+
Symbol key it spells:
|
|
76
|
+
|
|
77
|
+
~~~ruby
|
|
78
|
+
# The hash is logged as-is, and none of its keys are interpreted. Its own account_id, if any,
|
|
79
|
+
# cannot replace the one supplied here:
|
|
80
|
+
logger.info("Webhook received", payload: JSON.parse(body, symbolize_names: true), account_id: account.id)
|
|
81
|
+
~~~
|
|
82
|
+
|
|
83
|
+
A hash returned from a logging block, such as
|
|
84
|
+
`logger.debug("Webhook received") { JSON.parse(body, symbolize_names: true) }`, is checked the
|
|
85
|
+
same way, and never replaces a value the log call supplied. A key is only used as an argument when
|
|
86
|
+
its value is valid for it and the log entry does not have that argument yet: an empty `message`
|
|
87
|
+
counts as none, and a backtrace captured at the `backtrace_level` counts as supplied. Its `payload`
|
|
88
|
+
is only used when it is a Hash. Every other key, including an argument the entry already has, is
|
|
89
|
+
added to the payload without replacing the log call's values, and wins over the block's own
|
|
90
|
+
`payload` values of the same name. The caller's own payload Hash is left unchanged.
|
|
91
|
+
|
|
92
|
+
The options of a `measure_*` call are checked the same way: an option with a value that is not valid
|
|
93
|
+
for it is ignored, and its default is used. Keys that are not measure options are not logged at all,
|
|
94
|
+
so pass untrusted data to a measure call as its `payload:` too. A measure call without a block still
|
|
95
|
+
raises unless it is given a valid `:duration`.
|
|
96
|
+
|
|
55
97
|
### Redacting sensitive data
|
|
56
98
|
|
|
57
99
|
Semantic Logger does not automatically redact secrets or personal information; it logs
|
|
@@ -60,37 +102,144 @@ numbers, and similar values in the first place.
|
|
|
60
102
|
|
|
61
103
|
When sensitive values can reach the logs indirectly, redact them. A global `on_log`
|
|
62
104
|
subscriber runs inline, once per log event, before the event is handed to any appender,
|
|
63
|
-
so it can scrub the
|
|
105
|
+
so it can scrub the payload for every destination. The example below matches keys at any depth,
|
|
106
|
+
whether they are Strings, as in parsed JSON and Rails params, or Symbols, in any case and style,
|
|
107
|
+
such as `api_key`, `apiKey`, or `X-Api-Key`. It builds a new payload rather than changing the logged
|
|
108
|
+
one, which can be the application's own Hash. It fails closed: a payload that it cannot redact is
|
|
109
|
+
replaced, never written as it is.
|
|
64
110
|
|
|
65
111
|
~~~ruby
|
|
66
|
-
|
|
112
|
+
module LogRedaction
|
|
113
|
+
# Matched against each key in lower case with "_" between its words, see sensitive_key?.
|
|
114
|
+
SENSITIVE_KEY = Regexp.union(
|
|
115
|
+
# Anywhere in the key, like the default filter_parameters of Rails.
|
|
116
|
+
/passw|passphrase|secret|token|_key|apikey|credential|authorization|cookie|crypt|certificate|email/,
|
|
117
|
+
# Only as a whole word, so that "otp" does not match "footprint", nor "ssn" "business_name".
|
|
118
|
+
/(?<![a-z])(?:auth|pwd|otp|ssn|salt|cvv|cvc)(?![a-z])/
|
|
119
|
+
)
|
|
120
|
+
REDACTED = "[REDACTED]".freeze
|
|
121
|
+
MAX_DEPTH = 32
|
|
67
122
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
123
|
+
# Returns a copy of the value with the value of every sensitive key replaced, at any depth.
|
|
124
|
+
#
|
|
125
|
+
# copies maps each structure already walked to its copy, so that a structure held more than once
|
|
126
|
+
# is walked only once, and one that holds itself is cut off.
|
|
127
|
+
def self.redact(value, copies = {}.compare_by_identity, depth = 0)
|
|
128
|
+
return copies[value] || "[REDACTED: circular]" if copies.key?(value)
|
|
129
|
+
|
|
130
|
+
members = members(value)
|
|
131
|
+
return value if members.nil?
|
|
132
|
+
return "[REDACTED: nested too deeply]" if depth >= MAX_DEPTH
|
|
133
|
+
|
|
134
|
+
copies[value] = nil # Until its copy is complete.
|
|
135
|
+
copies[value] =
|
|
136
|
+
if members.is_a?(Hash)
|
|
137
|
+
members.each_with_object({}) do |(key, val), copy|
|
|
138
|
+
copy[key] = sensitive_key?(key) ? REDACTED : redact(val, copies, depth + 1)
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
members.map { |val| redact(val, copies, depth + 1) }
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.sensitive_key?(key)
|
|
146
|
+
# As valid UTF-8, since a key with invalid bytes, as from a query string, makes a match raise.
|
|
147
|
+
name = key.to_s.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
148
|
+
# "apiKey", "API_KEY", and "X-Api-Key" become "api_key", "api_key", and "x_api_key".
|
|
149
|
+
name = name.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.gsub(/[^a-z\d]+/, "_")
|
|
150
|
+
SENSITIVE_KEY.match?(name)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Returns the members of a Hash, Array, Struct, Data, Set, OpenStruct, or
|
|
154
|
+
# ActionController::Parameters, as a Hash or an Array. Returns nil for any other value.
|
|
155
|
+
def self.members(value)
|
|
156
|
+
case value
|
|
157
|
+
when Hash, Array then value
|
|
158
|
+
when Struct, Data then value.to_h
|
|
159
|
+
when Set then value.to_a
|
|
160
|
+
else
|
|
161
|
+
if defined?(OpenStruct) && value.is_a?(OpenStruct)
|
|
162
|
+
value.to_h
|
|
163
|
+
elsif value.respond_to?(:to_unsafe_h) # ActionController::Parameters
|
|
164
|
+
value.to_unsafe_h
|
|
165
|
+
end
|
|
166
|
+
end
|
|
71
167
|
end
|
|
72
168
|
end
|
|
169
|
+
|
|
170
|
+
# Register it before any other on_log subscriber.
|
|
171
|
+
SemanticLogger.on_log do |log|
|
|
172
|
+
log.payload = LogRedaction.redact(log.payload) unless log.payload.nil?
|
|
173
|
+
rescue Exception => e
|
|
174
|
+
# Fail closed. Any Exception, since Semantic Logger rescues whatever an on_log subscriber raises
|
|
175
|
+
# and goes on to write the entry.
|
|
176
|
+
log.payload = {redaction_failed: e.class.name}
|
|
177
|
+
end
|
|
73
178
|
~~~
|
|
74
179
|
|
|
75
|
-
|
|
76
|
-
|
|
180
|
+
`SENSITIVE_KEY` is a starting point: adapt it to the keys that your application logs. Like the
|
|
181
|
+
default `filter_parameters` of Rails, it also matches some keys that are not secret, such as
|
|
182
|
+
`max_tokens` or `cache_key`.
|
|
183
|
+
|
|
184
|
+
The example looks inside Hashes and Arrays, and inside the Struct, Data, Set, and OpenStruct values
|
|
185
|
+
that the JSON formatters write as objects and arrays, which it replaces with Hashes and Arrays. It
|
|
186
|
+
reads `ActionController::Parameters` with `to_unsafe_h`. Any other object is left as it is, and is
|
|
187
|
+
written the way the formatter writes it, which for some objects includes all of their attributes:
|
|
188
|
+
for example a JSON formatter writes an Active Record model with `as_json`. Log a Hash of the values
|
|
189
|
+
you need instead.
|
|
190
|
+
|
|
191
|
+
Register it before any other `on_log` subscriber that reads the payload. Subscribers run in the
|
|
192
|
+
order they were registered, so one registered before it, such as the [Rollbar](appenders.html#rollbar)
|
|
193
|
+
integration that sends `log.to_h`, receives the payload before it is redacted.
|
|
194
|
+
|
|
195
|
+
It only redacts the payload. The message, the tags and named tags, the message of the exception and
|
|
196
|
+
those of its causes, the context that `on_log` subscribers add, and metric dimensions are written
|
|
197
|
+
as they are, so keep secrets out of them. That includes a value other than a Hash passed after the
|
|
198
|
+
message, such as `logger.info("Login", user)`, which is added to the message: pass it as
|
|
199
|
+
`payload: user` instead.
|
|
200
|
+
|
|
201
|
+
It copies the payload of every entry that is logged, even when there is nothing to redact, on the
|
|
202
|
+
thread that logs it, so its cost grows with the size of the payloads.
|
|
203
|
+
|
|
204
|
+
A `filter` on a logger instance can redact the same way for that logger's entries; see
|
|
205
|
+
[Filtering](config.html#filtering). Do not redact in an appender's `filter`: every appender writes
|
|
206
|
+
the same log entry, so that only protects the appenders added after it, while the ones added before
|
|
207
|
+
it have already written the secret.
|
|
77
208
|
|
|
78
209
|
Rails applications should use the sister gem
|
|
79
210
|
[rails_semantic_logger](https://github.com/reidmorrison/rails_semantic_logger), which
|
|
80
211
|
honours `config.filter_parameters` so that parameters Rails already considers sensitive
|
|
81
|
-
are filtered out of the logs.
|
|
212
|
+
are filtered out of the logs. To match the payload's keys against the same list, `sensitive_key?`
|
|
213
|
+
can return `!filter.filter_param(name, nil).nil?`, where `filter` is
|
|
214
|
+
`ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)`, created once the
|
|
215
|
+
application has initialized, since one created from an incomplete list filters less. Proc filters
|
|
216
|
+
in the list are not applied this way. Do not call its `#filter` in place of `redact`: it only looks
|
|
217
|
+
inside Hashes and Arrays, raises for a key with invalid UTF-8, and has no protection against a Hash
|
|
218
|
+
that holds itself or is held more than once.
|
|
82
219
|
|
|
83
220
|
### Transport encryption
|
|
84
221
|
|
|
85
222
|
Appenders that send logs over the network support TLS. The appenders built on the
|
|
86
|
-
`:http` appender (`:http`, `:splunk_http`, and `:
|
|
223
|
+
`:http` appender (`:http`, `:splunk_http`, `:elasticsearch_http`, and `:loki`) default to
|
|
87
224
|
verifying the server certificate (`OpenSSL::SSL::VERIFY_PEER`) when an `https` URL is
|
|
88
|
-
used.
|
|
225
|
+
used. The `:tcp` appender, and the `:syslog` appender over TCP, verify both the certificate
|
|
226
|
+
and the host name when `ssl:` is enabled.
|
|
227
|
+
|
|
228
|
+
The deprecated `:splunk` appender is the exception: its `splunk-sdk-ruby` gem never verifies
|
|
229
|
+
the certificate. Use `:splunk_http` instead.
|
|
89
230
|
|
|
90
|
-
Do not disable certificate verification
|
|
231
|
+
Do not disable certificate verification. Settings such as
|
|
91
232
|
`ssl: {verify_mode: OpenSSL::SSL::VERIFY_NONE}` expose the connection to
|
|
92
|
-
man-in-the-middle attacks
|
|
93
|
-
|
|
233
|
+
man-in-the-middle attacks. To connect to a server with a self-signed certificate, or one issued
|
|
234
|
+
by a private certificate authority, trust that certificate instead, for example with
|
|
235
|
+
`ssl: {ca_file: "/path/to/ca.pem"}`.
|
|
236
|
+
|
|
237
|
+
Credentials can be supplied in the url of the HTTP based appenders, for example
|
|
238
|
+
`https://user:password@host`, or as a query parameter. They are removed from the url
|
|
239
|
+
written to the internal logger when the server rejects a request, and from the error raised
|
|
240
|
+
for an invalid url. `#inspect` on any appender, for example when `SemanticLogger.appenders` is
|
|
241
|
+
printed in a console, shows only what that appender says is safe, such as its url without
|
|
242
|
+
credentials, and none of its passwords, tokens, keys, or client options.
|
|
94
243
|
|
|
95
244
|
### Log file permissions
|
|
96
245
|
|
|
@@ -104,9 +253,27 @@ an existing log file:
|
|
|
104
253
|
SemanticLogger.add_appender(file_name: "production.log", permissions: 0o640)
|
|
105
254
|
~~~
|
|
106
255
|
|
|
256
|
+
With `permissions`, the log file is opened without following a symbolic link at its name, so a
|
|
257
|
+
link planted in the log directory can neither create nor open the file it points at. The file must
|
|
258
|
+
be a regular file owned by the process, with a single hard link, that the file name still names
|
|
259
|
+
once it is open. Otherwise, or when the file cannot be opened, nothing is written to it:
|
|
260
|
+
`add_appender` raises `IOError` giving the reason, with the error from opening the file, if any, as
|
|
261
|
+
its `cause`. When a file only fails the check once it is reopened later, on a rotation or after a
|
|
262
|
+
fork, the appender reports the reason once for each file, drops the log messages instead of
|
|
263
|
+
raising, and tries the file again every few seconds and at the next scheduled reopen, so that
|
|
264
|
+
logging resumes once the file is fixed. A symbolic link, hard link, or file planted in a shared log
|
|
265
|
+
directory, for example under the name of tomorrow's log file, can therefore neither loosen the
|
|
266
|
+
permissions of another file nor receive the log messages. A FIFO planted there fails to open
|
|
267
|
+
instead of blocking the process.
|
|
268
|
+
|
|
269
|
+
Without `permissions`, the file appender opens the log file the way Ruby's own `Logger` does,
|
|
270
|
+
following symbolic links, so that a log file can for example be linked to `/dev/stdout`. Keep the
|
|
271
|
+
log directory writable only by the application.
|
|
272
|
+
|
|
107
273
|
### Dependencies and supply chain
|
|
108
274
|
|
|
109
|
-
Semantic Logger has
|
|
275
|
+
Semantic Logger has two runtime dependencies, `concurrent-ruby` and `logger` (part of
|
|
276
|
+
Ruby's standard library until Ruby 4.0 moved it to the bundled gems). Appenders for
|
|
110
277
|
third-party services (Kafka, MongoDB, Sentry, etc.) keep their backing gem optional: it
|
|
111
278
|
is required lazily inside the appender only when that appender is used, and is never
|
|
112
279
|
added to the gemspec. This keeps the dependency surface of an application that only uses
|
data/docs/testing.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
|
+
title: Testing
|
|
4
|
+
description: >-
|
|
5
|
+
Assert that your code logged the expected message, level, payload or metric,
|
|
6
|
+
using the capture helpers for Minitest, RSpec, and other test frameworks.
|
|
3
7
|
---
|
|
4
8
|
|
|
5
|
-
## Testing
|
|
6
|
-
{:.no_toc}
|
|
7
9
|
|
|
8
10
|
**Contents**
|
|
9
11
|
|
|
@@ -106,7 +108,7 @@ test. The available checks are:
|
|
|
106
108
|
| `time` | When the entry was created. |
|
|
107
109
|
| `duration` | Duration of a measure call, in milliseconds. |
|
|
108
110
|
| `backtrace` | The captured backtrace, if any. |
|
|
109
|
-
| `thread_name` | Name of the thread that logged the entry. |
|
|
111
|
+
| `thread_name` | Name of the thread that logged the entry, or its object id when the thread has no name. |
|
|
110
112
|
|
|
111
113
|
### Match part of a message or payload
|
|
112
114
|
|
data/docs/upgrading.md
CHANGED
|
@@ -1,22 +1,290 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
|
+
title: Upgrading
|
|
4
|
+
description: >-
|
|
5
|
+
Version-by-version upgrade notes for Semantic Logger, from v4.0 through the
|
|
6
|
+
v5.2 security fixes, remote syslog facility fix, and deprecation of the Splunk SDK appender.
|
|
3
7
|
---
|
|
4
8
|
|
|
5
|
-
## Upgrading
|
|
6
|
-
{:.no_toc}
|
|
7
9
|
|
|
8
10
|
**Contents**
|
|
9
11
|
|
|
10
12
|
* TOC
|
|
11
13
|
{:toc}
|
|
12
14
|
|
|
15
|
+
### Upgrading to Semantic Logger v5.2
|
|
16
|
+
|
|
17
|
+
v5.2 sends the configured facility to remote syslog, deprecates the `:splunk` appender, stops
|
|
18
|
+
replacing Ruby's `Thread#name`, and fixes several security issues. None of the security fixes change
|
|
19
|
+
the public API, but a few change the output, which matters if you parse the logs or alert on them,
|
|
20
|
+
and the file appender's `permissions:` option can now make `add_appender` raise for a log file that
|
|
21
|
+
worked in v5.1.
|
|
22
|
+
|
|
23
|
+
#### Set the Sentry user and fingerprint with named tags
|
|
24
|
+
|
|
25
|
+
The `:sentry_ruby` appender takes the Sentry user (`user_id`, `username`, `user_email`, `ip_address`,
|
|
26
|
+
and `user`) and `fingerprint` from named tags or the payload. Taking them from the payload is
|
|
27
|
+
deprecated and will be **removed in v6**, since the payload often holds untrusted data, such as a
|
|
28
|
+
request body, which could then set the user an error is reported for. It emits a deprecation warning
|
|
29
|
+
the first time it happens. Until then, the named tags win over the payload, which no longer replaces
|
|
30
|
+
a user field they set, and only a String, Symbol, or Integer is taken from the payload for a user
|
|
31
|
+
field. Set them with named tags instead, which can now also hold the `fingerprint`:
|
|
32
|
+
|
|
33
|
+
~~~ruby
|
|
34
|
+
# Before:
|
|
35
|
+
logger.error("Payment failed", user_id: user.id, fingerprint: ["payment-failed"])
|
|
36
|
+
|
|
37
|
+
# After:
|
|
38
|
+
SemanticLogger.tagged(user_id: user.id, fingerprint: ["payment-failed"]) do
|
|
39
|
+
logger.error("Payment failed")
|
|
40
|
+
end
|
|
41
|
+
~~~
|
|
42
|
+
|
|
43
|
+
#### Replace the `:splunk` appender with `:splunk_http`
|
|
44
|
+
|
|
45
|
+
The `:splunk` appender depends on the `splunk-sdk-ruby` gem, which was last released in 2015 and
|
|
46
|
+
does not verify the server's TLS certificate. It now emits a deprecation warning and will be
|
|
47
|
+
**removed in v6**. Send logs to the Splunk HTTP Event Collector instead:
|
|
48
|
+
|
|
49
|
+
~~~ruby
|
|
50
|
+
# Before:
|
|
51
|
+
SemanticLogger.add_appender(appender: :splunk, index: "main")
|
|
52
|
+
|
|
53
|
+
# After:
|
|
54
|
+
SemanticLogger.add_appender(
|
|
55
|
+
appender: :splunk_http,
|
|
56
|
+
url: "https://splunk.example.com:8088/services/collector/event",
|
|
57
|
+
token: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
58
|
+
index: "main"
|
|
59
|
+
)
|
|
60
|
+
~~~
|
|
61
|
+
|
|
62
|
+
#### Remote syslog sends the configured facility
|
|
63
|
+
|
|
64
|
+
When logging to a remote syslog server over TCP or UDP, earlier versions sent the wrong facility.
|
|
65
|
+
The `facility:` option is documented as one of Ruby's `::Syslog::LOG_*` facility constants, but the
|
|
66
|
+
value was sent as if it were already the syslog protocol's facility number. Those constants are the
|
|
67
|
+
facility number shifted left by 3 bits, so the default `::Syslog::LOG_USER` (8) went out as facility
|
|
68
|
+
8, which is `uucp`, instead of facility 1, `user`. For example a `:info` message, at the default
|
|
69
|
+
`LOG_NOTICE` severity, was sent as `<69>` rather than `<13>`. Logging to a local syslog daemon was
|
|
70
|
+
not affected.
|
|
71
|
+
|
|
72
|
+
v5.2 sends the facility the constant names. If your syslog server filters or routes on facility,
|
|
73
|
+
for example an rsyslog rule for `uucp.*`, change it to the facility you configured: `user` unless
|
|
74
|
+
you set one.
|
|
75
|
+
|
|
76
|
+
The `facility:` option now only accepts the `::Syslog::LOG_*` facility constants, and raises
|
|
77
|
+
`ArgumentError` for other values when the appender is created. Previously a constant other than
|
|
78
|
+
`::Syslog::LOG_KERN`, `::Syslog::LOG_USER`, or `::Syslog::LOG_MAIL` raised `ArgumentError` while
|
|
79
|
+
logging. If you passed a bare facility number to get around that, switch to the constant. Check
|
|
80
|
+
this even if nothing raises: a bare 0, 8, or 16 is now read as `::Syslog::LOG_KERN`,
|
|
81
|
+
`::Syslog::LOG_USER`, or `::Syslog::LOG_MAIL`, so `16`, which used to send `local0`, now sends
|
|
82
|
+
`mail`:
|
|
83
|
+
|
|
84
|
+
~~~ruby
|
|
85
|
+
# Before (16 was sent as facility local0, and is now read as ::Syslog::LOG_MAIL):
|
|
86
|
+
SemanticLogger.add_appender(appender: :syslog, url: "udp://myloghost:514", facility: 16)
|
|
87
|
+
|
|
88
|
+
# After:
|
|
89
|
+
SemanticLogger.add_appender(appender: :syslog, url: "udp://myloghost:514", facility: ::Syslog::LOG_LOCAL0)
|
|
90
|
+
~~~
|
|
91
|
+
|
|
92
|
+
This applies to the `:syslog` and `:syslog_cee` formatters. An application that logs only to a remote
|
|
93
|
+
server, and so does not need the `syslog` gem, can use the same constants from
|
|
94
|
+
`SemanticLogger::Formatters::Syslog`, for example `SemanticLogger::Formatters::Syslog::LOG_LOCAL0`.
|
|
95
|
+
|
|
96
|
+
#### The file appender's `permissions:` refuses a log file it cannot secure
|
|
97
|
+
|
|
98
|
+
With `permissions:`, the file appender only writes to a regular file owned by the process, with a
|
|
99
|
+
single hard link, that the file name names directly rather than through a symbolic link.
|
|
100
|
+
`SemanticLogger.add_appender` now raises `IOError`, giving the reason, for any other log file,
|
|
101
|
+
which v5.1 wrote to, changing the permissions of whatever a link pointed at. So a configuration
|
|
102
|
+
that worked in v5.1 can now fail at boot, for example `permissions:` on a log file that is a
|
|
103
|
+
symbolic link to `/dev/stdout`, or on a log file that another user created. With `permissions:`,
|
|
104
|
+
a log file that cannot be opened also raises `IOError`, instead of the operating system's error,
|
|
105
|
+
such as `Errno::ENOENT`, which is its `cause`. A log file that fails the check, or to open, when it
|
|
106
|
+
is reopened later, on a rotation or after a fork, is reported to the internal logger and not
|
|
107
|
+
written to until it is fixed, instead of raising from the logging thread.
|
|
108
|
+
|
|
109
|
+
If you set `permissions:` on such a log file, either fix the file or remove `permissions:`. To log
|
|
110
|
+
to standard output, use `io: $stdout`:
|
|
111
|
+
|
|
112
|
+
~~~ruby
|
|
113
|
+
# Before, with log/production.log a symbolic link to /dev/stdout:
|
|
114
|
+
SemanticLogger.add_appender(file_name: "log/production.log", permissions: 0o640)
|
|
115
|
+
|
|
116
|
+
# After:
|
|
117
|
+
SemanticLogger.add_appender(io: $stdout)
|
|
118
|
+
~~~
|
|
119
|
+
|
|
120
|
+
#### Ruby's own `Thread#name` is no longer replaced
|
|
121
|
+
|
|
122
|
+
Earlier versions replaced `Thread#name` and `Thread#name=` with Semantic Logger's own versions,
|
|
123
|
+
which were written before Ruby had them and kept the name away from Ruby itself. v5.2 leaves Ruby's
|
|
124
|
+
own in place, so the name now also reaches the operating system, profilers, and `Thread#inspect`.
|
|
125
|
+
On MRI, log entries do not change: a thread that has no name is still logged with its object id.
|
|
126
|
+
|
|
127
|
+
Code anywhere in the application that calls `Thread#name` or `Thread#name=` now gets Ruby's
|
|
128
|
+
behaviour:
|
|
129
|
+
|
|
130
|
+
* `Thread#name` returns `nil` for a thread that has no name, where it returned the thread's object
|
|
131
|
+
id as a String (on JRuby, its JVM thread name). The main thread has no name on MRI; JRuby names it
|
|
132
|
+
`main`.
|
|
133
|
+
* `Thread#name=` is Ruby's built-in implementation again, with Ruby's standard behaviour, instead of
|
|
134
|
+
Semantic Logger's version, which accepted any value and converted it with `to_s`. Ruby's version
|
|
135
|
+
raises `TypeError` for a value that is not a String or `nil`, such as a Symbol or an Integer. It
|
|
136
|
+
raises `ArgumentError` for a String that contains a NUL byte (`"\0"`), or whose encoding is not
|
|
137
|
+
ASCII-compatible, such as UTF-16. Setting `nil` leaves the thread with no name, where it set the
|
|
138
|
+
name to `""`.
|
|
139
|
+
|
|
140
|
+
~~~ruby
|
|
141
|
+
# Before:
|
|
142
|
+
Thread.current.name = worker_number
|
|
143
|
+
Thread.current.name.start_with?("worker")
|
|
144
|
+
|
|
145
|
+
# After:
|
|
146
|
+
Thread.current.name = "worker-#{worker_number}"
|
|
147
|
+
Thread.current.name&.start_with?("worker")
|
|
148
|
+
~~~
|
|
149
|
+
|
|
150
|
+
Tests that expect `thread_name: Thread.current.name` from the
|
|
151
|
+
[test helpers](testing.html) no longer work on a thread that has no name, such as the main thread
|
|
152
|
+
on MRI: `Thread.current.name` is now `nil`, while the log entry still holds the thread's object id.
|
|
153
|
+
The RSpec matchers `be_a_semantic_logger_event` and `log_semantic_logger_event` then fail. Worse,
|
|
154
|
+
`assert_semantic_logger_event` in Minitest takes `nil` to mean "do not check", so the test still
|
|
155
|
+
passes but no longer checks the thread name. Expect the object id when the thread has no name:
|
|
156
|
+
|
|
157
|
+
~~~ruby
|
|
158
|
+
# Before:
|
|
159
|
+
assert_semantic_logger_event(event, thread_name: Thread.current.name)
|
|
160
|
+
|
|
161
|
+
# After:
|
|
162
|
+
assert_semantic_logger_event(event, thread_name: Thread.current.name || Thread.current.object_id.to_s)
|
|
163
|
+
~~~
|
|
164
|
+
|
|
165
|
+
Semantic Logger's own threads are now named after their appender, such as `SL File`, with
|
|
166
|
+
`SL Appenders` for the main one, instead of `SemanticLogger::Appender::File` and
|
|
167
|
+
`SemanticLogger::Appenders`. Linux keeps only the first 15 bytes of a thread name, so the longer
|
|
168
|
+
names all looked the same in tools that list threads, such as `top -H`. The new names also appear
|
|
169
|
+
in the thread column of the messages Semantic Logger logs about its own problems, such as an
|
|
170
|
+
appender that cannot write.
|
|
171
|
+
|
|
172
|
+
On JRuby:
|
|
173
|
+
|
|
174
|
+
* A Ruby thread that has no name is logged with its object id, instead of its JVM thread name, such
|
|
175
|
+
as `Ruby-0-Thread-1: app/worker.rb:12`. Name the thread to give it a readable name in the logs. A
|
|
176
|
+
thread created by Java code, such as a servlet container's request thread, is still logged with
|
|
177
|
+
its JVM thread name, which now also stays current when Java code renames the thread.
|
|
178
|
+
* Naming a Ruby thread sets its JVM thread name in JRuby's own format, such as
|
|
179
|
+
`Ruby-0-Thread-2@My Worker: app/worker.rb:12`, instead of exactly `My Worker`. Update any JVM
|
|
180
|
+
monitoring that matches thread names exactly.
|
|
181
|
+
|
|
182
|
+
#### Output changes
|
|
183
|
+
|
|
184
|
+
* `:logfmt`: spaces, control characters, invisible format characters (such as a zero width space),
|
|
185
|
+
`=`, `"`, and Unicode line separators in keys are replaced with `_`, and invalid UTF-8 bytes are
|
|
186
|
+
dropped. A tag such as `second breakfast` is now emitted as `second_breakfast=true`. A tag,
|
|
187
|
+
named tag, or payload key named like one of the entry's own fields, which are the time key
|
|
188
|
+
(`timestamp` by default), `level`, `name`, `message`, `duration`, `duration_ms`, `tag`, and the
|
|
189
|
+
exception fields, is emitted with its source as a prefix, such as `payload.name="shrek"` or
|
|
190
|
+
`tags.message=true`, instead of replacing the field, and so is a key that starts with `tags.`,
|
|
191
|
+
`named_tags.`, or `payload.`. A logged exception's class and message are emitted as
|
|
192
|
+
`exception_name` and `exception_message` (followed by `stack_trace` and `cause` as before),
|
|
193
|
+
instead of replacing the entry's `name` and `message`, and the detail that
|
|
194
|
+
`log_exception_details: true` adds as `exception_details`. Other keys that are the same once
|
|
195
|
+
encoded, such as `:user` and `"user"`, are emitted once, with the value from the later source:
|
|
196
|
+
the payload over named tags, and named tags over tags. A payload that is not a Hash is emitted
|
|
197
|
+
as a single `payload` field. A nested value (a Hash, Array, `Struct`,
|
|
198
|
+
`Data`, `Set`, or any other value whose JSON is an object or an array, such as the exception
|
|
199
|
+
backtrace) is emitted as quoted JSON, `user="{\"id\":7}"`, instead of its `#inspect` text.
|
|
200
|
+
* `:syslog`: an entry with an exception stays on one line; the newlines between the exception and
|
|
201
|
+
its backtrace are escaped as `\n`.
|
|
202
|
+
* `:default`, `:color`, `:pattern`, and `:syslog`: with `escape_control_chars`, the logger name,
|
|
203
|
+
thread name, file name, payload, the messages of the exceptions that caused the logged exception,
|
|
204
|
+
and the backtrace lines are escaped too. Invalid UTF-8 bytes are dropped instead of the entry failing to
|
|
205
|
+
format. With escaping on, `:color` renders the payload plain and on one line, since Amazing
|
|
206
|
+
Print's colors and line breaks cannot be told apart from control characters in the data.
|
|
207
|
+
* `:one_line`: line breaks are removed from every field (name, thread name, file name, tags, named
|
|
208
|
+
tags, payload, message, and exception), carriage returns as well as newlines, and
|
|
209
|
+
`escape_control_chars` is honoured.
|
|
210
|
+
* JSON based formatters: `Infinity` and `NaN` payload values are serialized as strings, and
|
|
211
|
+
structures nested deeper than 32 levels are replaced with a placeholder such as
|
|
212
|
+
`#<Hash nested too deeply>`, instead of the whole entry being dropped. A `Struct`, `Data`, or
|
|
213
|
+
`OpenStruct` value is written as an object of its members, and a `Set` as an array, as Rails
|
|
214
|
+
already does, instead of its `#inspect` text. An exception in the payload is written as its
|
|
215
|
+
message, and any other object whose JSON fails because of invalid UTF-8 as its `#to_s`, with
|
|
216
|
+
the invalid bytes dropped. The `:elasticsearch` and `:opensearch` appenders write their bulk lines the same way.
|
|
217
|
+
* JSON based formatters and `:one_line`: invalid UTF-8 bytes in the message are dropped, and a
|
|
218
|
+
message in another encoding is converted to UTF-8, instead of the entry failing to format.
|
|
219
|
+
* `:loki`: a nested Hash, Array, `Struct`, or `Set` in the payload is written once as JSON, keeping
|
|
220
|
+
the JSON types of its values, so `{number: 1}` is written as `{"number":1}` rather than
|
|
221
|
+
`{"number":"1"}`, and `[1, 2, 3]` as `[1,2,3]`. A payload that is not a Hash is written as a
|
|
222
|
+
single `payload` field.
|
|
223
|
+
* `:open_telemetry`: a `Struct`, `Set`, or other structure in the payload, or in an Array in it, is
|
|
224
|
+
written as JSON instead of its `#inspect`. A payload that is not a Hash is submitted as a single
|
|
225
|
+
`payload` attribute.
|
|
226
|
+
* Hashes logged as the payload, for example `logger.info("Webhook", params)`: a key that is also
|
|
227
|
+
a log argument (`duration`, `metric_amount`, `backtrace`, `log_exception`, ...) is only used as
|
|
228
|
+
that argument when its value is valid for it. Otherwise it stays in the payload, where it was
|
|
229
|
+
previously consumed or made the call raise. A `nil` value is ignored. When an explicit
|
|
230
|
+
`payload:` is also given, the other keys win over its values of the same name, a String key such
|
|
231
|
+
as `"account_id"` having the same name as `:account_id`.
|
|
232
|
+
* Hashes returned from a logging block, for example `logger.info("Webhook") { {user: "bob"} }`, are
|
|
233
|
+
checked the same way. A log argument from the block, such as `message`, `duration`, `exception`,
|
|
234
|
+
or `backtrace`, is only used when the log entry does not have it yet, and is otherwise kept in the
|
|
235
|
+
payload. A backtrace captured because the level is at or above `backtrace_level` counts, so the
|
|
236
|
+
block's `backtrace` no longer replaces it. The block's keys no longer replace values the log call
|
|
237
|
+
supplied under the same name, whether as a String or a Symbol, and win over the values of the
|
|
238
|
+
block's own `payload` with the same name.
|
|
239
|
+
* The HTTP appender and the appenders built on it (`:elasticsearch_http`, `:splunk_http`, `:loki`,
|
|
240
|
+
`:signalfx`): the url in the error logged for a rejected request shows only the scheme, host,
|
|
241
|
+
and port, and `#inspect` shows only the object address and that url.
|
|
242
|
+
* The `:statsd` metrics appender sends a counter as a single `metric:amount|c` packet.
|
|
243
|
+
|
|
244
|
+
#### Other behaviour changes
|
|
245
|
+
|
|
246
|
+
* An async or batching appender discards a log message that it cannot format, instead of
|
|
247
|
+
retrying it. Discarded messages are reported to the internal logger and counted in the new
|
|
248
|
+
`failed` key of the appender's stats. Every error raised by a built-in appender's formatter
|
|
249
|
+
counts, as does a message too large for a UDP datagram, a MongoDB document that BSON cannot
|
|
250
|
+
serialize, and a message GELF cannot send. A custom appender can format through
|
|
251
|
+
`format_log(log)` (or `format_batch(logs)`) instead of `formatter.call` to get the same
|
|
252
|
+
treatment, or override `#message_error?` for errors its own client raises.
|
|
253
|
+
* The text formatters write a payload nested more than 32 levels deep as the JSON formatters
|
|
254
|
+
write it, cut off with their placeholder, such as `"#<Hash nested too deeply>"`.
|
|
255
|
+
* A log message that overflows the stack while it is written is discarded, and the worker
|
|
256
|
+
thread carries on in a new thread, instead of the worker stopping for good.
|
|
257
|
+
* `#inspect` on every appender shows only its class, object address, and what it says is safe to
|
|
258
|
+
show, such as `url="https://es.example.com:9200"`, `queue_name="logs"`, or `group="/my/app"`,
|
|
259
|
+
instead of its instance variables. A custom appender can show more by overriding
|
|
260
|
+
`#inspect_details`. An appender added with `async: true` or `batch: true` shows as
|
|
261
|
+
`#<SemanticLogger::Appender::Async:0x... batch=true appender=#<...>>`.
|
|
262
|
+
* `:cloudwatch_logs` sends the buffered messages in as many requests as the PutLogEvents limits
|
|
263
|
+
need, oldest first, cuts short a message larger than 1 MB, and reports to the internal logger a
|
|
264
|
+
request that fails, and the messages that AWS rejects as too new, too old, or expired. It formats each message when it is logged, writes invalid UTF-8 as valid
|
|
265
|
+
UTF-8, and discards a message that its formatter cannot write, or writes as something other than
|
|
266
|
+
a String with content, instead of failing the whole request.
|
|
267
|
+
* `:sentry_ruby` leaves the log entry's payload and named tags unchanged, so every appender after
|
|
268
|
+
it, and your own Hash, keep the keys it reads. A user field, or `user` Hash, in the payload no
|
|
269
|
+
longer replaces one from the named tags. A payload user field that is not a String, Symbol, or
|
|
270
|
+
Integer, a `fingerprint` that is not an Array of Strings, Symbols, or numbers, and a payload that
|
|
271
|
+
is not a Hash, are sent as data instead.
|
|
272
|
+
|
|
273
|
+
#### Stricter `logger_measure_method` arguments
|
|
274
|
+
|
|
275
|
+
`logger_measure_method` now raises `ArgumentError` for a method name that is not a valid Ruby
|
|
276
|
+
method name, a `min_duration` that is not a finite number, and an invalid `log_exception` or
|
|
277
|
+
level. Numeric Strings, such as `min_duration: "100"`, are still accepted.
|
|
278
|
+
|
|
13
279
|
### Upgrading to Semantic Logger v5.1
|
|
14
280
|
|
|
15
|
-
v5.1 deprecates several legacy appenders and one appender option
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
281
|
+
v5.1 deprecates several legacy appenders and one appender option, and changes when the file appender
|
|
282
|
+
opens its log file.
|
|
283
|
+
|
|
284
|
+
The deprecated appenders and option all still work in v5.1, but each now emits a Ruby deprecation
|
|
285
|
+
warning and will be **removed in v6**. The warnings use `category: :deprecated`, so they are silent
|
|
286
|
+
by default and appear when deprecation warnings are enabled (for example `ruby -W:deprecated`, or
|
|
287
|
+
Rails development mode). Switch away now to be ready for v6.
|
|
20
288
|
|
|
21
289
|
#### Replace the `:sentry` appender with `:sentry_ruby`
|
|
22
290
|
|
|
@@ -75,6 +343,26 @@ SemanticLogger.add_appender(appender: :elasticsearch, url: "http://localhost:920
|
|
|
75
343
|
SemanticLogger.add_appender(appender: :elasticsearch, url: "http://localhost:9200")
|
|
76
344
|
~~~
|
|
77
345
|
|
|
346
|
+
#### The file appender opens its log file when it is created
|
|
347
|
+
|
|
348
|
+
The file appender now opens its log file at creation rather than on the first write, matching the
|
|
349
|
+
other appenders. A bad path or insufficient permissions therefore raises immediately from
|
|
350
|
+
`SemanticLogger.add_appender`, instead of surfacing later on the appender thread while log messages
|
|
351
|
+
are lost. This restores the pre-4.9 behavior.
|
|
352
|
+
|
|
353
|
+
As a result the log file is created as soon as the appender is added, even if nothing has been
|
|
354
|
+
logged yet, the same as the standard Ruby and Rails loggers. If you add appenders inside a `rescue`
|
|
355
|
+
or a conditional that assumed creation could never fail, review it: the failure now happens earlier
|
|
356
|
+
and in the calling thread.
|
|
357
|
+
|
|
358
|
+
#### Rails: log file failures are caught at boot
|
|
359
|
+
|
|
360
|
+
If you use [rails_semantic_logger](rails.html), the companion gem's v5.1 release pairs with the
|
|
361
|
+
eager open above: a log file that cannot be opened is now caught during boot and the app falls back
|
|
362
|
+
to standard error at the `:warn` level, rather than booting against a silently broken appender. v5.1
|
|
363
|
+
also drops support for Sidekiq v4, v5, and v6. See
|
|
364
|
+
[Upgrading to v5.1](rails.html#upgrading-to-v51) for details.
|
|
365
|
+
|
|
78
366
|
### Upgrading to Semantic Logger v5.0
|
|
79
367
|
|
|
80
368
|
#### Minimum Ruby version is now 3.2
|
|
@@ -21,13 +21,14 @@ module SemanticLogger
|
|
|
21
21
|
# Methods forwarded to the queue processor that owns the thread and queue.
|
|
22
22
|
# Tuning options (max_queue_size, batch_size, non_blocking, ...) are set once at
|
|
23
23
|
# construction via SemanticLogger.add_appender, so only the values that callers actually
|
|
24
|
-
# read back are re-exposed here. lag_*
|
|
24
|
+
# read back are re-exposed here. lag_* and non_blocking= back the public
|
|
25
|
+
# SemanticLogger.lag_* and SemanticLogger.non_blocking= API on the main Processor.
|
|
25
26
|
def_delegators :@processor,
|
|
26
27
|
:log, :flush, :close, :thread, :active?, :queue, :max_queue_size,
|
|
27
|
-
:capped?, :non_blocking?, :batch?,
|
|
28
|
+
:capped?, :non_blocking?, :non_blocking=, :batch?,
|
|
28
29
|
:lag_check_interval, :lag_check_interval=,
|
|
29
30
|
:lag_threshold_s, :lag_threshold_s=,
|
|
30
|
-
:processed_count, :dropped_count
|
|
31
|
+
:processed_count, :dropped_count, :failed_count
|
|
31
32
|
|
|
32
33
|
# Appender proxy to allow an existing appender to run asynchronously in a separate thread.
|
|
33
34
|
#
|
|
@@ -73,7 +74,8 @@ module SemanticLogger
|
|
|
73
74
|
#
|
|
74
75
|
# dropped_message_report_seconds: [Integer]
|
|
75
76
|
# When non_blocking is enabled, log the count of dropped messages to the internal logger
|
|
76
|
-
# at most once every this number of seconds.
|
|
77
|
+
# at most once every this number of seconds. Also limits how often messages discarded
|
|
78
|
+
# because their content could not be written are reported, after the first.
|
|
77
79
|
# Default: 30
|
|
78
80
|
#
|
|
79
81
|
# async_max_retries: [Integer]
|
|
@@ -87,6 +89,14 @@ module SemanticLogger
|
|
|
87
89
|
@processor = QueueProcessor.start(appender: appender, **args)
|
|
88
90
|
end
|
|
89
91
|
|
|
92
|
+
# Returns [String] the class and object address of this proxy, whether it batches, and the
|
|
93
|
+
# wrapped appender's own #inspect, which shows only what that appender says is safe, but none of
|
|
94
|
+
# the instance variables of this proxy or its queue processor. See Subscriber#inspect.
|
|
95
|
+
def inspect
|
|
96
|
+
address = Kernel.instance_method(:to_s).bind_call(self).delete_suffix(">")
|
|
97
|
+
"#{address} batch=#{batch?} appender=#{appender.inspect}>"
|
|
98
|
+
end
|
|
99
|
+
|
|
90
100
|
# Re-open appender after a fork.
|
|
91
101
|
def reopen
|
|
92
102
|
appender.reopen if appender.respond_to?(:reopen)
|
|
@@ -104,6 +114,8 @@ module SemanticLogger
|
|
|
104
114
|
# processed: [Integer] Cumulative number of log messages written since startup.
|
|
105
115
|
# dropped: [Integer] Cumulative number of log messages dropped because the queue
|
|
106
116
|
# was full (only possible when non_blocking is enabled).
|
|
117
|
+
# failed: [Integer] Cumulative number of log messages discarded because their
|
|
118
|
+
# content could not be written (see Subscriber#message_error?).
|
|
107
119
|
def stats
|
|
108
120
|
{
|
|
109
121
|
name: name,
|
|
@@ -113,7 +125,8 @@ module SemanticLogger
|
|
|
113
125
|
capped: capped?,
|
|
114
126
|
max_queue_size: capped? ? max_queue_size : nil,
|
|
115
127
|
processed: processed_count,
|
|
116
|
-
dropped: dropped_count
|
|
128
|
+
dropped: dropped_count,
|
|
129
|
+
failed: failed_count
|
|
117
130
|
}
|
|
118
131
|
end
|
|
119
132
|
end
|