semantic_logger 5.0.0 → 5.1.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 +9 -0
- data/Rakefile +29 -0
- data/docs/api.md +480 -0
- data/docs/appenders.md +990 -0
- data/docs/config.md +605 -0
- data/docs/index.md +197 -0
- data/docs/log.md +108 -0
- data/docs/metrics.md +168 -0
- data/docs/operations.md +400 -0
- data/docs/rails.md +816 -0
- data/docs/security.md +119 -0
- data/docs/testing.md +285 -0
- data/docs/upgrading.md +409 -0
- data/lib/semantic_logger/appender/cloudwatch_logs.rb +4 -2
- data/lib/semantic_logger/appender/elasticsearch.rb +2 -5
- data/lib/semantic_logger/appender/elasticsearch_base.rb +17 -17
- data/lib/semantic_logger/appender/elasticsearch_http.rb +14 -7
- data/lib/semantic_logger/appender/file.rb +5 -0
- data/lib/semantic_logger/appender/new_relic.rb +6 -0
- data/lib/semantic_logger/appender/new_relic_logs.rb +2 -1
- data/lib/semantic_logger/appender/open_telemetry.rb +2 -1
- data/lib/semantic_logger/appender/opensearch.rb +2 -1
- data/lib/semantic_logger/appender/rabbitmq.rb +4 -3
- data/lib/semantic_logger/appender/sentry.rb +5 -0
- data/lib/semantic_logger/appender/syslog.rb +6 -3
- data/lib/semantic_logger/appender/tcp.rb +4 -3
- data/lib/semantic_logger/appender/wrapper.rb +2 -1
- data/lib/semantic_logger/appender.rb +6 -3
- data/lib/semantic_logger/appenders.rb +2 -1
- data/lib/semantic_logger/base.rb +0 -2
- data/lib/semantic_logger/formatters/color.rb +5 -2
- data/lib/semantic_logger/formatters/default.rb +7 -2
- data/lib/semantic_logger/formatters/logfmt.rb +4 -2
- data/lib/semantic_logger/formatters/pattern.rb +3 -1
- data/lib/semantic_logger/formatters/syslog.rb +2 -1
- data/lib/semantic_logger/formatters/syslog_cee.rb +2 -1
- data/lib/semantic_logger/metric/signalfx.rb +4 -2
- data/lib/semantic_logger/queue_processor.rb +5 -2
- data/lib/semantic_logger/reporters/minitest.rb +4 -4
- data/lib/semantic_logger/test/capture_log_events.rb +1 -1
- data/lib/semantic_logger/version.rb +1 -1
- metadata +14 -3
data/docs/operations.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Operations
|
|
6
|
+
{:.no_toc}
|
|
7
|
+
|
|
8
|
+
**Contents**
|
|
9
|
+
|
|
10
|
+
* TOC
|
|
11
|
+
{:toc}
|
|
12
|
+
|
|
13
|
+
This page covers running Semantic Logger in production: keeping logging alive across process forks,
|
|
14
|
+
rotating log files, tuning the background pipeline, controlling a running process with signals, and
|
|
15
|
+
shipping logs to a centralized system. The defaults are good for most applications, so reach for
|
|
16
|
+
these topics when you have a specific operational need.
|
|
17
|
+
|
|
18
|
+
For first-time setup (global settings, appenders, formatters, and filtering), see
|
|
19
|
+
[Configuration](config.html).
|
|
20
|
+
|
|
21
|
+
## Process forking
|
|
22
|
+
|
|
23
|
+
Frameworks such as Puma, Unicorn, and Resque **fork** the process: they start a worker by cloning the
|
|
24
|
+
parent. A forked child does not inherit a working copy of the parent's log file handles or background
|
|
25
|
+
thread, so unless those are re-opened in the child, logging quietly stops.
|
|
26
|
+
|
|
27
|
+
### It is automatic (default)
|
|
28
|
+
|
|
29
|
+
As of v5 you do not need to do anything. Semantic Logger installs a `Process._fork` hook (Ruby 3.1
|
|
30
|
+
and later) that calls `SemanticLogger.reopen` in the child after `fork`, `Process.daemon`,
|
|
31
|
+
`IO.popen`, `Kernel#system`, and backticks. That covers every forking framework (Puma, Unicorn,
|
|
32
|
+
Resque, Spring, Phusion Passenger, parallel tests, and so on).
|
|
33
|
+
|
|
34
|
+
`reopen` runs only once per process after a fork, so it is safe even if something else also calls it.
|
|
35
|
+
|
|
36
|
+
### Reopen manually
|
|
37
|
+
|
|
38
|
+
You only need the steps below if you turned the automatic hook off, or you rotated logs in a way that
|
|
39
|
+
did not fork (see [Log rotation](#log-rotation)).
|
|
40
|
+
|
|
41
|
+
1. Disable the automatic hook during boot, if you want full manual control:
|
|
42
|
+
|
|
43
|
+
~~~ruby
|
|
44
|
+
SemanticLogger.reopen_on_fork = false
|
|
45
|
+
~~~
|
|
46
|
+
|
|
47
|
+
2. Call `reopen` yourself after each fork, or after an in-process log rotation. Within the same
|
|
48
|
+
process, pass `force: true` to bypass the once-per-process guard:
|
|
49
|
+
|
|
50
|
+
~~~ruby
|
|
51
|
+
SemanticLogger.reopen # after a fork
|
|
52
|
+
SemanticLogger.reopen(force: true) # same process, e.g. after external log rotation
|
|
53
|
+
~~~
|
|
54
|
+
|
|
55
|
+
You might opt out if another library forks in ways you do not want to trigger a reopen, or you need
|
|
56
|
+
to control exactly when the appender thread restarts.
|
|
57
|
+
|
|
58
|
+
## Log rotation
|
|
59
|
+
|
|
60
|
+
For performance the log file is **not** re-opened on every write, so a log file must be rotated with
|
|
61
|
+
a **copy-truncate** strategy (copy the file aside, then truncate the original in place). Deleting or
|
|
62
|
+
renaming the file would leave Semantic Logger writing to a handle that no longer points at the live
|
|
63
|
+
file.
|
|
64
|
+
|
|
65
|
+
Linux's `logrotate` does this well. To set it up:
|
|
66
|
+
|
|
67
|
+
1. Create a config file for your application, for example `/etc/logrotate.d/my_app`.
|
|
68
|
+
2. Point it at your log directory and include `copytruncate`. For daily rotation:
|
|
69
|
+
|
|
70
|
+
~~~
|
|
71
|
+
/var/www/rails/my_app/log/*.log {
|
|
72
|
+
daily
|
|
73
|
+
missingok
|
|
74
|
+
copytruncate
|
|
75
|
+
rotate 14
|
|
76
|
+
compress
|
|
77
|
+
delaycompress
|
|
78
|
+
notifempty
|
|
79
|
+
}
|
|
80
|
+
~~~
|
|
81
|
+
|
|
82
|
+
Or, to rotate by size for very high volume logging:
|
|
83
|
+
|
|
84
|
+
~~~
|
|
85
|
+
/var/www/rails/my_app/log/*.log {
|
|
86
|
+
size 2G
|
|
87
|
+
missingok
|
|
88
|
+
copytruncate
|
|
89
|
+
rotate 7
|
|
90
|
+
compress
|
|
91
|
+
nodelaycompress
|
|
92
|
+
notifempty
|
|
93
|
+
dateformat .%Y%m%d
|
|
94
|
+
}
|
|
95
|
+
~~~
|
|
96
|
+
|
|
97
|
+
Other rotation tools work too, as long as they use copy-truncate. If your tool cannot copy-truncate
|
|
98
|
+
and instead moves the file, reopen the handles in-process afterwards with
|
|
99
|
+
`SemanticLogger.reopen(force: true)` (see [Process forking](#process-forking)).
|
|
100
|
+
|
|
101
|
+
## Performance and reliability tuning
|
|
102
|
+
|
|
103
|
+
Every logger hands its events to one shared background thread through an in-memory queue. That thread
|
|
104
|
+
writes each event to every appender in turn, so the call to `logger.info` returns immediately. The
|
|
105
|
+
knobs below tune that pipeline. The defaults suit most applications; reach for a knob when you have a
|
|
106
|
+
specific throughput, availability, or reliability requirement.
|
|
107
|
+
|
|
108
|
+
### Drop messages instead of blocking
|
|
109
|
+
|
|
110
|
+
By default the queue is capped (`max_queue_size`, default `10,000`). When it fills (for example
|
|
111
|
+
because an appender cannot keep up), `logger.info` **blocks** until there is room, guaranteeing no
|
|
112
|
+
message is lost at the cost of briefly slowing the application.
|
|
113
|
+
|
|
114
|
+
When availability matters more than complete logs, set `non_blocking: true` so that messages are
|
|
115
|
+
**dropped** instead of blocking once the queue is full:
|
|
116
|
+
|
|
117
|
+
~~~ruby
|
|
118
|
+
SemanticLogger.add_appender(
|
|
119
|
+
file_name: "production.log",
|
|
120
|
+
async: true,
|
|
121
|
+
non_blocking: true
|
|
122
|
+
)
|
|
123
|
+
~~~
|
|
124
|
+
|
|
125
|
+
Dropped messages are counted and reported at most once every `dropped_message_report_seconds`
|
|
126
|
+
(default `30`) so they do not go unnoticed:
|
|
127
|
+
|
|
128
|
+
~~~ruby
|
|
129
|
+
SemanticLogger.add_appender(
|
|
130
|
+
file_name: "production.log",
|
|
131
|
+
async: true,
|
|
132
|
+
non_blocking: true,
|
|
133
|
+
dropped_message_report_seconds: 60
|
|
134
|
+
)
|
|
135
|
+
~~~
|
|
136
|
+
|
|
137
|
+
`non_blocking` applies only to a capped queue. An uncapped queue (`max_queue_size: -1`) never blocks
|
|
138
|
+
and never drops, but can grow without bound.
|
|
139
|
+
|
|
140
|
+
### Retry a failing appender
|
|
141
|
+
|
|
142
|
+
If an appender raises while the worker thread is writing, the thread logs the error and restarts, so
|
|
143
|
+
a transient failure (such as a brief network blip to a remote appender) does not permanently stop
|
|
144
|
+
logging. Each restart sleeps with an increasing back-off (1 second, then 2, ...), reset as soon as a
|
|
145
|
+
message is processed successfully.
|
|
146
|
+
|
|
147
|
+
After `async_max_retries` (default `100`) consecutive failed restarts the worker thread gives up,
|
|
148
|
+
rather than spinning forever on a persistent failure:
|
|
149
|
+
|
|
150
|
+
~~~ruby
|
|
151
|
+
SemanticLogger.add_appender(
|
|
152
|
+
appender: :http,
|
|
153
|
+
url: "https://example.com/log",
|
|
154
|
+
async: true,
|
|
155
|
+
async_max_retries: 20
|
|
156
|
+
)
|
|
157
|
+
~~~
|
|
158
|
+
|
|
159
|
+
Set `async_max_retries: -1` to retry indefinitely. The back-off still applies and still resets after
|
|
160
|
+
a successful message.
|
|
161
|
+
|
|
162
|
+
### Give a slow appender its own thread
|
|
163
|
+
|
|
164
|
+
If one destination is slow, such as a remote HTTP service, run just that appender on its own thread
|
|
165
|
+
and queue so it cannot hold up the others:
|
|
166
|
+
|
|
167
|
+
~~~ruby
|
|
168
|
+
SemanticLogger.add_appender(appender: :http, url: "https://example.com/log", async: true)
|
|
169
|
+
~~~
|
|
170
|
+
|
|
171
|
+
### Monitoring the background thread
|
|
172
|
+
|
|
173
|
+
The background thread can occasionally fall behind, for example when an appender is slow or a sudden
|
|
174
|
+
burst of logging occurs. Check the queue at runtime:
|
|
175
|
+
|
|
176
|
+
~~~ruby
|
|
177
|
+
# Number of log entries still waiting to be written
|
|
178
|
+
SemanticLogger.queue_size
|
|
179
|
+
~~~
|
|
180
|
+
|
|
181
|
+
For a fuller operational picture, including per-appender queues, use `SemanticLogger.stats`. It
|
|
182
|
+
returns a Hash describing the main pipeline and every appender, handy for exporting Semantic Logger's
|
|
183
|
+
own health to a monitoring system such as Prometheus or statsd:
|
|
184
|
+
|
|
185
|
+
~~~ruby
|
|
186
|
+
SemanticLogger.stats
|
|
187
|
+
# => {
|
|
188
|
+
# queue_size: 0, # entries waiting on the main pipeline queue
|
|
189
|
+
# capped: true, # whether the main queue has a maximum size
|
|
190
|
+
# max_queue_size: 10_000, # nil when uncapped
|
|
191
|
+
# thread_active: true, # whether the main pipeline thread is running
|
|
192
|
+
# processed: 1_532, # cumulative entries processed since startup
|
|
193
|
+
# dropped: 0, # cumulative entries dropped at the main queue
|
|
194
|
+
# appenders: [
|
|
195
|
+
# { name: "SemanticLogger::Appender::File", async: false },
|
|
196
|
+
# { name: "SemanticLogger::Appender::Http",
|
|
197
|
+
# async: true, # this appender has its own thread and queue
|
|
198
|
+
# thread_active: true,
|
|
199
|
+
# queue_size: 3,
|
|
200
|
+
# capped: true,
|
|
201
|
+
# max_queue_size: 10_000,
|
|
202
|
+
# processed: 1_529,
|
|
203
|
+
# dropped: 0 }
|
|
204
|
+
# ]
|
|
205
|
+
# }
|
|
206
|
+
~~~
|
|
207
|
+
|
|
208
|
+
The `processed` and `dropped` counters are cumulative since process startup. Reading `stats` is
|
|
209
|
+
thread-safe and adds no locking to the logging hot path.
|
|
210
|
+
|
|
211
|
+
Semantic Logger also warns when an entry has waited on the queue too long. Tune the threshold and how
|
|
212
|
+
often it is checked:
|
|
213
|
+
|
|
214
|
+
~~~ruby
|
|
215
|
+
# Warn when an entry has been on the queue longer than this many seconds ( default: 30 )
|
|
216
|
+
SemanticLogger.lag_threshold_s
|
|
217
|
+
|
|
218
|
+
# Number of messages to process between lag checks ( default: 1,000 )
|
|
219
|
+
SemanticLogger.lag_check_interval = 1_000
|
|
220
|
+
~~~
|
|
221
|
+
|
|
222
|
+
If a sustained burst is overwhelming logging, reduce the volume by raising the log level, reduce the
|
|
223
|
+
number of appenders, or speed up the slow appender.
|
|
224
|
+
|
|
225
|
+
### Synchronous operation
|
|
226
|
+
|
|
227
|
+
Synchronous mode bypasses the background thread and logs inline on the calling thread. This disables
|
|
228
|
+
a core design principle of Semantic Logger and slows the calling thread, so it is not recommended for
|
|
229
|
+
most applications. It can suit short-lived or single-threaded programs, or forked environments where
|
|
230
|
+
you would rather not re-create the logging thread.
|
|
231
|
+
|
|
232
|
+
Enable it **before** adding any appenders:
|
|
233
|
+
|
|
234
|
+
~~~ruby
|
|
235
|
+
SemanticLogger.sync!
|
|
236
|
+
~~~
|
|
237
|
+
|
|
238
|
+
To guarantee it is set early enough, replace the require with the synchronous variant:
|
|
239
|
+
|
|
240
|
+
~~~ruby
|
|
241
|
+
require "semantic_logger/sync"
|
|
242
|
+
~~~
|
|
243
|
+
|
|
244
|
+
Or, in a Gemfile:
|
|
245
|
+
|
|
246
|
+
~~~ruby
|
|
247
|
+
gem "semantic_logger", require: "semantic_logger/sync"
|
|
248
|
+
~~~
|
|
249
|
+
|
|
250
|
+
## Linux signals
|
|
251
|
+
|
|
252
|
+
On Linux, Unix, and Mac, Semantic Logger can respond to signals, for example to change the log level
|
|
253
|
+
of a running process without restarting it. It registers no signal handlers on startup, so as not to
|
|
254
|
+
interfere with any your application already uses.
|
|
255
|
+
|
|
256
|
+
**Step 1: enable signal handling** during boot:
|
|
257
|
+
|
|
258
|
+
~~~ruby
|
|
259
|
+
# config/initializers/semantic_logger.rb, or during startup of a standalone app
|
|
260
|
+
SemanticLogger.add_signal_handler
|
|
261
|
+
~~~
|
|
262
|
+
|
|
263
|
+
**Step 2: send the signal** you need. The capabilities are below.
|
|
264
|
+
|
|
265
|
+
### Change the log level (USR2)
|
|
266
|
+
|
|
267
|
+
Send `SIGUSR2` to rotate the global default level, without restarting. Each signal moves the level
|
|
268
|
+
one step through this sequence, wrapping from `:trace` back to `:fatal`:
|
|
269
|
+
|
|
270
|
+
:fatal :error :warn :info :debug :trace
|
|
271
|
+
|
|
272
|
+
~~~
|
|
273
|
+
kill -SIGUSR2 1234
|
|
274
|
+
~~~
|
|
275
|
+
|
|
276
|
+
This changes only the global default level. Loggers whose level was set explicitly in the application
|
|
277
|
+
are unaffected.
|
|
278
|
+
|
|
279
|
+
### Dump all threads (TTIN)
|
|
280
|
+
|
|
281
|
+
Send `TTIN` to write every thread, with its backtrace where available, to the log. Naming your
|
|
282
|
+
threads (`Thread.current.name = "My Worker"`) makes the dump far more useful:
|
|
283
|
+
|
|
284
|
+
~~~
|
|
285
|
+
kill -TTIN 1234
|
|
286
|
+
~~~
|
|
287
|
+
|
|
288
|
+
On JRuby this differs from the standard `QUIT`-triggered Java thread dump, which includes system
|
|
289
|
+
threads and Java stack traces.
|
|
290
|
+
|
|
291
|
+
### JRuby garbage collection logging
|
|
292
|
+
|
|
293
|
+
On JRuby, any garbage collection that takes longer than 100ms is logged as a warning to the regular
|
|
294
|
+
appenders, giving visibility into GC pauses that could affect active requests.
|
|
295
|
+
|
|
296
|
+
### Choose your own signals
|
|
297
|
+
|
|
298
|
+
Pass different signals, or set one to `nil` to skip it. Set the GC threshold to `nil` to skip the
|
|
299
|
+
JRuby garbage collection logging:
|
|
300
|
+
|
|
301
|
+
~~~ruby
|
|
302
|
+
# Log level change on USR1, thread dump on USR2, GC threshold of 100,000 micro-seconds
|
|
303
|
+
SemanticLogger.add_signal_handler("USR1", "USR2", 100000)
|
|
304
|
+
~~~
|
|
305
|
+
|
|
306
|
+
## Centralized logging
|
|
307
|
+
|
|
308
|
+
Once you run more than one process or server, reading log files one at a time stops scaling. A
|
|
309
|
+
**centralized logging** system collects the events from every process into one place where you can
|
|
310
|
+
search, filter, and build dashboards across all of them at once. This is where Semantic Logger's
|
|
311
|
+
structured output pays off: the payload, tags, duration, and metrics on each entry arrive as real
|
|
312
|
+
fields, not text that has to be re-parsed.
|
|
313
|
+
|
|
314
|
+
This walks through one popular stack end to end as a concrete example:
|
|
315
|
+
|
|
316
|
+
* **Semantic Logger** forwards structured events from your application.
|
|
317
|
+
* **Elasticsearch** stores and indexes them.
|
|
318
|
+
* **Kibana** provides search and dashboards on top of Elasticsearch.
|
|
319
|
+
|
|
320
|
+
The same shape applies to other aggregators (Graylog, Splunk, Loki, Logstash, Syslog). See
|
|
321
|
+
[Other destinations](#other-destinations) and [Appenders](appenders.html).
|
|
322
|
+
|
|
323
|
+
### Step 1: Run Elasticsearch and Kibana
|
|
324
|
+
|
|
325
|
+
Install and start both. Any installation method works; these notes use
|
|
326
|
+
[homebrew](https://brew.sh) on macOS, follow the product links for other platforms.
|
|
327
|
+
|
|
328
|
+
~~~
|
|
329
|
+
brew install elasticsearch
|
|
330
|
+
brew install kibana
|
|
331
|
+
~~~
|
|
332
|
+
|
|
333
|
+
Start each one (follow the on-screen instructions to auto-start them), and confirm Elasticsearch is
|
|
334
|
+
reachable, by default at `http://localhost:9200`, and Kibana at `http://localhost:5601`.
|
|
335
|
+
|
|
336
|
+
### Step 2: Forward your application's logs
|
|
337
|
+
|
|
338
|
+
Add the Elasticsearch appender so Semantic Logger ships every entry to Elasticsearch. In a Rails app
|
|
339
|
+
using [rails_semantic_logger](rails.html), put this in an initializer; otherwise add it where you
|
|
340
|
+
configure Semantic Logger at startup:
|
|
341
|
+
|
|
342
|
+
~~~ruby
|
|
343
|
+
SemanticLogger.add_appender(
|
|
344
|
+
appender: :elasticsearch,
|
|
345
|
+
url: "http://localhost:9200"
|
|
346
|
+
)
|
|
347
|
+
~~~
|
|
348
|
+
|
|
349
|
+
By default entries are written to a daily index named `semantic_logger-YYYY.MM.DD`, so the index
|
|
350
|
+
pattern to search in Kibana is `semantic_logger-*`. See [Elasticsearch](appenders.html#elasticsearch)
|
|
351
|
+
for options such as a custom index name or data streams.
|
|
352
|
+
|
|
353
|
+
Restart the application and exercise it so it generates a few log entries. If nothing appears later,
|
|
354
|
+
check the application's own log (for example `log/development.log`) for connection errors.
|
|
355
|
+
|
|
356
|
+
### Step 3: View the logs in Kibana
|
|
357
|
+
|
|
358
|
+
1. Open Kibana at [http://localhost:5601](http://localhost:5601).
|
|
359
|
+
2. Create an **index pattern** (called a "data view" in newer Kibana versions) that matches
|
|
360
|
+
`semantic_logger-*`.
|
|
361
|
+
3. When asked for the time field, choose `timestamp`.
|
|
362
|
+
4. Open **Discover**. Your application's log entries appear. If the list is empty, widen the time
|
|
363
|
+
range in the top right.
|
|
364
|
+
5. Add a few columns so each entry is readable at a glance, for example `host`, `level`, `name`, and
|
|
365
|
+
`message`.
|
|
366
|
+
|
|
367
|
+
The exact menu names vary between Kibana versions, but the three things you need are always the same:
|
|
368
|
+
an index pattern of `semantic_logger-*`, a time field of `timestamp`, and the Discover view.
|
|
369
|
+
|
|
370
|
+
### Step 4: Search
|
|
371
|
+
|
|
372
|
+
In Discover, query against the structured fields directly. A few examples:
|
|
373
|
+
|
|
374
|
+
~~~
|
|
375
|
+
# Only error level entries
|
|
376
|
+
level: error
|
|
377
|
+
|
|
378
|
+
# Only entries from one host
|
|
379
|
+
host: mymachine
|
|
380
|
+
|
|
381
|
+
# Find a value in the logging tags
|
|
382
|
+
tags: 17262353
|
|
383
|
+
~~~
|
|
384
|
+
|
|
385
|
+
Because the payload, tags, and metrics are real fields, you can filter and build dashboards on them
|
|
386
|
+
without writing log-parsing expressions.
|
|
387
|
+
|
|
388
|
+
### Other destinations
|
|
389
|
+
|
|
390
|
+
Elasticsearch and Kibana are just one option. Semantic Logger also forwards to other centralized
|
|
391
|
+
logging systems and aggregators, including:
|
|
392
|
+
|
|
393
|
+
* Logstash
|
|
394
|
+
* Graylog
|
|
395
|
+
* Splunk
|
|
396
|
+
* Grafana Loki
|
|
397
|
+
* Loggly
|
|
398
|
+
* Syslog
|
|
399
|
+
|
|
400
|
+
See [Appenders](appenders.html) to configure any of these as a destination.
|