philiprehberger-structured_logger 0.3.3 → 0.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: 50d320ddded4b27df7fd1288642a66633c791a208c902409758587319a5ed612
4
- data.tar.gz: 934b1119be3f20d97ea378643f7565be4d8033f4c3d2a9f16e20d1648b031307
3
+ metadata.gz: dc7adb741998f54fcb27d66e78322d0abcb77c6537298e8b8d9be58b999bf571
4
+ data.tar.gz: bbeea6cf1479ffa7548be5ab32cdaca87dc42c8d5657a778e1e71574056ac4f1
5
5
  SHA512:
6
- metadata.gz: 9a3f313e944d193a34245debfa06347340e0daec990911f672209a8a9221560d500db387bd8057ead52e13510c4552ebef2a4d35265d9a008d20c7f3276b0d13
7
- data.tar.gz: 73a5a960e9d8b8b0287b4380a7a2df5bbce8d108831015dbe9c109a68729b3b56d813830a14828c7fd0aea641b315f1d27d21373f93ab350042d9032ce9a54fa
6
+ metadata.gz: 67ac2a22e4a7b1b3397256fc39e9e04d11fe219b21096fd1b3c8e9996bb98f8120859baa378448d33cfe55425759c406aac72f17520582fcda4ad23e4171c4a2
7
+ data.tar.gz: db854b806432b15b8b891592011f02ca39741975657ef1948d6fcfad6f5408e69101b7100b46007b03588216fd85cb997c7d91fbfa299d99dcff82c88fdb5ace
data/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-04-17
11
+
12
+ ### Added
13
+ - `Logger#measure(event, **context) { block }` yields to the block, captures elapsed time, and emits a single info-level structured event with `duration_ms` (plus `error`/`error_class` on failure; original exception re-raised)
14
+
15
+ ## [0.3.5] - 2026-03-31
16
+
17
+ ### Added
18
+ - Add GitHub issue templates, dependabot config, and PR template
19
+
20
+ ## [0.3.4] - 2026-03-31
21
+
22
+ ### Changed
23
+ - Standardize README badges, support section, and license format
24
+
10
25
  ## [0.3.3] - 2026-03-26
11
26
 
