cogger 0.11.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +325 -78
- data/cogger.gemspec +2 -1
- data/lib/cogger/configuration.rb +8 -3
- data/lib/cogger/entry.rb +53 -0
- data/lib/cogger/formatters/color.rb +2 -2
- data/lib/cogger/formatters/crash.rb +2 -2
- data/lib/cogger/formatters/emoji.rb +14 -0
- data/lib/cogger/formatters/json.rb +10 -8
- data/lib/cogger/formatters/kit/sanitizer.rb +6 -27
- data/lib/cogger/formatters/parsers/individual.rb +1 -1
- data/lib/cogger/formatters/parsers/universal.rb +3 -1
- data/lib/cogger/formatters/processors/color.rb +3 -3
- data/lib/cogger/formatters/simple.rb +2 -2
- data/lib/cogger/hub.rb +59 -18
- data/lib/cogger/registry.rb +1 -8
- data/lib/cogger/tag.rb +40 -0
- data/lib/cogger.rb +0 -5
- data.tar.gz.sig +0 -0
- metadata +20 -4
- metadata.gz.sig +0 -0
- data/lib/cogger/client.rb +0 -53
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57f5049812c9af7fd2f1f936ac37c85562fad47517f6b3d2ccd2e34e2d1ee691
|
4
|
+
data.tar.gz: 2ecc0b1bd9e9cc37c85ee9f8db19c936b47d65a71b4723631f96debcdbd4b23e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4318c120bb3182a8a0740025b1528a6643b876b3d0ff71e3b875304484b4da5df44af044d474e2d93cacd1807487560063ac5e4d8499ef514a42c1c5644573a6
|
7
|
+
data.tar.gz: 7018412ed2a90da5e9fc31931e203d76767f405ff1559644570382891ccdb412c19c30eb2bb86f56f52be4e624f2938decb8f00c5fbf50feaf11735cf2e9f8bc
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.adoc
CHANGED
@@ -21,33 +21,34 @@ toc::[]
|
|
21
21
|
* Provides customizable templates which leverage the native {format_link}.
|
22
22
|
* Provides customizable formatters for simple, color, JSON, and/or custom output.
|
23
23
|
* Provides multiple streams so you can log the same information to several outputs at once.
|
24
|
+
* Provides global and individual log entry tagging.
|
24
25
|
* Provides filtering of sensitive information.
|
25
26
|
|
26
27
|
== Screenshots
|
27
28
|
|
28
29
|
*Emoji*
|
29
30
|
|
30
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/emoji.png[Emoji,width=
|
31
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/emoji.png[Emoji,width=234,height=293]
|
31
32
|
|
32
33
|
*Color*
|
33
34
|
|
34
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/color.png[Color,width=
|
35
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/color.png[Color,width=202,height=288]
|
35
36
|
|
36
37
|
*Simple*
|
37
38
|
|
38
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/simple.png[Simple,width=
|
39
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/simple.png[Simple,width=288,height=253]
|
39
40
|
|
40
41
|
*Detail*
|
41
42
|
|
42
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/detail.png[Detail,width=
|
43
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/detail.png[Detail,width=611,height=284]
|
43
44
|
|
44
45
|
*JSON*
|
45
46
|
|
46
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/json.png[JSON,width=
|
47
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/json.png[JSON,width=962,height=297]
|
47
48
|
|
48
49
|
*Rack*
|
49
50
|
|
50
|
-
image::https://alchemists.io/images/projects/cogger/screenshots/rack.png[Rack,width=
|
51
|
+
image::https://alchemists.io/images/projects/cogger/screenshots/rack.png[Rack,width=883,height=282]
|
51
52
|
|
52
53
|
== Requirements
|
53
54
|
|
@@ -92,7 +93,7 @@ All interaction is provided by `Cogger` which can be used as follows:
|
|
92
93
|
[source,ruby]
|
93
94
|
----
|
94
95
|
logger = Cogger.new
|
95
|
-
logger.info "
|
96
|
+
logger.info "Demo" # "Demo"
|
96
97
|
----
|
97
98
|
|
98
99
|
If you set your logging level to `debug`, you can walk through each level:
|
@@ -102,22 +103,24 @@ If you set your logging level to `debug`, you can walk through each level:
|
|
102
103
|
logger = Cogger.new level: :debug
|
103
104
|
|
104
105
|
# Without blocks.
|
105
|
-
logger.debug "
|
106
|
-
logger.info "
|
107
|
-
logger.warn "
|
108
|
-
logger.error "
|
109
|
-
logger.fatal "
|
110
|
-
logger.unknown "
|
111
|
-
logger.any "
|
106
|
+
logger.debug "Demo" # "🔎 Demo"
|
107
|
+
logger.info "Demo" # "🟢 Demo"
|
108
|
+
logger.warn "Demo" # "⚠️ Demo"
|
109
|
+
logger.error "Demo" # "🛑 Demo"
|
110
|
+
logger.fatal "Demo" # "🔥 Demo"
|
111
|
+
logger.unknown "Demo" # "⚫️ Demo"
|
112
|
+
logger.any "Demo" # "⚫️ Demo"
|
113
|
+
logger.add Logger::INFO, "Demo" # "🟢 Demo"
|
112
114
|
|
113
115
|
# With blocks.
|
114
|
-
logger.debug { "
|
115
|
-
logger.info { "
|
116
|
-
logger.warn { "
|
117
|
-
logger.error { "
|
118
|
-
logger.fatal { "
|
119
|
-
logger.unknown { "
|
120
|
-
logger.any { "
|
116
|
+
logger.debug { "Demo" } # "🔎 Demo"
|
117
|
+
logger.info { "Demo" } # "🟢 Demo"
|
118
|
+
logger.warn { "Demo" } # "⚠️ Demo"
|
119
|
+
logger.error { "Demo" } # "🛑 Demo"
|
120
|
+
logger.fatal { "Demo" } # "🔥 Demo"
|
121
|
+
logger.unknown { "Demo" } # "⚫️ Demo"
|
122
|
+
logger.any { "Demo" } # "⚫️ Demo"
|
123
|
+
logger.add(Logger::INFO) { "Demo" } # "🟢 Demo"
|
121
124
|
----
|
122
125
|
|
123
126
|
=== Initialization
|
@@ -128,6 +131,7 @@ When creating a new logger, you can configure behavior via the following attribu
|
|
128
131
|
* `io`: The input/output stream. This can be `STDOUT/$stdout`, a file/path, or `nil`. Default: `$stdout`.
|
129
132
|
* `level`: The severity level you want to log at. Can be `:debug`, `:info`, `:warn`, `:error`, `:fatal`, or `:unknown`. Default: `:info`.
|
130
133
|
* `formatter`: The formatter to use for formatting your log output. Default: `Cogger::Formatter::Color`. See the _Formatters_ section for more info.
|
134
|
+
* `tags`: Global tagging for _every_ log entry which _must_ be an array of objects you wish to use for tagging purposes.
|
131
135
|
* `mode`: The binary mode which determines if your logs should be written in binary mode or not. Can be `true` or `false` and is identical to the `binmode` functionality found in the {logger_link} class. Default: `false`.
|
132
136
|
* `age`: The rotation age of your log. This only applies when logging to a file. This is equivalent to the `shift_age` as found with the {logger_link} class. Default: `0`.
|
133
137
|
* `size`: The rotation size of your log. This only applies when logging to a file. This is equivalent to the `shift_size` as found with the {logger_link} class. Default: `1,048,576` (i.e. 1 MB).
|
@@ -144,12 +148,80 @@ logger = Cogger.new
|
|
144
148
|
logger = Cogger.new id: :demo,
|
145
149
|
io: "demo.log",
|
146
150
|
level: :debug,
|
151
|
+
formatter: :json,
|
152
|
+
tags: %w[DEMO DB],
|
147
153
|
mode: false,
|
148
154
|
age: 5,
|
149
155
|
size: 1_000,
|
150
156
|
suffix: "%Y"
|
151
157
|
----
|
152
158
|
|
159
|
+
=== Inspection
|
160
|
+
|
161
|
+
Each instance can be inspected via the `#inspect` message:
|
162
|
+
|
163
|
+
[source,ruby]
|
164
|
+
----
|
165
|
+
logger = Cogger.new
|
166
|
+
logger.inspect
|
167
|
+
|
168
|
+
# "#<Cogger::Hub @id=console,
|
169
|
+
# @io=IO,
|
170
|
+
# @level=1,
|
171
|
+
# @formatter=Cogger::Formatters::Emoji,
|
172
|
+
# @tags=[],
|
173
|
+
# @mode=false,
|
174
|
+
# @age=0,
|
175
|
+
# @size=1048576,
|
176
|
+
# @suffix=\"%Y-%m-%d\",
|
177
|
+
# @entry=Cogger::Entry,
|
178
|
+
# @logger=Logger>"
|
179
|
+
----
|
180
|
+
|
181
|
+
You can also look at individual attributes:
|
182
|
+
|
183
|
+
[source,ruby]
|
184
|
+
----
|
185
|
+
logger = Cogger.new
|
186
|
+
|
187
|
+
logger.id # "console"
|
188
|
+
logger.io # #<IO:<STDOUT>>
|
189
|
+
logger.tags # []
|
190
|
+
logger.mode # false
|
191
|
+
logger.age # 0
|
192
|
+
logger.size # 1048576
|
193
|
+
logger.suffix # "%Y-%m-%d"
|
194
|
+
|
195
|
+
logger.level # 1
|
196
|
+
logger.formatter # Cogger::Formatters::Emoji
|
197
|
+
logger.debug? # false
|
198
|
+
logger.info? # true
|
199
|
+
logger.warn? # true
|
200
|
+
logger.error? # true
|
201
|
+
logger.fatal? # true
|
202
|
+
----
|
203
|
+
|
204
|
+
=== Mutations
|
205
|
+
|
206
|
+
Each instance can be mutated using the following messages:
|
207
|
+
|
208
|
+
[source,ruby]
|
209
|
+
----
|
210
|
+
logger = Cogger.new io: StringIO.new
|
211
|
+
|
212
|
+
logger.close # nil
|
213
|
+
logger.reopen # Logger
|
214
|
+
logger.debug! # 0
|
215
|
+
logger.info! # 1
|
216
|
+
logger.warn! # 2
|
217
|
+
logger.error! # 3
|
218
|
+
logger.fatal! # 4
|
219
|
+
logger.formatter = Cogger::Formatters::Simple.new # Cogger::Formatters::Simple
|
220
|
+
logger.level = Logger::WARN # 2
|
221
|
+
----
|
222
|
+
|
223
|
+
Please see the {logger_link} documentation for more information.
|
224
|
+
|
153
225
|
=== Environment
|
154
226
|
|
155
227
|
The default log level is `INFO` but can be customized via your environment. For instance, you could
|
@@ -174,7 +246,7 @@ Templates are used by all formatters and adhere to {format_link} as used by `Ker
|
|
174
246
|
- Use of _reference by name_ is required which means `%<demo>s` is allowed but `%{demo}` is not. This is because _reference by name_ is required for regular expressions and/or {pattern_matching_link}.
|
175
247
|
- Use of the `n$` flag is prohibited because this isn't compatible with the above.
|
176
248
|
|
177
|
-
In addition to the above, the {format_link} is further enhanced with the use of _universal_ and _individual_ directives which are primarily used by the _color_ formatter but
|
249
|
+
In addition to the above, the {format_link} is further enhanced with the use of _universal_ and _individual_ directives which are primarily used by the _color_ formatter but can prove useful for other formatters. Example:
|
178
250
|
|
179
251
|
[source,ruby]
|
180
252
|
----
|
@@ -213,7 +285,7 @@ At this point, you might have gathered that there are specific keys you can use
|
|
213
285
|
|
214
286
|
This also means if you pass in these same keys as a log event (example: `logger.info id: :bad, at: Time.now, severity: :bogus`) they will be ignored.
|
215
287
|
|
216
|
-
The last key (or keys) is variable and customizable to your needs which is the log event message. Here a couple of examples to illustrate:
|
288
|
+
The last key (or keys) is variable and customizable to your needs which is the log event message. The only special key is the `tags` key which is explained later. Here a couple of examples to illustrate:
|
217
289
|
|
218
290
|
[source,ruby]
|
219
291
|
----
|
@@ -247,35 +319,38 @@ Cogger.emojis
|
|
247
319
|
# }
|
248
320
|
----
|
249
321
|
|
250
|
-
|
322
|
+
The `:emoji` formatter is the default formatter which provides dynamic rendering of emojis based on severity level. Example:
|
251
323
|
|
252
324
|
[source,ruby]
|
253
325
|
----
|
254
|
-
Cogger.
|
255
|
-
|
326
|
+
logger = Cogger.new
|
327
|
+
logger.info "Demo"
|
328
|
+
|
329
|
+
# 🟢 Demo
|
256
330
|
----
|
257
331
|
|
258
|
-
|
332
|
+
To add one or more emojis, you can chain messages together when registering them:
|
259
333
|
|
260
334
|
[source,ruby]
|
261
335
|
----
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
# 🟢 demo
|
336
|
+
Cogger.add_emoji(:tada, "🎉")
|
337
|
+
.add_emoji :favorite, "❇️"
|
266
338
|
----
|
267
339
|
|
268
|
-
If you
|
340
|
+
If you always want to use the _same_ emoji, you could use the emoji formatter with a specific template:
|
269
341
|
|
270
342
|
[source,ruby]
|
271
343
|
----
|
272
|
-
logger = Cogger.new formatter: Cogger::Formatters::
|
273
|
-
logger.info "demo"
|
344
|
+
logger = Cogger.new formatter: Cogger::Formatters::Emoji.new("%<emoji:tada>s %<message:dynamic>s")
|
274
345
|
|
275
|
-
|
346
|
+
logger.info "Demo"
|
347
|
+
logger.warn "Demo"
|
348
|
+
|
349
|
+
# 🎉 Demo
|
350
|
+
# 🎉 Demo
|
276
351
|
----
|
277
352
|
|
278
|
-
|
353
|
+
As you can see, using a specific and non-dynamic emoji will _always_ display regardless of the current severity level.
|
279
354
|
|
280
355
|
=== Aliases
|
281
356
|
|
@@ -300,7 +375,7 @@ Aliases are a powerful way to customize your colors and use short syntax in your
|
|
300
375
|
"%<message:haze>"
|
301
376
|
----
|
302
377
|
|
303
|
-
|
378
|
+
💡 These aliases are used by the color and emoji formatters but check out the {tone_link} documentation and _Formatters_ section below for further examples.
|
304
379
|
|
305
380
|
=== Formatters
|
306
381
|
|
@@ -320,8 +395,8 @@ Cogger.formatters
|
|
320
395
|
# "[%<id>s] [%<severity>s] [%<at>s] %<message>s"
|
321
396
|
# ],
|
322
397
|
# :emoji => [
|
323
|
-
# Cogger::Formatters::
|
324
|
-
#
|
398
|
+
# Cogger::Formatters::Emoji < Cogger::Formatters::Color,
|
399
|
+
# nil
|
325
400
|
# ],
|
326
401
|
# :json => [
|
327
402
|
# Cogger::Formatters::JSON < Object,
|
@@ -342,10 +417,10 @@ You can add a formatter by providing a key, class, and _optional_ template. If a
|
|
342
417
|
|
343
418
|
[source,ruby]
|
344
419
|
----
|
345
|
-
#
|
420
|
+
# Registration
|
346
421
|
Cogger.add_formatter :basic, Cogger::Formatters::Simple, "%<severity>s %<message>s"
|
347
422
|
|
348
|
-
#
|
423
|
+
# Usage
|
349
424
|
Cogger.get_formatter :basic
|
350
425
|
# [Cogger::Formatters::Simple, "%<severity>s %<message>s"]
|
351
426
|
----
|
@@ -354,8 +429,10 @@ Symbols or strings can be used interchangeably when adding/getting formatters. A
|
|
354
429
|
|
355
430
|
[source,ruby]
|
356
431
|
----
|
357
|
-
Cogger::Formatters::
|
358
|
-
# "%<message>s"
|
432
|
+
Cogger::Formatters::Color::TEMPLATE # "%<message:dynamic>s"
|
433
|
+
Cogger::Formatters::Emoji::TEMPLATE # "%<emoji:dynamic>s %<message:dynamic>s"
|
434
|
+
Cogger::Formatters::JSON::TEMPLATE # nil
|
435
|
+
Cogger::Formatters::Simple::TEMPLATE # "%<message>s"
|
359
436
|
----
|
360
437
|
|
361
438
|
💡 When you find yourself customizing any of the default formatters, you can reduce typing by adding your custom configuration to the registry and then referring to it via it's associated key when initializing a new logger.
|
@@ -375,18 +452,14 @@ logger = Cogger.new formatter: :rack
|
|
375
452
|
|
376
453
|
==== Color
|
377
454
|
|
378
|
-
The color formatter
|
455
|
+
The color formatter allows you to have color coded logs and can be configured as follows:
|
379
456
|
|
380
457
|
[source,ruby]
|
381
458
|
----
|
382
|
-
logger = Cogger.new
|
383
|
-
logger = Cogger.new formatter: Cogger::Formatters::Color.new
|
384
|
-
logger = Cogger.new formatter: Cogger::Formatters::Color.new("%<message:dynamic>s")
|
459
|
+
logger = Cogger.new formatter: :color
|
385
460
|
----
|
386
461
|
|
387
|
-
|
388
|
-
|
389
|
-
In addition to template customization, you can customize your color aliases as well. Default colors are provided by {tone_link} which are _aliased_ by log level:
|
462
|
+
Please refer back to the _Templates_ section on how to customize this formatter with more sophisticated templates. In addition to template customization, you can customize your color aliases as well. Default colors are provided by {tone_link} which are _aliased_ by log level:
|
390
463
|
|
391
464
|
[source,ruby]
|
392
465
|
----
|
@@ -417,21 +490,71 @@ Once an alias is added, it can be immediately applied via the template of your f
|
|
417
490
|
logger = Cogger.new formatter: Cogger::Formatters::Color.new("<mystery>%<message>s</mystery>")
|
418
491
|
----
|
419
492
|
|
420
|
-
ℹ️ Much like the simple formatter, any leading or trailing whitespace is automatically after the template has been formatted.
|
493
|
+
ℹ️ Much like the simple formatter, any leading or trailing whitespace is automatically removed after the template has been formatted.
|
494
|
+
|
495
|
+
==== Emoji
|
496
|
+
|
497
|
+
The emoji formatter is enabled by default and is the equivalent of initializing with either of the following:
|
498
|
+
|
499
|
+
[source,ruby]
|
500
|
+
----
|
501
|
+
logger = Cogger.new
|
502
|
+
logger = Cogger.new formatter: :emoji
|
503
|
+
logger = Cogger.new formatter: Cogger::Formatters::Emoji.new("%<emoji:dynamic>s %<message:dynamic>s")
|
504
|
+
----
|
505
|
+
|
506
|
+
All of the above examples are identical so you can see how different formatters can be used and customized further. The default emojis are registered as follows:
|
507
|
+
|
508
|
+
[source,ruby]
|
509
|
+
----
|
510
|
+
Cogger.emojis
|
511
|
+
|
512
|
+
# {
|
513
|
+
# :debug => "🔎",
|
514
|
+
# :info => "🟢",
|
515
|
+
# :warn => "⚠️ ",
|
516
|
+
# :error => "🛑",
|
517
|
+
# :fatal => "🔥",
|
518
|
+
# :any => "⚫️"
|
519
|
+
# }
|
520
|
+
----
|
521
|
+
|
522
|
+
This allows an emoji to be dynamically applied based on log severity. You can add or modify aliases as follows:
|
523
|
+
|
524
|
+
[source,ruby]
|
525
|
+
----
|
526
|
+
Cogger.add_emoji :warn, "🟡"
|
527
|
+
----
|
528
|
+
|
529
|
+
Once an alias is added/updated, it can be immediately applied via the template of your formatter. Example:
|
530
|
+
|
531
|
+
[source,ruby]
|
532
|
+
----
|
533
|
+
logger = Cogger.new
|
534
|
+
logger.warn "Demo"
|
535
|
+
# 🟡 Demo
|
536
|
+
----
|
537
|
+
|
538
|
+
ℹ️ Much like the simple and color formatters, any leading or trailing whitespace is automatically removed after the template has been formatted.
|
421
539
|
|
422
540
|
==== JSON
|
423
541
|
|
424
|
-
This formatter is similar in behavior to the _simple_ formatter except the template allows you to
|
542
|
+
This formatter is similar in behavior to the _simple_ formatter except the template allows you to _order_ the layout of your keys. All other template information is ignored, only the order of your template keys matters. Example:
|
543
|
+
|
544
|
+
*Default Order*
|
425
545
|
|
426
546
|
[source,ruby]
|
427
547
|
----
|
428
|
-
# Default order
|
429
548
|
logger = Cogger.new formatter: :json
|
430
549
|
logger.info verb: "GET", path: "/"
|
431
550
|
|
432
551
|
# {"id":"console","severity":"INFO","at":"2023-04-10 09:03:55 -0600","verb":"GET","path":"/"}
|
552
|
+
----
|
433
553
|
|
434
|
-
|
554
|
+
*Custom Order*
|
555
|
+
|
556
|
+
[source,ruby]
|
557
|
+
----
|
435
558
|
logger = Cogger.new formatter: Cogger::Formatters::JSON.new("%<severity>s %<verb>s")
|
436
559
|
logger.info verb: "GET", path: "/"
|
437
560
|
|
@@ -440,18 +563,65 @@ logger.info verb: "GET", path: "/"
|
|
440
563
|
|
441
564
|
Your template can be a full or partial match of keys. If no keys match what is defined in the template, then the original order of the keys will be used instead.
|
442
565
|
|
443
|
-
|
566
|
+
You can always supply a message as your first argument -- or specify it by using the `:message` key -- but is removed if not supplied which is why the above doesn't print a message in the output. To illustrate, the following are equivalent:
|
567
|
+
|
568
|
+
[source,ruby]
|
569
|
+
----
|
570
|
+
logger = Cogger.new formatter: json
|
571
|
+
|
572
|
+
logger.info "Demo"
|
573
|
+
# {"id":"console","severity":"INFO","at":"2023-10-18 19:38:55 -0600","message":"Demo"}
|
574
|
+
|
575
|
+
logger.info messsage: "Demo"
|
576
|
+
{"id":"console","severity":"INFO","at":"2023-10-18 19:39:19 -0600","messsage":"Demo"}
|
577
|
+
----
|
578
|
+
|
579
|
+
When tags are provided, the `:tags` key will appear in the output depending on whether you are using _single tags_. If hash tags are used, they'll show up as additional attributes in the outout. Here's an example where a mix of single and hash keys are used:
|
580
|
+
|
581
|
+
[source,ruby]
|
582
|
+
----
|
583
|
+
logger = Cogger.new formatter: :json
|
584
|
+
|
585
|
+
logger.info "Demo", tags: ["WEB", "PRIMARY", {service: :api, demo: true}]
|
586
|
+
|
587
|
+
# {
|
588
|
+
# "id":"console",
|
589
|
+
# "severity":"INFO",
|
590
|
+
# "at":"2023-10-18 19:47:11 -0600",
|
591
|
+
# "message":"Demo",
|
592
|
+
# "tags":["WEB","PRIMARY"],
|
593
|
+
# "service":"api",
|
594
|
+
# "demo":true
|
595
|
+
# }
|
596
|
+
----
|
597
|
+
|
598
|
+
Notice, with the above, that the single tags of `WEB` and `PRIMARY` show up in the `tags` array while the `:service` and `:demo` keys show up at the top level of the hash. Since the `:tags`, `:service`, `:demo` keys are normal keys, like any key in your JSON output, this means you can use a custom template to arrange the order of these keys if you don't like the default.
|
599
|
+
|
600
|
+
==== Native
|
444
601
|
|
445
|
-
Should you wish to use the
|
602
|
+
Should you wish to use the native formatter as provided by original/native {logger_link}, it will work but not in the manner you might expect. Example:
|
446
603
|
|
447
604
|
[source,ruby]
|
448
605
|
----
|
449
606
|
require "logger"
|
450
607
|
|
451
608
|
logger = Cogger.new formatter: Logger::Formatter.new
|
452
|
-
logger.info "
|
609
|
+
logger.info "Demo"
|
610
|
+
|
611
|
+
# I, [2023-10-15T14:32:55.061777 #72801] INFO -- console: #<data Cogger::Entry id="console", severity=:info, at=2023-10-15 14:32:55.061734 -0600, message="Demo", tags=[], payload={}>
|
612
|
+
----
|
453
613
|
|
454
|
-
|
614
|
+
While the above doesn't cause an error, you only get a dump of the `Cogger::Entry` which is not what you want. To replicate native {logger_link} functionality, you can do use the simple formatter as follows to produce the rough equivalent:
|
615
|
+
|
616
|
+
[source,ruby]
|
617
|
+
----
|
618
|
+
formatter = Cogger::Formatters::Simple.new(
|
619
|
+
"%<severity>s, [%<at>s] %<severity>s -- %<id>s: %<message>s"
|
620
|
+
)
|
621
|
+
logger = Cogger.new(formatter:)
|
622
|
+
logger.info "Demo"
|
623
|
+
|
624
|
+
# INFO, [2023-10-15 15:07:13 -0600] INFO -- console: Demo
|
455
625
|
----
|
456
626
|
|
457
627
|
==== Custom
|
@@ -468,7 +638,7 @@ class MyFormatter
|
|
468
638
|
@sanitizer = sanitizer
|
469
639
|
end
|
470
640
|
|
471
|
-
def call(*
|
641
|
+
def call(*input) = "#{format template, sanitizer.call(*input)}\n"
|
472
642
|
|
473
643
|
private
|
474
644
|
|
@@ -476,47 +646,105 @@ class MyFormatter
|
|
476
646
|
end
|
477
647
|
----
|
478
648
|
|
479
|
-
There is no restriction on what dependency you might want to initialize your custom formatter with but -- as a bare minimum -- you'll want to provide a default template and inject the sanitizer which sanitizes the raw
|
649
|
+
There is no restriction on what dependency you might want to initialize your custom formatter with but -- as a bare minimum -- you'll want to provide a default template and inject the sanitizer which sanitizes the raw input into a `Cogger::Entry` object you can interact with in your implementation. The only other requirement is that you must implement `#call` which takes a log entry which is an array of positional arguments (i.e. `severity`, `at`, `id`, `entry`) and answers back a formatted string. If you need more examples you can look at any of the formatters provided within this gem.
|
480
650
|
|
481
|
-
===
|
651
|
+
=== Tags
|
482
652
|
|
483
|
-
|
653
|
+
Tags allow you to tag your messages at both a global and local (i.e. per message) level. For example, here's what tagging looks like when used globally:
|
484
654
|
|
485
655
|
[source,ruby]
|
486
656
|
----
|
487
|
-
Cogger.
|
657
|
+
logger = Cogger.new tags: %w[WEB]
|
658
|
+
logger.info "Demo"
|
488
659
|
|
489
|
-
# [
|
490
|
-
# :_csrf,
|
491
|
-
# :password,
|
492
|
-
# :password_confirmation
|
493
|
-
# ]
|
660
|
+
# 🟢 [WEB] Demo
|
494
661
|
----
|
495
662
|
|
496
|
-
|
663
|
+
Each tag is wrapped in brackets (i.e. `[]`) and you can use multiple tags:
|
664
|
+
|
665
|
+
[source,ruby]
|
666
|
+
----
|
667
|
+
logger = Cogger.new tags: %w[WEB EXAMPLE]
|
668
|
+
logger.info "Demo"
|
669
|
+
|
670
|
+
# 🟢 [WEB] [EXAMPLE] Demo
|
671
|
+
----
|
672
|
+
|
673
|
+
You are not limited to string-based tags. Any object will work:
|
674
|
+
|
675
|
+
|
676
|
+
[source,ruby]
|
677
|
+
----
|
678
|
+
logger = Cogger.new tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
|
679
|
+
logger.info "Demo"
|
680
|
+
|
681
|
+
# 🟢 [ONE] [two] [3] [FIVE] [four=FOUR] Demo
|
682
|
+
----
|
683
|
+
|
684
|
+
With the above, we have string, symbol, integer, hash, and proc tags. With hashes, you'll always get a the key/value pair formatted as: `key=value`. Procs/lambdas allow you to lazy evaluate your tag at time of logging which provides a powerful way to acquire the current process ID, thread ID, and so forth.
|
685
|
+
|
686
|
+
In addition to global tags, you can use local tags per log message. Example:
|
687
|
+
|
688
|
+
[source,ruby]
|
689
|
+
----
|
690
|
+
logger = Cogger.new
|
691
|
+
logger.info "Demo", tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
|
692
|
+
|
693
|
+
# 🟢 [ONE] [two] [3] [FIVE] [four=FOUR] Demo
|
694
|
+
----
|
695
|
+
|
696
|
+
You can also combine global and local tags:
|
697
|
+
|
698
|
+
[source,ruby]
|
699
|
+
----
|
700
|
+
logger = Cogger.new tags: ["ONE", :two]
|
701
|
+
logger.info "Demo", tags: [3, proc { "FOUR" }]
|
702
|
+
|
703
|
+
# 🟢 [ONE] [two] [3] [FOUR] Demo
|
704
|
+
----
|
705
|
+
|
706
|
+
As you can see, tags are highly versatile. That said, the following guidelines are worth consideration when using them:
|
707
|
+
|
708
|
+
* Prefer uppercase tag names to make them visually stand out.
|
709
|
+
* Prefer short names, ideally 1-4 characters since long tags defeat the purpose of brevity.
|
710
|
+
* Prefer consistent tag names by using tags that are not synonymous or ambiguous.
|
711
|
+
* Prefer using tags by feature rather than things like environments. Examples: API, DB, MAILER.
|
712
|
+
* Prefer the JSON formatter for structured metadata instead of tags. Logging JSON formatted messages with tags will work but sticking with a traditional hash, instead of tags, will probably serve you better.
|
713
|
+
|
714
|
+
=== Filters
|
715
|
+
|
716
|
+
Filters allow you to mask sensitive information you don't want showing up in your logs. The default is an empty set:
|
717
|
+
|
718
|
+
[source,ruby]
|
719
|
+
----
|
720
|
+
Cogger.filters # #<Set: {}>
|
721
|
+
----
|
722
|
+
|
723
|
+
To add filters, use:
|
497
724
|
|
498
725
|
[source,ruby]
|
499
726
|
----
|
500
727
|
Cogger.add_filter(:login)
|
501
728
|
.add_filter "email"
|
502
729
|
|
503
|
-
#
|
504
|
-
# :_csrf,
|
505
|
-
# :password,
|
506
|
-
# :password_confirmation,
|
507
|
-
# :login,
|
508
|
-
# :email
|
509
|
-
# ]
|
730
|
+
Cogger.filters # #<Set: {:login, :email}>
|
510
731
|
----
|
511
732
|
|
512
733
|
Symbols and strings can be used interchangeably but are stored as symbols since symbols are used when filtering log entries. Once your filters are in place, you can immediately see their effects:
|
513
734
|
|
514
735
|
[source,ruby]
|
515
736
|
----
|
737
|
+
Cogger.add_filter :password
|
516
738
|
logger = Cogger.new formatter: :json
|
517
739
|
logger.info login: "jayne", password: "secret"
|
518
740
|
|
519
|
-
# {
|
741
|
+
# {
|
742
|
+
# "id":"console",
|
743
|
+
# "severity":"INFO",
|
744
|
+
# "at":"2023-10-18 19:21:40 -0600",
|
745
|
+
# "login":"jayne",
|
746
|
+
# "password":"[FILTERED]"
|
747
|
+
# }
|
520
748
|
----
|
521
749
|
|
522
750
|
=== Streams
|
@@ -532,7 +760,7 @@ logger = Cogger.new
|
|
532
760
|
logger.info "Demo."
|
533
761
|
----
|
534
762
|
|
535
|
-
The above would log the `"Demo."` message to `$stdout`
|
763
|
+
The above would log the `"Demo."` message to `$stdout` -- the default stream -- to the `tmp/demo.log` file, and to `/dev/null`. All attributes used to construct your default logger apply to all additional streams unless customized further. This means any custom template/formatter can be applied to your streams. Example:
|
536
764
|
|
537
765
|
[source,ruby]
|
538
766
|
----
|
@@ -542,6 +770,25 @@ logger.info "Demo."
|
|
542
770
|
|
543
771
|
In this situation, you'd get colorized output to `$stdout` and JSON output to the `tmp/demo.log` file.
|
544
772
|
|
773
|
+
There is a lot you can do with streams. For example, if you wanted to experiment with the same message formatted by multiple formatters, you could add a stream per format. Example:
|
774
|
+
|
775
|
+
[source,ruby]
|
776
|
+
----
|
777
|
+
logger = Cogger.new
|
778
|
+
.add_stream(formatter: :color)
|
779
|
+
.add_stream(formatter: :detail)
|
780
|
+
.add_stream(formatter: :json)
|
781
|
+
.add_stream(formatter: :simple)
|
782
|
+
|
783
|
+
logger.info "Demo"
|
784
|
+
|
785
|
+
# 🟢 Demo
|
786
|
+
# Demo
|
787
|
+
# [console] [INFO] [2023-10-15 15:17:27 -0600] Demo
|
788
|
+
# {"id":"console","severity":"INFO","at":"2023-10-15 15:17:27 -0600","message":"Demo"}
|
789
|
+
# Demo
|
790
|
+
----
|
791
|
+
|
545
792
|
=== Defaults
|
546
793
|
|
547
794
|
Should you ever need quick access to the defaults, you can use:
|
data/cogger.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "cogger"
|
5
|
-
spec.version = "0.
|
5
|
+
spec.version = "0.13.0"
|
6
6
|
spec.authors = ["Brooke Kuhlmann"]
|
7
7
|
spec.email = ["brooke@alchemists.io"]
|
8
8
|
spec.homepage = "https://alchemists.io/projects/cogger"
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.cert_chain = [Gem.default_cert_path]
|
24
24
|
|
25
25
|
spec.required_ruby_version = "~> 3.2"
|
26
|
+
spec.add_dependency "core", "~> 0.1"
|
26
27
|
spec.add_dependency "refinements", "~> 11.0"
|
27
28
|
spec.add_dependency "tone", "~> 0.3"
|
28
29
|
spec.add_dependency "zeitwerk", "~> 2.6"
|
data/lib/cogger/configuration.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "core"
|
3
4
|
require "logger"
|
4
5
|
|
5
6
|
module Cogger
|
@@ -9,20 +10,24 @@ module Cogger
|
|
9
10
|
:io,
|
10
11
|
:level,
|
11
12
|
:formatter,
|
13
|
+
:tags,
|
12
14
|
:mode,
|
13
15
|
:age,
|
14
16
|
:size,
|
15
17
|
:suffix,
|
18
|
+
:entry,
|
16
19
|
:logger
|
17
20
|
) do
|
18
21
|
def initialize id: Program.call,
|
19
22
|
io: $stdout,
|
20
23
|
level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
|
21
|
-
formatter: Formatters::
|
24
|
+
formatter: Formatters::Emoji.new,
|
25
|
+
tags: Core::EMPTY_ARRAY,
|
22
26
|
mode: false,
|
23
27
|
age: 0,
|
24
28
|
size: 1_048_576,
|
25
29
|
suffix: "%Y-%m-%d",
|
30
|
+
entry: Entry,
|
26
31
|
logger: Logger
|
27
32
|
super
|
28
33
|
end
|
@@ -40,9 +45,9 @@ module Cogger
|
|
40
45
|
|
41
46
|
def inspect
|
42
47
|
"#<#{self.class} @id=#{id}, @io=#{io.class}, @level=#{level}, " \
|
43
|
-
"@formatter=#{formatter.class}, " \
|
48
|
+
"@formatter=#{formatter.class}, @tags=#{tags.inspect}, " \
|
44
49
|
"@mode=#{mode}, @age=#{age}, @size=#{size}, @suffix=#{suffix.inspect}, " \
|
45
|
-
"@logger=#{logger}>"
|
50
|
+
"@entry=#{entry}, @logger=#{logger}>"
|
46
51
|
end
|
47
52
|
end
|
48
53
|
end
|
data/lib/cogger/entry.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "core"
|
4
|
+
|
5
|
+
module Cogger
|
6
|
+
# Defines a log entry which can be formatted for output.
|
7
|
+
Entry = Data.define :id, :severity, :at, :message, :tags, :payload do
|
8
|
+
def self.for(message = nil, **payload)
|
9
|
+
new id: (payload.delete(:id) || Program.call),
|
10
|
+
severity: (payload.delete(:severity) || "INFO").upcase,
|
11
|
+
message: (block_given? ? yield : message),
|
12
|
+
tags: Array(payload.delete(:tags)),
|
13
|
+
payload:
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.for_crash message, error, id:
|
17
|
+
new id:,
|
18
|
+
severity: "FATAL",
|
19
|
+
message:,
|
20
|
+
payload: {
|
21
|
+
error_message: error.message,
|
22
|
+
error_class: error.class,
|
23
|
+
backtrace: error.backtrace
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize id: Program.call,
|
28
|
+
severity: "INFO",
|
29
|
+
at: Time.now,
|
30
|
+
message: nil,
|
31
|
+
tags: [],
|
32
|
+
payload: Core::EMPTY_HASH
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def attributes = {id:, severity:, at:, message:, **payload}
|
37
|
+
|
38
|
+
def tagged_attributes tagger: Tag
|
39
|
+
computed_tags = tagger.for(*tags)
|
40
|
+
|
41
|
+
return attributes if computed_tags.empty?
|
42
|
+
|
43
|
+
{id:, severity:, at:, message:, **computed_tags.to_h, **payload}
|
44
|
+
end
|
45
|
+
|
46
|
+
def tagged tagger: Tag
|
47
|
+
attributes.tap do |pairs|
|
48
|
+
computed_tags = tagger.for(*tags)
|
49
|
+
pairs[:message] = "#{computed_tags} #{pairs[:message]}" unless computed_tags.empty?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -11,8 +11,8 @@ module Cogger
|
|
11
11
|
@processor = processor
|
12
12
|
end
|
13
13
|
|
14
|
-
def call(*
|
15
|
-
updated_template, attributes = processor.call(template, *
|
14
|
+
def call(*input)
|
15
|
+
updated_template, attributes = processor.call(template, *input)
|
16
16
|
"#{format(updated_template, **attributes).tap(&:strip!)}\n"
|
17
17
|
end
|
18
18
|
|
@@ -16,8 +16,8 @@ module Cogger
|
|
16
16
|
@processor = processor
|
17
17
|
end
|
18
18
|
|
19
|
-
def call(*
|
20
|
-
updated_template, attributes = processor.call(template, *
|
19
|
+
def call(*input)
|
20
|
+
updated_template, attributes = processor.call(template, *input)
|
21
21
|
attributes[:backtrace] = %( #{attributes[:backtrace].join "\n "})
|
22
22
|
"#{format(updated_template, **attributes)}\n"
|
23
23
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cogger
|
4
|
+
module Formatters
|
5
|
+
# Formats by emoji and color.
|
6
|
+
class Emoji < Color
|
7
|
+
TEMPLATE = "%<emoji:dynamic>s %<message:dynamic>s"
|
8
|
+
|
9
|
+
def initialize(template = TEMPLATE, ...)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,30 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "core"
|
3
4
|
require "json"
|
4
5
|
|
5
6
|
module Cogger
|
6
7
|
module Formatters
|
7
8
|
# Formats as JSON output.
|
8
9
|
class JSON
|
9
|
-
TEMPLATE =
|
10
|
+
TEMPLATE = nil
|
10
11
|
|
11
12
|
def initialize template = TEMPLATE,
|
12
13
|
parser: Parsers::Individual.new,
|
13
|
-
sanitizer: Kit::Sanitizer
|
14
|
-
@
|
15
|
-
@parser = parser
|
14
|
+
sanitizer: Kit::Sanitizer
|
15
|
+
@positions = template ? parser.call(template).last.keys : Core::EMPTY_ARRAY
|
16
16
|
@sanitizer = sanitizer
|
17
17
|
end
|
18
18
|
|
19
|
-
def call(*
|
20
|
-
|
21
|
-
|
19
|
+
def call(*input)
|
20
|
+
attributes = sanitizer.call(*input).tagged_attributes.tap(&:compact!)
|
21
|
+
|
22
|
+
return "#{attributes.to_json}\n" if positions.empty?
|
23
|
+
|
22
24
|
"#{attributes.slice(*positions).merge!(attributes.except(*positions)).to_json}\n"
|
23
25
|
end
|
24
26
|
|
25
27
|
private
|
26
28
|
|
27
|
-
attr_reader :
|
29
|
+
attr_reader :positions, :sanitizer
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
@@ -3,34 +3,13 @@
|
|
3
3
|
module Cogger
|
4
4
|
module Formatters
|
5
5
|
module Kit
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
6
|
+
# Ensures log entry is filtered of sensitive data.
|
7
|
+
Sanitizer = lambda do |*input, filters: Cogger.filters|
|
8
|
+
*, entry = input
|
9
|
+
payload = entry.payload
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
severity, at, id, message = entry
|
15
|
-
|
16
|
-
attributes = if message.is_a? Hash
|
17
|
-
{id:, severity:, at:, message: nil, **message.except(:id, :severity, :at)}
|
18
|
-
else
|
19
|
-
{id:, severity:, at:, message:}
|
20
|
-
end
|
21
|
-
|
22
|
-
filter attributes
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
attr_reader :filters
|
28
|
-
|
29
|
-
# :reek:FeatureEnvy
|
30
|
-
def filter attributes
|
31
|
-
filters.each { |key| attributes[key] = "[FILTERED]" if attributes.key? key }
|
32
|
-
attributes
|
33
|
-
end
|
11
|
+
filters.each { |key| payload[key] = "[FILTERED]" if payload.key? key }
|
12
|
+
entry
|
34
13
|
end
|
35
14
|
end
|
36
15
|
end
|
@@ -45,7 +45,7 @@ module Cogger
|
|
45
45
|
# :reek:FeatureEnvy
|
46
46
|
# :reek:TooManyStatements
|
47
47
|
def sanitize_and_extract template, attributes
|
48
|
-
template.
|
48
|
+
template.gsub pattern do
|
49
49
|
captures = Regexp.last_match.named_captures
|
50
50
|
attributes[captures["key"].to_sym] = captures["directive"]
|
51
51
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "core"
|
4
|
+
|
3
5
|
module Cogger
|
4
6
|
module Formatters
|
5
7
|
module Parsers
|
@@ -32,7 +34,7 @@ module Cogger
|
|
32
34
|
def call template
|
33
35
|
return template unless template.match? pattern
|
34
36
|
|
35
|
-
[template.gsub(pattern,
|
37
|
+
[template.gsub(pattern, Core::EMPTY_STRING), template.match(pattern)[key]]
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
@@ -8,15 +8,15 @@ module Cogger
|
|
8
8
|
# Processes emojis and colors.
|
9
9
|
class Color
|
10
10
|
def initialize parser: Parsers::Dynamic.new,
|
11
|
-
kit: {sanitizer: Kit::Sanitizer
|
11
|
+
kit: {sanitizer: Kit::Sanitizer, colorizer: Kit::Colorizer},
|
12
12
|
registry: Cogger
|
13
13
|
@parser = parser
|
14
14
|
@kit = kit
|
15
15
|
@registry = registry
|
16
16
|
end
|
17
17
|
|
18
|
-
def call(template, *
|
19
|
-
attributes = sanitizer.call(*
|
18
|
+
def call(template, *input)
|
19
|
+
attributes = sanitizer.call(*input).tagged
|
20
20
|
|
21
21
|
case parser.call template
|
22
22
|
in [String => body, String => style] then universal body, style, **attributes
|
@@ -6,12 +6,12 @@ module Cogger
|
|
6
6
|
class Simple
|
7
7
|
TEMPLATE = "%<message>s"
|
8
8
|
|
9
|
-
def initialize template = TEMPLATE, sanitizer: Kit::Sanitizer
|
9
|
+
def initialize template = TEMPLATE, sanitizer: Kit::Sanitizer
|
10
10
|
@template = template
|
11
11
|
@sanitizer = sanitizer
|
12
12
|
end
|
13
13
|
|
14
|
-
def call(*
|
14
|
+
def call(*input) = "#{format(template, sanitizer.call(*input).tagged).tap(&:strip!)}\n"
|
15
15
|
|
16
16
|
private
|
17
17
|
|
data/lib/cogger/hub.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "forwardable"
|
3
4
|
require "logger"
|
4
5
|
require "refinements/hashes"
|
5
6
|
require "refinements/loggers"
|
@@ -8,12 +9,35 @@ module Cogger
|
|
8
9
|
# Loads configuration and simultaneously sends messages to multiple streams.
|
9
10
|
# :reek:TooManyInstanceVariables
|
10
11
|
class Hub
|
12
|
+
extend Forwardable
|
13
|
+
|
11
14
|
using Refinements::Loggers
|
12
15
|
using Refinements::Hashes
|
13
16
|
|
14
|
-
|
17
|
+
delegate %i[
|
18
|
+
close
|
19
|
+
reopen
|
20
|
+
debug!
|
21
|
+
debug?
|
22
|
+
info!
|
23
|
+
info?
|
24
|
+
warn!
|
25
|
+
warn?
|
26
|
+
error!
|
27
|
+
error?
|
28
|
+
fatal!
|
29
|
+
fatal?
|
30
|
+
formatter
|
31
|
+
formatter=
|
32
|
+
level
|
33
|
+
level=
|
34
|
+
] => :primary
|
35
|
+
|
36
|
+
delegate %i[id io tags mode age size suffix] => :configuration
|
37
|
+
|
38
|
+
def initialize(registry: Cogger, model: Configuration, **attributes)
|
15
39
|
@registry = registry
|
16
|
-
@configuration = model
|
40
|
+
@configuration = model[**find_formatter(attributes)]
|
17
41
|
@primary = configuration.to_logger
|
18
42
|
@streams = [@primary]
|
19
43
|
@mutex = Mutex.new
|
@@ -25,19 +49,23 @@ module Cogger
|
|
25
49
|
self
|
26
50
|
end
|
27
51
|
|
28
|
-
def debug(
|
52
|
+
def debug(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
53
|
+
|
54
|
+
def info(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
29
55
|
|
30
|
-
def
|
56
|
+
def warn(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
31
57
|
|
32
|
-
def
|
58
|
+
def error(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
33
59
|
|
34
|
-
def
|
60
|
+
def fatal(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
35
61
|
|
36
|
-
def
|
62
|
+
def any(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
37
63
|
|
38
|
-
def
|
64
|
+
def add(severity, message = nil, **payload, &)
|
65
|
+
log(Logger::SEV_LABEL.fetch(severity, "ANY").downcase, message, **payload, &)
|
66
|
+
end
|
39
67
|
|
40
|
-
alias any
|
68
|
+
alias unknown any
|
41
69
|
|
42
70
|
def reread = primary.reread
|
43
71
|
|
@@ -60,17 +88,30 @@ module Cogger
|
|
60
88
|
)
|
61
89
|
end
|
62
90
|
|
63
|
-
|
64
|
-
|
65
|
-
mutex.synchronize { streams.each { |logger| logger.public_send(severity, message, &) } }
|
66
|
-
true
|
91
|
+
def log(severity, message = nil, **payload, &)
|
92
|
+
dispatch(severity, message, **payload, &)
|
67
93
|
rescue StandardError => error
|
68
|
-
|
94
|
+
crash message, error
|
95
|
+
end
|
96
|
+
|
97
|
+
def dispatch(severity, message, **payload, &)
|
98
|
+
entry = configuration.entry.for(
|
99
|
+
message,
|
100
|
+
id: configuration.id,
|
101
|
+
severity:,
|
102
|
+
tags: configuration.tags + Array(payload.delete(:tags)),
|
103
|
+
**payload,
|
104
|
+
&
|
105
|
+
)
|
106
|
+
|
107
|
+
mutex.synchronize { streams.each { |logger| logger.public_send severity, entry } }
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
def crash message, error
|
112
|
+
configuration.with(id: :cogger, io: $stdout, formatter: Formatters::Crash.new)
|
69
113
|
.to_logger
|
70
|
-
.fatal message
|
71
|
-
error_message: error.message,
|
72
|
-
error_class: error.class,
|
73
|
-
backtrace: error.backtrace
|
114
|
+
.fatal configuration.entry.for_crash(message, error, id: configuration.id)
|
74
115
|
true
|
75
116
|
end
|
76
117
|
end
|
data/lib/cogger/registry.rb
CHANGED
@@ -18,20 +18,13 @@ module Cogger
|
|
18
18
|
.add_emoji(:error, "🛑")
|
19
19
|
.add_emoji(:fatal, "🔥")
|
20
20
|
.add_emoji(:any, "⚫️")
|
21
|
-
.add_filter(:_csrf)
|
22
|
-
.add_filter(:password)
|
23
|
-
.add_filter(:password_confirmation)
|
24
21
|
.add_formatter(:color, Cogger::Formatters::Color)
|
25
22
|
.add_formatter(
|
26
23
|
:detail,
|
27
24
|
Cogger::Formatters::Simple,
|
28
25
|
"[%<id>s] [%<severity>s] [%<at>s] %<message>s"
|
29
26
|
)
|
30
|
-
.add_formatter(
|
31
|
-
:emoji,
|
32
|
-
Cogger::Formatters::Color,
|
33
|
-
"%<emoji:dynamic>s %<message:dynamic>s"
|
34
|
-
)
|
27
|
+
.add_formatter(:emoji, Cogger::Formatters::Emoji)
|
35
28
|
.add_formatter(:json, Cogger::Formatters::JSON)
|
36
29
|
.add_formatter(:simple, Cogger::Formatters::Simple)
|
37
30
|
.add_formatter :rack,
|
data/lib/cogger/tag.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "core"
|
4
|
+
require "refinements/hashes"
|
5
|
+
|
6
|
+
module Cogger
|
7
|
+
# Models a tag which may consist of an array and/or hash.
|
8
|
+
Tag = Data.define :singles, :pairs do
|
9
|
+
using Refinements::Hashes
|
10
|
+
|
11
|
+
def self.for(*bag)
|
12
|
+
bag.each.with_object new do |item, tag|
|
13
|
+
value = item.is_a?(Proc) ? item.call : item
|
14
|
+
value.is_a?(Hash) ? tag.pairs.merge!(value) : tag.singles.append(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize singles: [], pairs: {}
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty? = singles.empty? && pairs.empty?
|
23
|
+
|
24
|
+
def to_h = empty? ? Core::EMPTY_HASH : {tags: singles.to_a, **pairs}.tap(&:compress!)
|
25
|
+
|
26
|
+
def to_s = empty? ? Core::EMPTY_STRING : "#{format_singles} #{format_pairs}".tap(&:strip!)
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def format_singles
|
31
|
+
singles.map { |value| "[#{value}]" }
|
32
|
+
.join " "
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_pairs
|
36
|
+
pairs.map { |key, value| "[#{key}=#{value}]" }
|
37
|
+
.join(" ")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/cogger.rb
CHANGED
@@ -15,10 +15,5 @@ module Cogger
|
|
15
15
|
|
16
16
|
def self.loader(registry = Zeitwerk::Registry) = registry.loader_for __FILE__
|
17
17
|
|
18
|
-
def self.init(...)
|
19
|
-
warn "#{self}##{__method__} is deprecated, use `.new` instead.", category: :deprecated
|
20
|
-
Client.new(...)
|
21
|
-
end
|
22
|
-
|
23
18
|
def self.new(...) = Hub.new(...)
|
24
19
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cogger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brooke Kuhlmann
|
@@ -35,8 +35,22 @@ cert_chain:
|
|
35
35
|
3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
|
36
36
|
gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2023-10-
|
38
|
+
date: 2023-10-19 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: core
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.1'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.1'
|
40
54
|
- !ruby/object:Gem::Dependency
|
41
55
|
name: refinements
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
@@ -92,10 +106,11 @@ files:
|
|
92
106
|
- README.adoc
|
93
107
|
- cogger.gemspec
|
94
108
|
- lib/cogger.rb
|
95
|
-
- lib/cogger/client.rb
|
96
109
|
- lib/cogger/configuration.rb
|
110
|
+
- lib/cogger/entry.rb
|
97
111
|
- lib/cogger/formatters/color.rb
|
98
112
|
- lib/cogger/formatters/crash.rb
|
113
|
+
- lib/cogger/formatters/emoji.rb
|
99
114
|
- lib/cogger/formatters/json.rb
|
100
115
|
- lib/cogger/formatters/kit/colorizer.rb
|
101
116
|
- lib/cogger/formatters/kit/sanitizer.rb
|
@@ -107,6 +122,7 @@ files:
|
|
107
122
|
- lib/cogger/hub.rb
|
108
123
|
- lib/cogger/program.rb
|
109
124
|
- lib/cogger/registry.rb
|
125
|
+
- lib/cogger/tag.rb
|
110
126
|
homepage: https://alchemists.io/projects/cogger
|
111
127
|
licenses:
|
112
128
|
- Hippocratic-2.1
|
@@ -133,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
149
|
- !ruby/object:Gem::Version
|
134
150
|
version: '0'
|
135
151
|
requirements: []
|
136
|
-
rubygems_version: 3.4.
|
152
|
+
rubygems_version: 3.4.21
|
137
153
|
signing_key:
|
138
154
|
specification_version: 4
|
139
155
|
summary: A customizable and feature rich logger.
|
metadata.gz.sig
CHANGED
Binary file
|
data/lib/cogger/client.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "forwardable"
|
4
|
-
require "logger"
|
5
|
-
require "refinements/loggers"
|
6
|
-
require "tone"
|
7
|
-
|
8
|
-
module Cogger
|
9
|
-
# Provides the primary client for colorized logging.
|
10
|
-
class Client
|
11
|
-
extend Forwardable
|
12
|
-
|
13
|
-
using Refinements::Loggers
|
14
|
-
|
15
|
-
delegate %i[formatter level progname debug info warn error fatal unknown] => :logger
|
16
|
-
|
17
|
-
# :reek:TooManyStatements
|
18
|
-
def initialize logger = Logger.new($stdout), color: Cogger.color, **attributes
|
19
|
-
warn "#{self.class}##{__method__} is deprecated, use `Cogger.new` instead.",
|
20
|
-
category: :deprecated
|
21
|
-
|
22
|
-
@logger = logger
|
23
|
-
@color = color
|
24
|
-
@attributes = attributes
|
25
|
-
|
26
|
-
configure
|
27
|
-
yield logger if block_given?
|
28
|
-
end
|
29
|
-
|
30
|
-
def any(...) = logger.unknown(...)
|
31
|
-
|
32
|
-
def reread = logger.reread
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
attr_reader :logger, :color, :attributes
|
37
|
-
|
38
|
-
# rubocop:disable Metrics/AbcSize
|
39
|
-
def configure
|
40
|
-
logger.datetime_format = attributes.fetch :datetime_format, logger.datetime_format
|
41
|
-
logger.level = attributes.fetch :level, default_level
|
42
|
-
logger.progname = attributes.fetch :progname, logger.progname
|
43
|
-
logger.formatter = attributes.fetch :formatter, default_formatter
|
44
|
-
end
|
45
|
-
# rubocop:enable Metrics/AbcSize
|
46
|
-
|
47
|
-
def default_level = logger.class.const_get ENV.fetch("LOG_LEVEL", "INFO")
|
48
|
-
|
49
|
-
def default_formatter
|
50
|
-
-> severity, _at, _name, message { "#{color[message, severity.downcase]}\n" }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|