philiprehberger-structured_logger 0.4.0 → 0.5.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: dc7adb741998f54fcb27d66e78322d0abcb77c6537298e8b8d9be58b999bf571
4
- data.tar.gz: bbeea6cf1479ffa7548be5ab32cdaca87dc42c8d5657a778e1e71574056ac4f1
3
+ metadata.gz: c8cf0e19aa5820ae2ee81a3cf73ece78e151e6a53f7fc48f8c74e94f9b7841ed
4
+ data.tar.gz: c7ab4e1bc11362adc93bfe54dcf06360b846a232d1d433c597a60f7d2e4a1fd0
5
5
  SHA512:
6
- metadata.gz: 67ac2a22e4a7b1b3397256fc39e9e04d11fe219b21096fd1b3c8e9996bb98f8120859baa378448d33cfe55425759c406aac72f17520582fcda4ad23e4171c4a2
7
- data.tar.gz: db854b806432b15b8b891592011f02ca39741975657ef1948d6fcfad6f5408e69101b7100b46007b03588216fd85cb997c7d91fbfa299d99dcff82c88fdb5ace
6
+ metadata.gz: d239e014b25dd71f821e97213b1ad979c2444d6cae3f4db330debec29e6f6a045b6dde4abc84e350f6cca3f384886fccc2430d413709131f58a6b81f8b09657e
7
+ data.tar.gz: 9a390ecf766ec22be918f9fb3a2daf5fcc8643cafe7848e82b72ce6fae7fc90c3cc176ba15da5dc2fb7f2544cf51730088851ec4fa801fe80ab70dfdd98eca7f
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-04-25
11
+
12
+ ### Added
13
+ - `Logger#with_tags(*tags)` for tagged context blocks with automatic restoration
14
+ - `Logger#measure_value(message)` variant of `#measure` that returns the block result while still emitting the timing entry
15
+
10
16
  ## [0.4.0] - 2026-04-17
11
17
 
12
18
  ### Added
data/README.md CHANGED
@@ -256,6 +256,40 @@ logger.close
256
256
 
257
257
  When the buffer is full, writes fall back to synchronous mode (backpressure) to avoid dropping log entries.
258
258
 
259
+ ### Tagged Context
260
+
261
+ Use `with_tags` to add tags to the logging context for the duration of a block. Tags merge with any existing tags (de-duplicated, preserving order) and the original context is restored on exit:
262
+
263
+ ```ruby
264
+ require "philiprehberger/structured_logger"
265
+
266
+ logger = Philiprehberger::StructuredLogger::Logger.new
267
+
268
+ logger.with_tags("auth", "request") do
269
+ logger.info("Login attempt")
270
+ # => {"timestamp":"...","level":"info","message":"Login attempt","tags":["auth","request"]}
271
+ end
272
+ # Tags are restored after the block
273
+ ```
274
+
275
+ ### Measuring with Return Value
276
+
277
+ Use `measure_value` when you need the block's result while still emitting a timing entry:
278
+
279
+ ```ruby
280
+ require "philiprehberger/structured_logger"
281
+
282
+ logger = Philiprehberger::StructuredLogger::Logger.new
283
+
284
+ result = logger.measure_value("db.query") do
285
+ # ...query the database...
286
+ [{ id: 1, name: "Alice" }]
287
+ end
288
+
289
+ # result == [{ id: 1, name: "Alice" }]
290
+ # => {"timestamp":"...","level":"info","message":"db.query","event":"db.query","duration_ms":12.345}
291
+ ```
292
+
259
293
  ## API
260
294
 
261
295
  ### `Philiprehberger::StructuredLogger::Logger`
@@ -275,6 +309,8 @@ When the buffer is full, writes fall back to synchronous mode (backpressure) to
275
309
  | `silence(level = :fatal, &block)` | Temporarily raise log level for a block |
276
310
  | `log_exception(exception, level: :error, **extra)` | Log exception details |
277
311
  | `measure(event_name, **context) { block }` | Time a block, emit an info event with `duration_ms`, and re-raise on failure |
312
+ | `#with_tags(*tags) { block }` | Add tags to context for the block |
313
+ | `#measure_value(message) { block }` | Like #measure but returns the block's value |
278
314
  | `add_output(io, level: nil, formatter: nil)` | Add an output destination at runtime |
279
315
  | `with_correlation_id(id = nil, &block)` | Set a correlation ID for the block |
280
316
  | `flush` | Force write of all buffered log entries |
@@ -50,6 +50,41 @@ module Philiprehberger
50
50
  end
51
51
  end
52
52
 
53
+ # Adds the given tags to the logger's context under the `:tags`
54
+ # key, merging with any existing tags (de-duplicated, preserving
55
+ # insertion order). When a block is given, the previous context is
56
+ # restored when the block exits (even on exception). Without a
57
+ # block, the change persists like {#with_context}.
58
+ #
59
+ # @param tags [Array<String, Symbol>] one or more tags to add.
60
+ # @yield (optional) executes within the tagged context; the
61
+ # original context is restored on exit.
62
+ # @return [Object, Hash] the block's return value when a block is
63
+ # given, otherwise the new merged context hash.
64
+ #
65
+ # @example Block form
66
+ # logger.with_tags('auth', 'request') do
67
+ # logger.info('Login attempt')
68
+ # # entry includes tags: ['auth', 'request']
69
+ # end
70
+ def with_tags(*tags)
71
+ existing = @context[:tags] || []
72
+ merged_tags = (existing + tags).uniq
73
+ if block_given?
74
+ @monitor.synchronize do
75
+ original = @context
76
+ @context = @context.merge(tags: merged_tags).freeze
77
+ yield
78
+ ensure
79
+ @context = original
80
+ end
81
+ else
82
+ @monitor.synchronize do
83
+ @context = @context.merge(tags: merged_tags).freeze
84
+ end
85
+ end
86
+ end
87
+
53
88
  def silence(temp_level = :fatal, &block)
54
89
  @monitor.synchronize do
55
90
  original = @level
@@ -111,6 +146,23 @@ module Philiprehberger
111
146
  end
112
147
  end
113
148
 
149
+ # Variant of {#measure} that emits the same timing log entry but
150
+ # also returns the block's return value. Captures and re-raises
151
+ # exceptions like {#measure}.
152
+ #
153
+ # @param event_name [String, Symbol] the event name to record as
154
+ # the `event` field in the log entry.
155
+ # @param context [Hash] extra context merged into the log entry.
156
+ # @yield executes the measured block.
157
+ # @return [Object] the block's return value on success.
158
+ # @raise re-raises any exception raised by the block.
159
+ #
160
+ # @example Capturing a query result
161
+ # result = logger.measure_value('db.query') { query_database }
162
+ def measure_value(event_name, **context, &)
163
+ measure(event_name, **context, &)
164
+ end
165
+
114
166
  def flush
115
167
  @monitor.synchronize { @outputs.each { |out| out[:io].flush if out[:io].respond_to?(:flush) } }
116
168
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module StructuredLogger
5
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-structured_logger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-18 00:00:00.000000000 Z
11
+ date: 2026-04-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A zero-dependency Ruby gem for structured JSON logging with context merging,
14
14
  child loggers, level filtering, and pluggable outputs.