cogger 2.2.0 → 2.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c81fa9c3fdd3ce5ef1db012304946e95dd84c5c9087f5a82b6029f507f278c51
4
- data.tar.gz: fb40393d822afe21906b559bca98bdf602875b2d78ca38a843a58ad34154d4f6
3
+ metadata.gz: d3dd4c51c4410b817ca889b3c06abf51fcab7e6e91f5746caed31d1bf45f3eb7
4
+ data.tar.gz: aa1f55995d710f2147071f4eaf9709e625c1fdd809d255860a3113ea6d742064
5
5
  SHA512:
6
- metadata.gz: 1975f4568ca48b1d0f6851a66d255b43cc57c79849ab6edafc621f2705ffbe6ae116aad3cc76d024ab67cd5b17d431795fddda0de9083062876192176fe71d3a
7
- data.tar.gz: 7f0c08112e4510dd7120e5e2c4ecd8cb98a3a35fa13087ebd6252e9367d81574a46e44365697637a3b4bc2e9de9c6823b897ff8ecc1d13d06aef002ec947dd55
6
+ metadata.gz: 61b318f30b68581323296d09159df52721d5a4e7554ada7d7d6ac09416016d26dee20b83630aa708ddc1b02e43966ab0e1e98b3fb08c2243a769a840a7853f04
7
+ data.tar.gz: 4731b4c644c505603098c41b0f0ca5a3aa81b0d81c175dfe37c914cdbb5b06fb57619f4f1f154007dfafb331263262646ae50338db9eff930084b78072697209
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -142,6 +142,34 @@ logger = Cogger.new id: :demo,
142
142
  suffix: "%Y"
143
143
  ----
144
144
 
145
+ === Identity
146
+
147
+ Your logger's identity defaults to `$PROGRAM_NAME` -- as mentioned earlier -- but can be customized during initialization. Example:
148
+
149
+ [source,ruby]
150
+ ----
151
+ logger = Cogger.new id: :demo
152
+ logger.info "A demonstration."
153
+
154
+ # 🟢 [demo] A demonstration.
155
+ ----
156
+
157
+ You can also customize the identity by passing in an ID per log entry. Example:
158
+
159
+ [source,ruby]
160
+ ----
161
+ logger = Cogger.new
162
+
163
+ logger.info "A demonstration.", id: :other
164
+ # 🟢 [other] A demonstration.
165
+
166
+ logger.info(id: :other) { "A demonstration." }
167
+ # 🟢 [other] A demonstration.
168
+
169
+ logger.info { {id: :other, message: "A demonstration."} }
170
+ # 🟢 [other] A demonstration.
171
+ ----
172
+
145
173
  === Levels
146
174
 
147
175
  Supported levels can be obtained via `Cogger::LEVELS`. Example:
@@ -165,7 +193,26 @@ The above adheres to {rfc_3339_link} and can be customized -- as mentioned earli
165
193
 
166
194
  [source,ruby]
167
195
  ----
168
- Cogger.new datetime_format: "%Y-%m-%d"
196
+ logger = Cogger.new formatter: :json, datetime_format: "%Y-%m-%d"
197
+ logger.info "A demonstration."
198
+
199
+ # {"id":"console","level":"INFO","at":"2026-06-28","message":"A demonstration."}
200
+ ----
201
+
202
+ You can also customize the date/time format by passing in an date/time format per log entry. Example:
203
+
204
+ [source,ruby]
205
+ ----
206
+ logger = Cogger.new formatter: :json
207
+
208
+ logger.info "A demonstration.", datetime_format: "%Y-%m-%d"
209
+ # {"id":"console","level":"INFO","at":"2026-06-28","message":"A demonstration."}
210
+
211
+ logger.info(datetime_format: "%Y-%m-%d") { "A demonstration." }
212
+ # {"id":"console","level":"INFO","at":"2026-06-28","message":"A demonstration."}
213
+
214
+ logger.info { {datetime_format: "%Y-%m-%d", message: "A demonstration."} }
215
+ # {"id":"console","level":"INFO","at":"2026-06-28","message":"A demonstration."}
169
216
  ----
170
217
 
171
218
  === Environment
@@ -182,7 +229,7 @@ export LOG_LEVEL=fatal
182
229
  export LOG_LEVEL=unknown
183
230
  ----
184
231
 
185
- While downcase is preferred for each log level value, you can use upcased values as well. If the `LOG_LEVEL` environment variable is not set, `Cogger` will fall back to `"info"` unless overwritten during initialization. Example: `Cogger.new level: :debug`. Otherwise, an invalid log level will result in an `ArgumentError`.
232
+ While lower case is preferred for each log level value, you can use upper case values as well. If the `LOG_LEVEL` environment variable is not set, `Cogger` will fall back to `"info"` unless overwritten during initialization. Example: `Cogger.new level: :debug`. Otherwise, an invalid log level will result in an `ArgumentError`.
186
233
 
