Module: Logtide::StructuredException
- Defined in:
- lib/logtide/structured_exception.rb
Overview
Serialises a Ruby exception into the StructuredException wire shape (spec 003 section 4): type, message, language, stacktrace (outermost first), the cause chain (capped at 10, cycle-safe) and an optional raw trace.
Constant Summary collapse
- LANGUAGE =
"ruby"- MAX_CAUSE_DEPTH =
10- MAX_FRAMES =
100
Class Method Summary collapse
- .build(exception, depth, seen, include_stacktrace) ⇒ Object
- .follow_cause?(cause, depth, seen) ⇒ Boolean
- .frame(location) ⇒ Object
- .in_app?(path) ⇒ Boolean
- .message_of(exception) ⇒ Object
- .raw_text(exception) ⇒ Object
- .safe_cause(exception) ⇒ Object
- .serialize(exception, include_stacktrace: true) ⇒ Object
- .stacktrace(exception) ⇒ Object
- .type_of(exception) ⇒ Object
Class Method Details
.build(exception, depth, seen, include_stacktrace) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/logtide/structured_exception.rb', line 20 def build(exception, depth, seen, include_stacktrace) seen[exception] = true result = { "type" => type_of(exception), "message" => (exception), "language" => LANGUAGE } if include_stacktrace frames = stacktrace(exception) result["stacktrace"] = frames unless frames.empty? raw = raw_text(exception) result["raw"] = raw if raw end cause = safe_cause(exception) result["cause"] = build(cause, depth + 1, seen, include_stacktrace) if follow_cause?(cause, depth, seen) result end |
.follow_cause?(cause, depth, seen) ⇒ Boolean
83 84 85 |
# File 'lib/logtide/structured_exception.rb', line 83 def follow_cause?(cause, depth, seen) cause && depth < MAX_CAUSE_DEPTH && !seen.key?(cause) end |
.frame(location) ⇒ Object
60 61 62 63 64 65 66 67 68 |
# File 'lib/logtide/structured_exception.rb', line 60 def frame(location) path = location.absolute_path || location.path { "file" => path, "function" => location.label, "line" => location.lineno, "metadata" => { "in_app" => in_app?(path) } } end |
.in_app?(path) ⇒ Boolean
70 71 72 73 74 |
# File 'lib/logtide/structured_exception.rb', line 70 def in_app?(path) return false unless path !(path.include?("/gems/") || path.start_with?(RbConfig::CONFIG["rubylibdir"].to_s)) end |
.message_of(exception) ⇒ Object
46 47 48 49 |
# File 'lib/logtide/structured_exception.rb', line 46 def (exception) = exception..to_s .empty? ? type_of(exception) : end |
.raw_text(exception) ⇒ Object
76 77 78 79 80 81 |
# File 'lib/logtide/structured_exception.rb', line 76 def raw_text(exception) backtrace = exception.backtrace return nil unless backtrace (["#{type_of(exception)}: #{(exception)}"] + backtrace).join("\n") end |
.safe_cause(exception) ⇒ Object
87 88 89 90 91 92 |
# File 'lib/logtide/structured_exception.rb', line 87 def safe_cause(exception) cause = exception.cause cause if cause.is_a?(Exception) rescue StandardError nil end |
.serialize(exception, include_stacktrace: true) ⇒ Object
16 17 18 |
# File 'lib/logtide/structured_exception.rb', line 16 def serialize(exception, include_stacktrace: true) build(exception, 0, {}.compare_by_identity, include_stacktrace) end |
.stacktrace(exception) ⇒ Object
51 52 53 54 55 56 57 58 |
# File 'lib/logtide/structured_exception.rb', line 51 def stacktrace(exception) locations = exception.backtrace_locations return [] unless locations # Ruby backtraces are innermost-first; the wire format wants outermost # first, and truncation keeps the outermost frames (003 section 4). locations.reverse.first(MAX_FRAMES).map { |location| frame(location) } end |
.type_of(exception) ⇒ Object
41 42 43 44 |
# File 'lib/logtide/structured_exception.rb', line 41 def type_of(exception) name = exception.class.name name.nil? || name.empty? ? "Exception" : name end |