12
27
  ### Fixed
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  [![Tests](https://github.com/philiprehberger/rb-structured-logger/actions/workflows/ci.yml/badge.svg)](https://github.com/philiprehberger/rb-structured-logger/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/philiprehberger-structured_logger.svg)](https://rubygems.org/gems/philiprehberger-structured_logger)
5
- [![License](https://img.shields.io/github/license/philiprehberger/rb-structured-logger)](LICENSE)
6
- [![Sponsor](https://img.shields.io/badge/sponsor-GitHub%20Sponsors-ec6cb9)](https://github.com/sponsors/philiprehberger)
5
+ [![Last updated](https://img.shields.io/github/last-commit/philiprehberger/rb-structured-logger)](https://github.com/philiprehberger/rb-structured-logger/commits/main)
7
6
 
8
7
  Structured JSON logger with context and child loggers
9
8
 
@@ -122,6 +121,23 @@ end
122
121
  logger.log_exception(e, level: :fatal, user_id: 42)
123
122
  ```
124
123
 
124
+ ### Timing a block
125
+
126
+ Use `measure` to time a block and emit a single structured event with `duration_ms`:
127
+
128
+ ```ruby
129
+ logger.measure("db.query", table: "users") { User.find(1) }
130
+ # => {"timestamp":"...","level":"info","message":"db.query","event":"db.query","table":"users","duration_ms":12.345}
131
+ ```
132
+
133
+ On failure, the original exception is re-raised and the log entry also includes `error` and `error_class`:
134
+
135
+ ```ruby
136
+ logger.measure("db.query") { raise "boom" }
137
+ # => {"timestamp":"...","level":"info","message":"db.query","event":"db.query","duration_ms":0.123,"error":"boom","error_class":"RuntimeError"}
138
+ # RuntimeError: boom
139
+ ```
140
+
125
141
  ### Multiple Outputs
126
142
 
127
143
  Log to multiple destinations simultaneously. Each output can have its own level filter and formatter:
@@ -258,6 +274,7 @@ When the buffer is full, writes fall back to synchronous mode (backpressure) to
258
274
  | `with_context(**extra, &block)` | Temporarily merge context for a block |
259
275
  | `silence(level = :fatal, &block)` | Temporarily raise log level for a block |
260
276
  | `log_exception(exception, level: :error, **extra)` | Log exception details |
277
+ | `measure(event_name, **context) { block }` | Time a block, emit an info event with `duration_ms`, and re-raise on failure |
261
278
  | `add_output(io, level: nil, formatter: nil)` | Add an output destination at runtime |
262
279
  | `with_correlation_id(id = nil, &block)` | Set a correlation ID for the block |
263
280
  | `flush` | Force write of all buffered log entries |
@@ -292,6 +309,24 @@ bundle exec rspec
292
309
  bundle exec rubocop
293
310
  ```
294
311
 
312
+ ## Support
313
+
314
+ If you find this project useful:
315
+
316
+ ⭐ [Star the repo](https://github.com/philiprehberger/rb-structured-logger)
317
+
318
+ 🐛 [Report issues](https://github.com/philiprehberger/rb-structured-logger/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
319
+
320
+ 💡 [Suggest features](https://github.com/philiprehberger/rb-structured-logger/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
321
+
322
+ ❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)
323
+
324
+ 🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)
325
+
326
+ 💻 [GitHub Profile](https://github.com/philiprehberger)
327
+
328
+ 🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)
329
+
295
330
  ## License
296
331
 
297
332
  [MIT](LICENSE)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "time"
3
+ require 'json'
4
+ require 'time'
5
5
 
6
6
  module Philiprehberger
7
7
  module StructuredLogger
@@ -44,7 +44,7 @@ module Philiprehberger
44
44
  context.each do |key, value|
45
45
  parts << "#{key}=#{value}"
46
46
  end
47
- parts.join(" ")
47
+ parts.join(' ')
48
48
  end
49
49
  end
50
50
 
@@ -54,7 +54,7 @@ module Philiprehberger
54
54
  when :text then TextFormatter.new
55
55
  when Proc then formatter
56
56
  else
57
- raise ArgumentError, "Invalid formatter" unless formatter.respond_to?(:call)
57
+ raise ArgumentError, 'Invalid formatter' unless formatter.respond_to?(:call)
58
58
 
59
59
  formatter
60
60
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "monitor"
4
- require "securerandom"
3
+ require 'monitor'
4
+ require 'securerandom'
5
5
 
6
6
  module Philiprehberger
7
7
  module StructuredLogger
@@ -76,6 +76,41 @@ module Philiprehberger
76
76
  **extra)
77
77
  end
78
78
 
79
+ # Yields to the given block, measures its monotonic wall-clock
80
+ # duration, and emits a single info-level log entry describing the
81
+ # outcome. On success, the block's return value is returned. On
82
+ # exception, the failure is logged and the original exception is
83
+ # re-raised.
84
+ #
85
+ # @param event_name [String, Symbol] the event name to record as
86
+ # the `event` field in the log entry.
87
+ # @param context [Hash] extra context merged into the log entry.
88
+ # @yield executes the measured block.
89
+ # @return [Object] the block's return value on success.
90
+ # @raise re-raises any exception raised by the block.
91
+ #
92
+ # @example Measuring a database query
93
+ # logger.measure('db.query', table: 'users') { User.find(1) }
94
+ # # logs event: 'db.query', table: 'users', duration_ms: 12.345
95
+ def measure(event_name, **context)
96
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
97
+ begin
98
+ result = yield
99
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0).round(3)
100
+ log(:info, event_name.to_s, event: event_name, duration_ms: duration_ms, **context)
101
+ result
102
+ rescue StandardError => e
103
+ duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0).round(3)
104
+ log(:info, event_name.to_s,
105
+ event: event_name,
106
+ duration_ms: duration_ms,
107
+ error: e.message,
108
+ error_class: e.class.name,
109
+ **context)
110
+ raise
111
+ end
112
+ end
113
+
79
114
  def flush
80
115
  @monitor.synchronize { @outputs.each { |out| out[:io].flush if out[:io].respond_to?(:flush) } }
81
116
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module StructuredLogger
5
- VERSION = "0.3.3"
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "structured_logger/version"
4
- require_relative "structured_logger/formatter"
5
- require_relative "structured_logger/async_writer"
6
- require_relative "structured_logger/logger"
3
+ require_relative 'structured_logger/version'
4
+ require_relative 'structured_logger/formatter'
5
+ require_relative 'structured_logger/async_writer'
6
+ require_relative 'structured_logger/logger'
7
7
 
8
8
  module Philiprehberger
9
9
  module StructuredLogger
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.3.3
4
+ version: 0.4.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-03-27 00:00:00.000000000 Z
11
+ date: 2026-04-18 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.
@@ -26,11 +26,11 @@ files:
26
26
  - lib/philiprehberger/structured_logger/formatter.rb
27
27
  - lib/philiprehberger/structured_logger/logger.rb
28
28
  - lib/philiprehberger/structured_logger/version.rb
29
- homepage: https://github.com/philiprehberger/rb-structured-logger
29
+ homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-structured_logger
30
30
  licenses:
31
31
  - MIT
32
32
  metadata:
33
- homepage_uri: https://github.com/philiprehberger/rb-structured-logger
33
+ homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-structured_logger
34
34
  source_code_uri: https://github.com/philiprehberger/rb-structured-logger
35
35
  changelog_uri: https://github.com/philiprehberger/rb-structured-logger/blob/main/CHANGELOG.md
36
36
  rubygems_mfa_required: 'true'