cogger 0.11.0 → 0.12.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 +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
|