187
234
  === Mutations
188
235
 
@@ -457,7 +504,7 @@ Additional keys as provided by your message hash and/or tags can be customized a
457
504
  Template keys, emojis, and elements do have a few restrictions:
458
505
 
459
506
  * Use the special `emoji` key to provide dynamic or specific emoji logging.
460
- * Use the special `tags` key to provide tagged logging. More information on tags can be found later in this document.
507
+ * Use the special `tags` key to provide tagged logging. For more information, the _Tags_ section below.
461
508
  * Avoid supplying the same keys as the default keys. Example: `logger.info id: :bad, at: Time.now, level: :bogus`. This is because these keys will be ignored. In other words, you can't _override_ the default keys.
462
509
  * Avoid wrapping keys and/or emojis in elements because nesting isn't supported and can lead to strange output. Example: `<green>%<emoji:error>s %<id:dynamic>s</green>`.
463
510
  * Avoid wrapping elements within elements because nesting isn't supported and can lead to strange output. Example: `<dynamic><cyan>%<message>s</cyan></dynamic>`.
@@ -757,8 +804,7 @@ logger.info "Demo", tags: ["WEB", "PRIMARY", {service: :api, demo: true}]
757
804
  # "level":"INFO",
758
805
  # "at":"2023-12-10T18:44:32.723+00:00",
759
806
  # "message":"Demo",
760
- # "tags":["WEB",
761
- # "PRIMARY"],
807
+ # "tags":["WEB", "PRIMARY"],
762
808
  # "service":"api",
763
809
  # "demo":true
764
810
  # }
