Class: Philiprehberger::StructuredLogger::Logger
- Inherits:
-
Object
- Object
- Philiprehberger::StructuredLogger::Logger
- Defined in:
- lib/philiprehberger/structured_logger/logger.rb
Constant Summary collapse
- LEVELS =
{ debug: 0, info: 1, warn: 2, error: 3, fatal: 4 }.freeze
- CORRELATION_ID_KEY =
:philiprehberger_structured_logger_correlation_id- BACKTRACE_LINE =
Regex matching a single Ruby backtrace line. Captures the file path, the line number, and (optionally) the method name. Handles both Ruby 3.4+ single-quote (‘’method’‘) and Ruby 3.3-and-earlier backtick-apostrophe (“ `method’ “) quoting.
/\A(?<file>.+?):(?<line>\d+)(?::in ['`](?<method>[^']+)')?\z/
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
-
#level ⇒ Object
Returns the value of attribute level.
Instance Method Summary collapse
- #add_output(io, level: nil, formatter: nil) ⇒ Object
- #child(**extra) ⇒ Object
- #close ⇒ Object
- #flush ⇒ Object
-
#initialize(**opts) ⇒ Logger
constructor
A new instance of Logger.
-
#log_exception(exception, level: :error, structured_backtrace: false, **extra) ⇒ void
Logs an exception’s message, class, and backtrace as a single structured entry.
-
#measure(event_name, **context) { ... } ⇒ Object
Yields to the given block, measures its monotonic wall-clock duration, and emits a single info-level log entry describing the outcome.
-
#measure_value(event_name, **context) { ... } ⇒ Object
Variant of #measure that emits the same timing log entry but also returns the block’s return value.
- #silence(temp_level = :fatal, &block) ⇒ Object
- #with_context(**extra, &block) ⇒ Object
- #with_correlation_id(id = nil, &block) ⇒ Object
-
#with_tags(*tags) {|optional| ... } ⇒ Object, Hash
Adds the given tags to the logger’s context under the ‘:tags` key, merging with any existing tags (de-duplicated, preserving insertion order).
Constructor Details
#initialize(**opts) ⇒ Logger
Returns a new instance of Logger.
21 22 23 24 25 26 27 28 29 30 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 21 def initialize(**opts) @level = opts.fetch(:level, :debug) @context = opts.fetch(:context, {}).freeze @sampling = opts.fetch(:sampling, {}) @async = opts.fetch(:async, false) @buffer_size = opts.fetch(:buffer_size, 1000) @monitor = Monitor.new @outputs = OutputBuilder.call(opts, @async, @buffer_size) end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
19 20 21 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 19 def context @context end |
#level ⇒ Object
Returns the value of attribute level.
19 20 21 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 19 def level @level end |
Instance Method Details
#add_output(io, level: nil, formatter: nil) ⇒ Object
37 38 39 40 41 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 37 def add_output(io, level: nil, formatter: nil) resolved = StructuredLogger.resolve_formatter(formatter) wrapped = @async ? AsyncWriter.new(io, buffer_size: @buffer_size) : io @monitor.synchronize { @outputs << { io: wrapped, level: level, formatter: resolved } } end |
#child(**extra) ⇒ Object
43 44 45 46 47 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 43 def child(**extra) clone = self.class.allocate clone.send(:initialize_child, @outputs, @level, @context.merge(extra), @sampling, @monitor) clone end |
#close ⇒ Object
207 208 209 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 207 def close @monitor.synchronize { @outputs.each { |out| out[:io].close if out[:io].is_a?(AsyncWriter) } } end |
#flush ⇒ Object
203 204 205 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 203 def flush @monitor.synchronize { @outputs.each { |out| out[:io].flush if out[:io].respond_to?(:flush) } } end |
#log_exception(exception, level: :error, structured_backtrace: false, **extra) ⇒ void
This method returns an undefined value.
Logs an exception’s message, class, and backtrace as a single structured entry.
142 143 144 145 146 147 148 149 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 142 def log_exception(exception, level: :error, structured_backtrace: false, **extra) bt = exception.backtrace || [] bt = parse_backtrace(bt) if structured_backtrace log(level, exception., error_class: exception.class.name, backtrace: bt, **extra) end |
#measure(event_name, **context) { ... } ⇒ Object
Yields to the given block, measures its monotonic wall-clock duration, and emits a single info-level log entry describing the outcome. On success, the block’s return value is returned. On exception, the failure is logged and the original exception is re-raised.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 167 def measure(event_name, **context) start = Process.clock_gettime(Process::CLOCK_MONOTONIC) begin result = yield duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0).round(3) log(:info, event_name.to_s, event: event_name, duration_ms: duration_ms, **context) result rescue StandardError => e duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000.0).round(3) log(:info, event_name.to_s, event: event_name, duration_ms: duration_ms, error: e., error_class: e.class.name, **context) raise end end |
#measure_value(event_name, **context) { ... } ⇒ Object
199 200 201 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 199 def measure_value(event_name, **context, &) measure(event_name, **context, &) end |
#silence(temp_level = :fatal, &block) ⇒ Object
94 95 96 97 98 99 100 101 102 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 94 def silence(temp_level = :fatal, &block) @monitor.synchronize do original = @level @level = temp_level block.call ensure @level = original end end |
#with_context(**extra, &block) ⇒ Object
49 50 51 52 53 54 55 56 57 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 49 def with_context(**extra, &block) @monitor.synchronize do original = @context @context = @context.merge(extra).freeze block.call ensure @context = original end end |
#with_correlation_id(id = nil, &block) ⇒ Object
104 105 106 107 108 109 110 111 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 104 def with_correlation_id(id = nil, &block) id ||= SecureRandom.uuid previous = Thread.current[CORRELATION_ID_KEY] Thread.current[CORRELATION_ID_KEY] = id block.call ensure Thread.current[CORRELATION_ID_KEY] = previous end |
#with_tags(*tags) {|optional| ... } ⇒ Object, Hash
Adds the given tags to the logger’s context under the ‘:tags` key, merging with any existing tags (de-duplicated, preserving insertion order). When a block is given, the previous context is restored when the block exits (even on exception). Without a block, the change persists like #with_context.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/philiprehberger/structured_logger/logger.rb', line 76 def (*) existing = @context[:tags] || [] = (existing + ).uniq if block_given? @monitor.synchronize do original = @context @context = @context.merge(tags: ).freeze yield ensure @context = original end else @monitor.synchronize do @context = @context.merge(tags: ).freeze end end end |