cogger 0.11.0 → 0.12.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
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +179 -36
- data/cogger.gemspec +1 -1
- data/lib/cogger/configuration.rb +7 -3
- data/lib/cogger/entry.rb +51 -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 +5 -7
- data/lib/cogger/formatters/kit/sanitizer.rb +6 -27
- data/lib/cogger/formatters/parsers/individual.rb +1 -1
- data/lib/cogger/formatters/processors/color.rb +3 -3
- data/lib/cogger/formatters/simple.rb +2 -2
- data/lib/cogger/hub.rb +53 -18
- data/lib/cogger/registry.rb +1 -5
- data/lib/cogger/tag.rb +40 -0
- data/lib/cogger.rb +0 -5
- data.tar.gz.sig +0 -0
- metadata +5 -3
- 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: 5f8bcb6e5db91a1e5216dba5864806aead97bd32a9d8fcda1d59a4b22beb079c
|
4
|
+
data.tar.gz: a21a6b830568388316d651204f93f9209ba4694b00e6760a677c81224d1e1a3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae3d73862e4cbb63a98223023e77cde2b58cf53a4a5b6a94b7e972215629f47a81b956d196c20384386b31d6833ed33689943e01fb4fb4b60957c98794cfc381
|
7
|
+
data.tar.gz: 6d73ec6858638127a168388b23821e77abf7f196a49af51e184ba496084f81295316c148d4d7417d1d3e4b700cc079bc66695b392accc62be47d7cd6e23f3b45
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.adoc
CHANGED
@@ -21,6 +21,7 @@ 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
|
@@ -128,6 +129,7 @@ When creating a new logger, you can configure behavior via the following attribu
|
|
128
129
|
* `io`: The input/output stream. This can be `STDOUT/$stdout`, a file/path, or `nil`. Default: `$stdout`.
|
129
130
|
* `level`: The severity level you want to log at. Can be `:debug`, `:info`, `:warn`, `:error`, `:fatal`, or `:unknown`. Default: `:info`.
|
130
131
|
* `formatter`: The formatter to use for formatting your log output. Default: `Cogger::Formatter::Color`. See the _Formatters_ section for more info.
|
132
|
+
* `tags`: Global tagging for _every_ log entry which _must_ be an array of objects you wish to use for tagging purposes.
|
131
133
|
* `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
134
|
* `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
135
|
* `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,6 +146,8 @@ logger = Cogger.new
|
|
144
146
|
logger = Cogger.new id: :demo,
|
145
147
|
io: "demo.log",
|
146
148
|
level: :debug,
|
149
|
+
formatter: :json,
|
150
|
+
tags: %w[DEMO DB],
|
147
151
|
mode: false,
|
148
152
|
age: 5,
|
149
153
|
size: 1_000,
|
@@ -174,7 +178,7 @@ Templates are used by all formatters and adhere to {format_link} as used by `Ker
|
|
174
178
|
- 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
179
|
- Use of the `n$` flag is prohibited because this isn't compatible with the above.
|
176
180
|
|
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
|
181
|
+
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
182
|
|
179
183
|
[source,ruby]
|
180
184
|
----
|
@@ -213,7 +217,7 @@ At this point, you might have gathered that there are specific keys you can use
|
|
213
217
|
|
214
218
|
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
219
|
|
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:
|
220
|
+
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
221
|
|
218
222
|
[source,ruby]
|
219
223
|
----
|
@@ -247,35 +251,38 @@ Cogger.emojis
|
|
247
251
|
# }
|
248
252
|
----
|
249
253
|
|
250
|
-
|
254
|
+
The `:emoji` formatter is the default formatter which provides dynamic rendering of emojis based on severity level. Example:
|
251
255
|
|
252
256
|
[source,ruby]
|
253
257
|
----
|
254
|
-
Cogger.
|
255
|
-
|
258
|
+
logger = Cogger.new
|
259
|
+
logger.info "Demo"
|
260
|
+
|
261
|
+
# 🟢 Demo
|
256
262
|
----
|
257
263
|
|
258
|
-
|
264
|
+
To add one or more emojis, you can chain messages together when registering them:
|
259
265
|
|
260
266
|
[source,ruby]
|
261
267
|
----
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
# 🟢 demo
|
268
|
+
Cogger.add_emoji(:tada, "🎉")
|
269
|
+
.add_emoji :favorite, "❇️"
|
266
270
|
----
|
267
271
|
|
268
|
-
If you
|
272
|
+
If you always want to use the _same_ emoji, you could use the emoji formatter with a specific template:
|
269
273
|
|
270
274
|
[source,ruby]
|
271
275
|
----
|
272
|
-
logger = Cogger.new formatter: Cogger::Formatters::
|
273
|
-
logger.info "demo"
|
276
|
+
logger = Cogger.new formatter: Cogger::Formatters::Emoji.new("%<emoji:tada>s %<message:dynamic>s")
|
274
277
|
|
275
|
-
|
278
|
+
logger.info "Demo"
|
279
|
+
logger.warn "Demo"
|
280
|
+
|
281
|
+
# 🎉 Demo
|
282
|
+
# 🎉 Demo
|
276
283
|
----
|
277
284
|
|
278
|
-
|
285
|
+
As you can see, using a specific and non-dynamic emoji will _always_ display regardless of the current severity level.
|
279
286
|
|
280
287
|
=== Aliases
|
281
288
|
|
@@ -300,7 +307,7 @@ Aliases are a powerful way to customize your colors and use short syntax in your
|
|
300
307
|
"%<message:haze>"
|
301
308
|
----
|
302
309
|
|
303
|
-
|
310
|
+
💡 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
311
|
|
305
312
|
=== Formatters
|
306
313
|
|
@@ -320,8 +327,8 @@ Cogger.formatters
|
|
320
327
|
# "[%<id>s] [%<severity>s] [%<at>s] %<message>s"
|
321
328
|
# ],
|
322
329
|
# :emoji => [
|
323
|
-
# Cogger::Formatters::
|
324
|
-
#
|
330
|
+
# Cogger::Formatters::Emoji < Cogger::Formatters::Color,
|
331
|
+
# nil
|
325
332
|
# ],
|
326
333
|
# :json => [
|
327
334
|
# Cogger::Formatters::JSON < Object,
|
@@ -342,10 +349,10 @@ You can add a formatter by providing a key, class, and _optional_ template. If a
|
|
342
349
|
|
343
350
|
[source,ruby]
|
344
351
|
----
|
345
|
-
#
|
352
|
+
# Registration
|
346
353
|
Cogger.add_formatter :basic, Cogger::Formatters::Simple, "%<severity>s %<message>s"
|
347
354
|
|
348
|
-
#
|
355
|
+
# Usage
|
349
356
|
Cogger.get_formatter :basic
|
350
357
|
# [Cogger::Formatters::Simple, "%<severity>s %<message>s"]
|
351
358
|
----
|
@@ -375,18 +382,14 @@ logger = Cogger.new formatter: :rack
|
|
375
382
|
|
376
383
|
==== Color
|
377
384
|
|
378
|
-
The color formatter
|
385
|
+
The color formatter allows you to have color coded logs and can be configured as follows:
|
379
386
|
|
380
387
|
[source,ruby]
|
381
388
|
----
|
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")
|
389
|
+
logger = Cogger.new formatter: :color
|
385
390
|
----
|
386
391
|
|
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:
|
392
|
+
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
393
|
|
391
394
|
[source,ruby]
|
392
395
|
----
|
@@ -417,11 +420,56 @@ Once an alias is added, it can be immediately applied via the template of your f
|
|
417
420
|
logger = Cogger.new formatter: Cogger::Formatters::Color.new("<mystery>%<message>s</mystery>")
|
418
421
|
----
|
419
422
|
|
420
|
-
ℹ️ Much like the simple formatter, any leading or trailing whitespace is automatically after the template has been formatted.
|
423
|
+
ℹ️ Much like the simple formatter, any leading or trailing whitespace is automatically removed after the template has been formatted.
|
424
|
+
|
425
|
+
==== Emoji
|
426
|
+
|
427
|
+
The emoji formatter is enabled by default and is the equivalent of initializing with either of the following:
|
428
|
+
|
429
|
+
[source,ruby]
|
430
|
+
----
|
431
|
+
logger = Cogger.new
|
432
|
+
logger = Cogger.new formatter: :emoji
|
433
|
+
logger = Cogger.new formatter: Cogger::Formatters::Emoji.new("%<emoji:dynamic>s %<message:dynamic>s")
|
434
|
+
----
|
435
|
+
|
436
|
+
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:
|
437
|
+
|
438
|
+
[source,ruby]
|
439
|
+
----
|
440
|
+
Cogger.emojis
|
441
|
+
|
442
|
+
# {
|
443
|
+
# :debug => "🔎",
|
444
|
+
# :info => "🟢",
|
445
|
+
# :warn => "⚠️ ",
|
446
|
+
# :error => "🛑",
|
447
|
+
# :fatal => "🔥",
|
448
|
+
# :any => "⚫️"
|
449
|
+
# }
|
450
|
+
----
|
451
|
+
|
452
|
+
This allows an emoji to be dynamically applied based on log severity. You can add or modify aliases as follows:
|
453
|
+
|
454
|
+
[source,ruby]
|
455
|
+
----
|
456
|
+
Cogger.add_emoji :warn, "🟡"
|
457
|
+
----
|
458
|
+
|
459
|
+
Once an alias is added/updated, it can be immediately applied via the template of your formatter. Example:
|
460
|
+
|
461
|
+
[source,ruby]
|
462
|
+
----
|
463
|
+
logger = Cogger.new
|
464
|
+
logger.warn "Demo"
|
465
|
+
# 🟡 Demo
|
466
|
+
----
|
467
|
+
|
468
|
+
ℹ️ Much like the simple and color formatters, any leading or trailing whitespace is automatically removed after the template has been formatted.
|
421
469
|
|
422
470
|
==== JSON
|
423
471
|
|
424
|
-
This formatter is similar in behavior to the _simple_ formatter except the template allows you to
|
472
|
+
This formatter is similar in behavior to the _simple_ formatter except the template allows you to _order_ the layout of your keys. All other information is ignored. To use:
|
425
473
|
|
426
474
|
[source,ruby]
|
427
475
|
----
|
@@ -440,18 +488,31 @@ logger.info verb: "GET", path: "/"
|
|
440
488
|
|
441
489
|
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
490
|
|
443
|
-
====
|
491
|
+
==== Native
|
444
492
|
|
445
|
-
Should you wish to use the
|
493
|
+
Should you wish to use the native formatter as provided by original/native {logger_link}, it will work but not in the manner expected. Example:
|
446
494
|
|
447
495
|
[source,ruby]
|
448
496
|
----
|
449
497
|
require "logger"
|
450
498
|
|
451
499
|
logger = Cogger.new formatter: Logger::Formatter.new
|
452
|
-
logger.info "
|
500
|
+
logger.info "Demo"
|
501
|
+
|
502
|
+
# 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={}>
|
503
|
+
----
|
504
|
+
|
505
|
+
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:
|
506
|
+
|
507
|
+
[source,ruby]
|
508
|
+
----
|
509
|
+
formatter = Cogger::Formatters::Simple.new(
|
510
|
+
"%<severity>s, [%<at>s] %<severity>s -- %<id>s: %<message>s"
|
511
|
+
)
|
512
|
+
logger = Cogger.new(formatter:)
|
513
|
+
logger.info "Demo"
|
453
514
|
|
454
|
-
#
|
515
|
+
# INFO, [2023-10-15 15:07:13 -0600] INFO -- console: Demo
|
455
516
|
----
|
456
517
|
|
457
518
|
==== Custom
|
@@ -468,7 +529,7 @@ class MyFormatter
|
|
468
529
|
@sanitizer = sanitizer
|
469
530
|
end
|
470
531
|
|
471
|
-
def call(*
|
532
|
+
def call(*input) = "#{format template, sanitizer.call(*input)}\n"
|
472
533
|
|
473
534
|
private
|
474
535
|
|
@@ -476,7 +537,70 @@ class MyFormatter
|
|
476
537
|
end
|
477
538
|
----
|
478
539
|
|
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
|
540
|
+
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.
|
541
|
+
|
542
|
+
=== Tags
|
543
|
+
|
544
|
+
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:
|
545
|
+
|
546
|
+
[source,ruby]
|
547
|
+
----
|
548
|
+
logger = Cogger.new tags: %w[WEB]
|
549
|
+
logger.info "Demo"
|
550
|
+
|
551
|
+
# 🟢 [WEB] Demo
|
552
|
+
----
|
553
|
+
|
554
|
+
Each tag is wrapped in brackets (i.e. `[]`) and you can use multiple tags:
|
555
|
+
|
556
|
+
[source,ruby]
|
557
|
+
----
|
558
|
+
logger = Cogger.new tags: %w[WEB EXAMPLE]
|
559
|
+
logger.info "Demo"
|
560
|
+
|
561
|
+
# 🟢 [WEB] [EXAMPLE] Demo
|
562
|
+
----
|
563
|
+
|
564
|
+
You are not limited to string-based tags. Any object will work:
|
565
|
+
|
566
|
+
|
567
|
+
[source,ruby]
|
568
|
+
----
|
569
|
+
logger = Cogger.new tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
|
570
|
+
logger.info "Demo"
|
571
|
+
|
572
|
+
🟢 [ONE] [two] [3] [FIVE] [four=FOUR] Demo
|
573
|
+
----
|
574
|
+
|
575
|
+
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 thread ID, request ID, etc.
|
576
|
+
|
577
|
+
In addition to global tags, you can use local tags per log message. Example:
|
578
|
+
|
579
|
+
[source,ruby]
|
580
|
+
----
|
581
|
+
logger = Cogger.new
|
582
|
+
logger.info "Demo", tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
|
583
|
+
|
584
|
+
🟢 [ONE] [two] [3] [FIVE] [four=FOUR] Demo
|
585
|
+
----
|
586
|
+
|
587
|
+
You can also combine global and local tags:
|
588
|
+
|
589
|
+
[source,ruby]
|
590
|
+
----
|
591
|
+
logger = Cogger.new tags: ["ONE", :two]
|
592
|
+
logger.info "Demo", tags: [3, proc { "FOUR" }]
|
593
|
+
|
594
|
+
# 🟢 [ONE] [two] [3] [FOUR] Demo
|
595
|
+
----
|
596
|
+
|
597
|
+
As you can see, tags are highly versatile. That said, the following guidelines are worth consideration when using them:
|
598
|
+
|
599
|
+
* Prefer uppercase tag names to make them visually stand out.
|
600
|
+
* Prefer short names, ideally 1-4 characters since long tags defeat the purpose of brevity.
|
601
|
+
* Prefer consistent tag names by using tags that are not synonymous or ambiguous.
|
602
|
+
* Prefer using tags by feature rather than things like environments. Examples: API, DB, MAILER.
|
603
|
+
* 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.
|
480
604
|
|
481
605
|
=== Filters
|
482
606
|
|
@@ -532,7 +656,7 @@ logger = Cogger.new
|
|
532
656
|
logger.info "Demo."
|
533
657
|
----
|
534
658
|
|
535
|
-
The above would log the `"Demo."` message to `$stdout`
|
659
|
+
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
660
|
|
537
661
|
[source,ruby]
|
538
662
|
----
|
@@ -542,6 +666,25 @@ logger.info "Demo."
|
|
542
666
|
|
543
667
|
In this situation, you'd get colorized output to `$stdout` and JSON output to the `tmp/demo.log` file.
|
544
668
|
|
669
|
+
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:
|
670
|
+
|
671
|
+
[source,ruby]
|
672
|
+
----
|
673
|
+
cogger = Cogger.new
|
674
|
+
.add_stream(formatter: :color)
|
675
|
+
.add_stream(formatter: :detail)
|
676
|
+
.add_stream(formatter: :json)
|
677
|
+
.add_stream(formatter: :simple)
|
678
|
+
|
679
|
+
cogger.info "Demo"
|
680
|
+
|
681
|
+
# 🟢 Demo
|
682
|
+
# Demo
|
683
|
+
# [console] [INFO] [2023-10-15 15:17:27 -0600] Demo
|
684
|
+
# {"id":"console","severity":"INFO","at":"2023-10-15 15:17:27 -0600","message":"Demo"}
|
685
|
+
# Demo
|
686
|
+
----
|
687
|
+
|
545
688
|
=== Defaults
|
546
689
|
|
547
690
|
Should you ever need quick access to the defaults, you can use:
|
data/cogger.gemspec
CHANGED
data/lib/cogger/configuration.rb
CHANGED
@@ -9,20 +9,24 @@ module Cogger
|
|
9
9
|
:io,
|
10
10
|
:level,
|
11
11
|
:formatter,
|
12
|
+
:tags,
|
12
13
|
:mode,
|
13
14
|
:age,
|
14
15
|
:size,
|
15
16
|
:suffix,
|
17
|
+
:entry,
|
16
18
|
:logger
|
17
19
|
) do
|
18
20
|
def initialize id: Program.call,
|
19
21
|
io: $stdout,
|
20
22
|
level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
|
21
|
-
formatter: Formatters::
|
23
|
+
formatter: Formatters::Emoji.new,
|
24
|
+
tags: [],
|
22
25
|
mode: false,
|
23
26
|
age: 0,
|
24
27
|
size: 1_048_576,
|
25
28
|
suffix: "%Y-%m-%d",
|
29
|
+
entry: Entry,
|
26
30
|
logger: Logger
|
27
31
|
super
|
28
32
|
end
|
@@ -40,9 +44,9 @@ module Cogger
|
|
40
44
|
|
41
45
|
def inspect
|
42
46
|
"#<#{self.class} @id=#{id}, @io=#{io.class}, @level=#{level}, " \
|
43
|
-
"@formatter=#{formatter.class}, " \
|
47
|
+
"@formatter=#{formatter.class}, @tags=#{tags.inspect}, " \
|
44
48
|
"@mode=#{mode}, @age=#{age}, @size=#{size}, @suffix=#{suffix.inspect}, " \
|
45
|
-
"@logger=#{logger}>"
|
49
|
+
"@entry=#{entry}, @logger=#{logger}>"
|
46
50
|
end
|
47
51
|
end
|
48
52
|
end
|
data/lib/cogger/entry.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cogger
|
4
|
+
# Defines a log entry which can be formatted for output.
|
5
|
+
Entry = Data.define :id, :severity, :at, :message, :tags, :payload do
|
6
|
+
def self.for(message = nil, **payload)
|
7
|
+
new id: (payload.delete(:id) || Program.call),
|
8
|
+
severity: (payload.delete(:severity) || "INFO").upcase,
|
9
|
+
message: (block_given? ? yield : message),
|
10
|
+
tags: Array(payload.delete(:tags)),
|
11
|
+
payload:
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.for_crash message, error, id:
|
15
|
+
new id:,
|
16
|
+
severity: "FATAL",
|
17
|
+
message:,
|
18
|
+
payload: {
|
19
|
+
error_message: error.message,
|
20
|
+
error_class: error.class,
|
21
|
+
backtrace: error.backtrace
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize id: Program.call,
|
26
|
+
severity: "INFO",
|
27
|
+
at: Time.now,
|
28
|
+
message: nil,
|
29
|
+
tags: [],
|
30
|
+
payload: {}
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def attributes = {id:, severity:, at:, message:, **payload}
|
35
|
+
|
36
|
+
def tagged_attributes tagger: Tag
|
37
|
+
computed_tags = tagger.for(*tags)
|
38
|
+
|
39
|
+
return attributes if computed_tags.empty?
|
40
|
+
|
41
|
+
{id:, severity:, at:, message:, tags: computed_tags.to_a, **payload}
|
42
|
+
end
|
43
|
+
|
44
|
+
def tagged tagger: Tag
|
45
|
+
attributes.tap do |pairs|
|
46
|
+
computed_tags = tagger.for(*tags)
|
47
|
+
pairs[:message] = "#{computed_tags} #{pairs[:message]}" unless computed_tags.empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
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
|
@@ -10,21 +10,19 @@ module Cogger
|
|
10
10
|
|
11
11
|
def initialize template = TEMPLATE,
|
12
12
|
parser: Parsers::Individual.new,
|
13
|
-
sanitizer: Kit::Sanitizer
|
14
|
-
@
|
15
|
-
@parser = parser
|
13
|
+
sanitizer: Kit::Sanitizer
|
14
|
+
@positions = parser.call(template).last.keys
|
16
15
|
@sanitizer = sanitizer
|
17
16
|
end
|
18
17
|
|
19
|
-
def call(*
|
20
|
-
|
21
|
-
attributes = sanitizer.call(*entry).tap(&:compact!)
|
18
|
+
def call(*input)
|
19
|
+
attributes = sanitizer.call(*input).tagged_attributes.tap(&:compact!)
|
22
20
|
"#{attributes.slice(*positions).merge!(attributes.except(*positions)).to_json}\n"
|
23
21
|
end
|
24
22
|
|
25
23
|
private
|
26
24
|
|
27
|
-
attr_reader :
|
25
|
+
attr_reader :positions, :sanitizer
|
28
26
|
end
|
29
27
|
end
|
30
28
|
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
|
|
@@ -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,33 @@ 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
|
+
def initialize(registry: Cogger, model: Configuration, **attributes)
|
15
37
|
@registry = registry
|
16
|
-
@configuration = model
|
38
|
+
@configuration = model[**find_formatter(attributes)]
|
17
39
|
@primary = configuration.to_logger
|
18
40
|
@streams = [@primary]
|
19
41
|
@mutex = Mutex.new
|
@@ -25,19 +47,19 @@ module Cogger
|
|
25
47
|
self
|
26
48
|
end
|
27
49
|
|
28
|
-
def debug(
|
50
|
+
def debug(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
29
51
|
|
30
|
-
def info(
|
52
|
+
def info(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
31
53
|
|
32
|
-
def warn(
|
54
|
+
def warn(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
33
55
|
|
34
|
-
def error(
|
56
|
+
def error(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
35
57
|
|
36
|
-
def fatal(
|
58
|
+
def fatal(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
37
59
|
|
38
|
-
def
|
60
|
+
def any(message = nil, **payload, &) = log(__method__, message, **payload, &)
|
39
61
|
|
40
|
-
alias any
|
62
|
+
alias unknown any
|
41
63
|
|
42
64
|
def reread = primary.reread
|
43
65
|
|
@@ -60,17 +82,30 @@ module Cogger
|
|
60
82
|
)
|
61
83
|
end
|
62
84
|
|
63
|
-
|
64
|
-
|
65
|
-
mutex.synchronize { streams.each { |logger| logger.public_send(severity, message, &) } }
|
66
|
-
true
|
85
|
+
def log(severity, message, **payload, &)
|
86
|
+
dispatch(severity, message, **payload, &)
|
67
87
|
rescue StandardError => error
|
68
|
-
|
88
|
+
crash message, error
|
89
|
+
end
|
90
|
+
|
91
|
+
def dispatch(severity, message, **payload, &)
|
92
|
+
entry = configuration.entry.for(
|
93
|
+
message,
|
94
|
+
id: configuration.id,
|
95
|
+
severity:,
|
96
|
+
tags: configuration.tags + Array(payload.delete(:tags)),
|
97
|
+
**payload,
|
98
|
+
&
|
99
|
+
)
|
100
|
+
|
101
|
+
mutex.synchronize { streams.each { |logger| logger.public_send severity, entry } }
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
def crash message, error
|
106
|
+
configuration.with(id: :cogger, io: $stdout, formatter: Formatters::Crash.new)
|
69
107
|
.to_logger
|
70
|
-
.fatal message
|
71
|
-
error_message: error.message,
|
72
|
-
error_class: error.class,
|
73
|
-
backtrace: error.backtrace
|
108
|
+
.fatal configuration.entry.for_crash(message, error, id: configuration.id)
|
74
109
|
true
|
75
110
|
end
|
76
111
|
end
|
data/lib/cogger/registry.rb
CHANGED
@@ -27,11 +27,7 @@ module Cogger
|
|
27
27
|
Cogger::Formatters::Simple,
|
28
28
|
"[%<id>s] [%<severity>s] [%<at>s] %<message>s"
|
29
29
|
)
|
30
|
-
.add_formatter(
|
31
|
-
:emoji,
|
32
|
-
Cogger::Formatters::Color,
|
33
|
-
"%<emoji:dynamic>s %<message:dynamic>s"
|
34
|
-
)
|
30
|
+
.add_formatter(:emoji, Cogger::Formatters::Emoji)
|
35
31
|
.add_formatter(:json, Cogger::Formatters::JSON)
|
36
32
|
.add_formatter(:simple, Cogger::Formatters::Simple)
|
37
33
|
.add_formatter :rack,
|
data/lib/cogger/tag.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cogger
|
4
|
+
# Models a tag which may consist of an array and/or hash.
|
5
|
+
Tag = Data.define :singles, :pairs do
|
6
|
+
def self.for(*bag)
|
7
|
+
bag.each.with_object new do |item, tag|
|
8
|
+
value = item.is_a?(Proc) ? item.call : item
|
9
|
+
value.is_a?(Hash) ? tag.pairs.merge!(value) : tag.singles.append(value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize singles: [], pairs: {}
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty? = singles.empty? && pairs.empty?
|
18
|
+
|
19
|
+
def to_a
|
20
|
+
return [] if empty?
|
21
|
+
|
22
|
+
pairs.map { |key, value| "#{key}=#{value}" }
|
23
|
+
.prepend(*singles.map(&:to_s))
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s = empty? ? "" : "#{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.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brooke Kuhlmann
|
@@ -35,7 +35,7 @@ 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-15 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: refinements
|
@@ -92,10 +92,11 @@ files:
|
|
92
92
|
- README.adoc
|
93
93
|
- cogger.gemspec
|
94
94
|
- lib/cogger.rb
|
95
|
-
- lib/cogger/client.rb
|
96
95
|
- lib/cogger/configuration.rb
|
96
|
+
- lib/cogger/entry.rb
|
97
97
|
- lib/cogger/formatters/color.rb
|
98
98
|
- lib/cogger/formatters/crash.rb
|
99
|
+
- lib/cogger/formatters/emoji.rb
|
99
100
|
- lib/cogger/formatters/json.rb
|
100
101
|
- lib/cogger/formatters/kit/colorizer.rb
|
101
102
|
- lib/cogger/formatters/kit/sanitizer.rb
|
@@ -107,6 +108,7 @@ files:
|
|
107
108
|
- lib/cogger/hub.rb
|
108
109
|
- lib/cogger/program.rb
|
109
110
|
- lib/cogger/registry.rb
|
111
|
+
- lib/cogger/tag.rb
|
110
112
|
homepage: https://alchemists.io/projects/cogger
|
111
113
|
licenses:
|
112
114
|
- Hippocratic-2.1
|
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
|