@@ -877,7 +923,7 @@ Tags allow you to tag your messages at both a global and local (i.e. per message
877
923
  logger = Cogger.new tags: %w[WEB]
878
924
  logger.info "Demo"
879
925
 
880
- # 🟢 [console] [WEB] Demo
926
+ # 🟢 [console] ["WEB"] Demo
881
927
  ----
882
928
 
883
929
  You can use multiple tags as well:
@@ -887,7 +933,7 @@ You can use multiple tags as well:
887
933
  logger = Cogger.new tags: %w[WEB EXAMPLE]
888
934
  logger.info "Demo"
889
935
 
890
- # 🟢 [console] [WEB] [EXAMPLE] Demo
936
+ # 🟢 [console] ["WEB"] ["EXAMPLE"] Demo
891
937
  ----
892
938
 
893
939
  You are not limited to string-based tags. Any object will work:
@@ -897,7 +943,7 @@ You are not limited to string-based tags. Any object will work:
897
943
  logger = Cogger.new tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
898
944
  logger.info "Demo"
899
945
 
900
- # 🟢 [console] [ONE] [two] [3] [FIVE] [four=FOUR] Demo
946
+ # 🟢 [console] ["ONE"] [:two] [3] ["FIVE"] [four="FOUR"] Demo
901
947
  ----
902
948
 
903
949
  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.
@@ -909,7 +955,7 @@ In addition to global tags, you can use local tags per log message. Example:
909
955
  logger = Cogger.new
910
956
  logger.info "Demo", tags: ["ONE", :two, 3, {four: "FOUR"}, proc { "FIVE" }]
911
957
 
912
- # 🟢 [console] [ONE] [two] [3] [FIVE] [four=FOUR] Demo
958
+ # 🟢 [console] ["ONE"] [:two] [3] ["FIVE"] [four="FOUR"] Demo
913
959
  ----
914
960
 
915
961
  You can also combine global and local tags:
@@ -919,16 +965,34 @@ You can also combine global and local tags:
919
965
  logger = Cogger.new tags: ["ONE", :two]
920
966
  logger.info "Demo", tags: [3, proc { "FOUR" }]
921
967
 
922
- # 🟢 [console] [ONE] [two] [3] [FOUR] Demo
968
+ # 🟢 [console] ["ONE"] [:two] [3] ["FOUR"] Demo
969
+ ----
970
+
971
+ When using blocks, use a hash inside the block instead of supplying the tags before using the block. Example:
972
+
973
+ [source,ruby]
974
+ ----
975
+ logger = Cogger.new level: :debug
976
+
977
+ # No
978
+ logger.debug(tags: ["ONE", :TWO]) { "Demo" }
979
+ 🔎 [console] ["ONE"] [:TWO] Demo
980
+
981
+ # Yes
982
+ logger.debug { {tags: ["ONE", :TWO], message: "Demo"} }
983
+ 🔎 [console] ["ONE"] [:TWO] Demo
923
984
  ----
924
985
 
986
+ This ensures your tags and associated message are only evaluated when the correct log level is accepted. This is especially important when dealing with tags that need additional evaluation like procs because you'll improve performance when the debug log level, in this example, isn't set.
987
+
925
988
  As you can see, tags are highly versatile. That said, the following guidelines are worth consideration when using them:
926
989
 
927
- * Prefer uppercase tag names to make them visually stand out.
928
- * Prefer short names, ideally 1-4 characters since long tags defeat the purpose of brevity.
929
- * Prefer consistent tag names by using tags that are not synonymous or ambiguous.
930
- * Prefer using tags by feature rather than things like environments. Examples: API, DB, MAILER.
931
- * 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.
990
+ * Prefer uppercase tags to make them visually stand out.
991
+ * Prefer short tags, ideally 1-4 characters since long tags defeat the purpose of brevity.
992
+ * Prefer consistent tags by using tags that are not synonymous or ambiguous.
993
+ * Prefer feature tags instead of environment tags. Examples: API, DB, MAILER.
994
+ * 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 serve you better.
995
+ * Avoid using `id`, `level`, `at`, and/or `message` as hash keys since those are reserved keys and will be rejected if supplied.
932
996
 
933
997
  === Filters
934
998
 
@@ -970,14 +1034,25 @@ You can add multiple log streams (outputs) by using:
970
1034
 
971
1035
  [source,ruby]
972
1036
  ----
1037
+ require "stringio"
1038
+
1039
+ buffer = StringIO.new
973
1040
  logger = Cogger.new
1041
+ .add_stream(io: buffer)
974
1042
  .add_stream(io: "tmp/demo.log")
975
1043
  .add_stream(io: nil)
976
1044
 
977
1045
  logger.info "Demo."
978
1046
  ----
979
1047
 
980
- 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:
1048
+ The above would log `"Demo."` to:
1049
+
1050
+ * To `$stdout` (the default stream which is your console).
1051
+ * To the StringIO `buffer` where you can use `buffer.string` to view content.
1052
+ * To the `tmp/demo.log` file where you can use `cat tmp/demo.log` to view content.
1053
+ * To `/dev/null` which immediately ignores all writes.
1054
+
1055
+ 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:
981
1056
 
982
1057
  [source,ruby]
983
1058
  ----
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 = "2.2.0"
5
+ spec.version = "2.4.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/cogger"
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_dependency "logger", "~> 1.7"
29
29
  spec.add_dependency "refinements", "~> 14.0"
30
30
  spec.add_dependency "tone", "~> 3.0"
31
- spec.add_dependency "zeitwerk", "~> 2.7"
31
+ spec.add_dependency "zeitwerk", "~> 2.8"
32
32
 
33
33
  spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
34
34
  spec.files = Dir["*.gemspec", "lib/**/*"]
data/lib/cogger/entry.rb CHANGED
@@ -30,7 +30,7 @@ module Cogger
30
30
 
31
31
  def self.sanitize! content, payload
32
32
  body = if content.is_a? Hash
33
- content.delete(:message).tap { payload.merge! content }
33
+ content.delete(:message).tap { payload.merge! content.except(:level, :at) }
34
34
  else
35
35
  content
36
36
  end
@@ -17,7 +17,8 @@ module Cogger
17
17
 
18
18
  def call(*input)
19
19
  *, entry = input
20
- attributes = sanitize(entry, :tagged_attributes).tap(&:compact!)
20
+ attributes = sanitize entry, :tagged_attributes
21
+ attributes.delete :message unless attributes[:message]
21
22
 
22
23
  parser.call(template, attributes).to_json << NEW_LINE
23
24
  end
@@ -16,7 +16,8 @@ module Cogger
16
16
 
17
17
  def call(*input)
18
18
  *, entry = input
19
- attributes = sanitize(entry, :tagged_attributes).tap(&:compact!)
19
+ attributes = sanitize entry, :tagged_attributes
20
+ attributes.delete :message unless attributes[:message]
20
21
 
21
22
  concat(attributes).chop! << NEW_LINE
22
23
  end
@@ -35,7 +35,7 @@ module Cogger
35
35
 
36
36
  attr_reader :pattern
37
37
 
38
- def dump(value) = value.to_s.gsub(pattern, &:dump)
38
+ def dump(value) = value ? value.to_s.gsub(pattern, &:dump) : value.inspect
39
39
  end
40
40
  end
41
41
  end
data/lib/cogger/hub.rb CHANGED
@@ -101,22 +101,23 @@ module Cogger
101
101
  crash message, error
102
102
  end
103
103
 
104
- # rubocop:todo Metrics/MethodLength
105
104
  def dispatch(level, message, **payload, &)
106
- entry = configuration.entry.for(
105
+ entry = build_entry(level, message, payload, &)
106
+ configuration.mutex.synchronize { streams.each { |logger| logger.public_send level, entry } }
107
+ true
108
+ end
109
+
110
+ def build_entry(level, message, payload, &)
111
+ configuration.entry.for(
107
112
  message,
108
113
  id: configuration.id,
109
114
  level:,
110
- tags: configuration.entag(payload.delete(:tags)),
115
+ tags: configuration.entag(payload[:tags]),
111
116
  datetime_format: configuration.datetime_format,
112
- **payload,
117
+ **payload.except(:level, :at, :tags),
113
118
  &
114
119
  )
115
-
116
- configuration.mutex.synchronize { streams.each { |logger| logger.public_send level, entry } }
117
- true
118
120
  end
119
- # rubocop:enable Metrics/MethodLength
120
121
 
121
122
  def crash message, error
122
123
  configuration.with(id: :cogger, io: $stdout, formatter: Formatters::Crash.new)
data/lib/cogger/tag.rb CHANGED
@@ -5,35 +5,45 @@ require "refinements/hash"
5
5
 
6
6
  module Cogger
7
7
  # Models a tag which may consist of an array and/or hash.
8
- Tag = Data.define :singles, :pairs do
8
+ Tag = Data.define :singles, :pairs, :exclusions do
9
9
  using Refinements::Hash
10
10
 
11
- def self.for(*bag)
12
- bag.each.with_object new do |item, tag|
11
+ def self.for(*bag) = new(**reduce(bag))
12
+
13
+ def self.reduce bag
14
+ bag.each.with_object({singles: [], pairs: {}}) do |item, all|
13
15
  value = item.is_a?(Proc) ? item.call : item
14
- value.is_a?(Hash) ? tag.pairs.merge!(value) : tag.singles.append(value)
16
+ value.is_a?(Hash) ? all[:pairs].merge!(value) : all[:singles].append(value)
15
17
  end
16
18
  end
17
19
 
18
- def initialize singles: [], pairs: {}
19
- super
20
+ private_class_method :reduce
21
+
22
+ def initialize singles: [], pairs: {}, exclusions: %w[id level at message]
23
+ filtered_pairs = pairs.reject { |key, _| exclusions.include? key.to_s }
24
+
25
+ super singles:, pairs: filtered_pairs, exclusions:
20
26
  end
21
27
 
22
28
  def empty? = singles.empty? && pairs.empty?
23
29
 
24
- def to_h = empty? ? Core::EMPTY_HASH : {tags: singles.to_a, **pairs}.tap(&:compress!)
30
+ def to_h
31
+ return Core::EMPTY_HASH if empty?
32
+
33
+ singles.empty? ? pairs : {tags: singles.to_a, **pairs}
34
+ end
25
35
 
26
36
  def to_s = empty? ? Core::EMPTY_STRING : "#{format_singles} #{format_pairs}".tap(&:strip!)
27
37
 
28
38
  private
29
39
 
30
40
  def format_singles
31
- singles.map { |value| "[#{value}]" }
41
+ singles.map { |value| "[#{value.inspect}]" }
32
42
  .join " "
33
43
  end
34
44
 
35
45
  def format_pairs
36
- pairs.map { |key, value| "[#{key}=#{value}]" }
46
+ pairs.map { |key, value| "[#{key}=#{value.inspect}]" }
37
47
  .join(" ")
38
48
  end
39
49
  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: 2.2.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -97,14 +97,14 @@ dependencies:
97
97
  requirements:
98
98
  - - "~>"
99
99
  - !ruby/object:Gem::Version
100
- version: '2.7'
100
+ version: '2.8'
101
101
  type: :runtime
102
102
  prerelease: false
103
103
  version_requirements: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - "~>"
106
106
  - !ruby/object:Gem::Version
107
- version: '2.7'
107
+ version: '2.8'
108
108
  email:
109
109
  - brooke@alchemists.io
110
110
  executables: []
@@ -175,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
175
175
  - !ruby/object:Gem::Version
176
176
  version: '0'
177
177
  requirements: []
178
- rubygems_version: 4.0.10
178
+ rubygems_version: 4.0.15
179
179
  specification_version: 4
180
180
  summary: A customizable and feature rich logger.
181
181
  test_files: []
metadata.gz.sig CHANGED
Binary file