Module: Shipeasy::SDK::See
- Defined in:
- lib/shipeasy/sdk/see.rb
Defined Under Namespace
Modules: Context Classes: Built, Chain, ControlFlowChain, ControlFlowTail, Limiter, NullChain, Violation
Constant Summary collapse
- SEE_MAX_MESSAGE =
---- Limits (mirror core.ts; kept in sync with the worker's /collect) ----
500- SEE_MAX_STACK =
8000- SEE_MAX_SUBJECT =
used for subject, outcome, error_type
200- SEE_MAX_EXTRA_VALUE =
200- SEE_MAX_EXTRA_KEYS =
20- SEE_DEDUP_WINDOW_MS =
30_000- SEE_MAX_PER_PROCESS =
25- DEFAULT_SUBJECT =
Default consequence parts when a chain omits them.
"app".freeze
- DEFAULT_OUTCOME =
"hit an error".freeze
- EXPECTED_IVAR =
Marker attribute stamped onto an exception by control_flow_exception().
:@__shipeasy_see_expected
Class Method Summary collapse
-
.build_event(problem, subject, outcome, extras, sdk_version:, env:, backtrace_cleaner: nil) ⇒ Object
Build the type:"error" event accepted by POST /collect.
-
.clean_backtrace(bt, cleaner) ⇒ Object
Apply the framework-provided backtrace cleaner to a raw backtrace, returning an application-only frame array.
- .expected?(err) ⇒ Boolean
-
.mark_expected(err, because, extras = nil) ⇒ Object
Best-effort stamp marking an exception as expected control flow.
-
.sanitize_extras(extras) ⇒ Object
Drop nil values, keep only String/Numeric(finite)/boolean, truncate string values to 200 chars, cap at 20 keys (insertion order).
- .top_stack_line(stack) ⇒ Object
- .truncate(str, limit) ⇒ Object
Class Method Details
.build_event(problem, subject, outcome, extras, sdk_version:, env:, backtrace_cleaner: nil) ⇒ Object
Build the type:"error" event accepted by POST /collect.
backtrace_cleaner (optional) is a callable that takes the raw backtrace
array and returns an application-only subset (framework/gem frames
stripped) — e.g. ->(bt) { Rails.backtrace_cleaner.clean(bt) }. It is
applied before the stack is joined; if it strips every frame (a stack
that is purely framework/gem code) we fall back to the raw backtrace so
an error is never left with an empty stack. We never invent the filtering
rules — the callable is supplied by the framework's own cleaner.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/shipeasy/sdk/see.rb', line 134 def build_event(problem, subject, outcome, extras, sdk_version:, env:, backtrace_cleaner: nil) stack = nil if problem.is_a?(Violation) error_type = problem.name = problem.name kind = "violation" elsif problem.is_a?(Exception) error_type = problem.class.name || "Error" = (problem..to_s.empty? ? error_type : problem.) bt = clean_backtrace(problem.backtrace, backtrace_cleaner) stack = bt.join("\n") if bt && !bt.empty? kind = "caught" else error_type = "Error" = problem.to_s kind = "caught" end ev = { "type" => "error", "kind" => kind, "error_type" => truncate(error_type, SEE_MAX_SUBJECT), "message" => truncate(, SEE_MAX_MESSAGE), "subject" => truncate(subject, SEE_MAX_SUBJECT), "outcome" => truncate(outcome, SEE_MAX_SUBJECT), "side" => "server", "sdk_version" => sdk_version, "ts" => (Time.now.to_f * 1000).to_i, } ev["stack"] = truncate(stack, SEE_MAX_STACK) if stack clean = sanitize_extras(extras) ev["extras"] = clean if clean ev["env"] = env if env && !env.to_s.empty? ev end |
.clean_backtrace(bt, cleaner) ⇒ Object
Apply the framework-provided backtrace cleaner to a raw backtrace, returning an application-only frame array. No cleaner (or no backtrace) ⇒ the input is returned untouched. If the cleaner raises, or strips every frame, fall back to the raw backtrace — a cleaned-to-empty stack would lose all debugging signal for an error that lives entirely in framework code. Never raises.
177 178 179 180 181 182 183 184 |
# File 'lib/shipeasy/sdk/see.rb', line 177 def clean_backtrace(bt, cleaner) return bt if cleaner.nil? || bt.nil? || bt.empty? cleaned = cleaner.call(bt) cleaned && !cleaned.empty? ? cleaned : bt rescue StandardError bt end |
.expected?(err) ⇒ Boolean
106 107 108 109 110 111 |
# File 'lib/shipeasy/sdk/see.rb', line 106 def expected?(err) err.instance_variable_defined?(EXPECTED_IVAR) && !err.instance_variable_get(EXPECTED_IVAR).nil? rescue StandardError false end |
.mark_expected(err, because, extras = nil) ⇒ Object
Best-effort stamp marking an exception as expected control flow.
96 97 98 99 100 101 102 103 104 |
# File 'lib/shipeasy/sdk/see.rb', line 96 def mark_expected(err, because, extras = nil) mark = { "because" => because.to_s } clean = sanitize_extras(extras) mark["extras"] = clean if clean err.instance_variable_set(EXPECTED_IVAR, mark) rescue StandardError # Frozen / builtin objects that reject ivars: best effort only. nil end |
.sanitize_extras(extras) ⇒ Object
Drop nil values, keep only String/Numeric(finite)/boolean, truncate string values to 200 chars, cap at 20 keys (insertion order). Returns nil if nothing is kept. Keys are stringified.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/shipeasy/sdk/see.rb', line 69 def sanitize_extras(extras) return nil unless extras.is_a?(Hash) return nil if extras.empty? out = {} extras.each do |k, v| break if out.size >= SEE_MAX_EXTRA_KEYS next if v.nil? case v when true, false out[k.to_s] = v when String out[k.to_s] = truncate(v, SEE_MAX_EXTRA_VALUE) when Numeric # Reject NaN / Infinity (not representable in JSON). next if v.respond_to?(:finite?) && !v.finite? out[k.to_s] = v else next end end out.empty? ? nil : out end |
.top_stack_line(stack) ⇒ Object
221 222 223 224 225 226 227 228 229 |
# File 'lib/shipeasy/sdk/see.rb', line 221 def top_stack_line(stack) return "" if stack.nil? || stack.empty? stack.each_line do |line| s = line.strip return s[0, 200] if s.start_with?("File ") || s.start_with?("at ") || s.include?("line ") || s.include?(":in ") end "" end |
.truncate(str, limit) ⇒ Object
61 62 63 64 |
# File 'lib/shipeasy/sdk/see.rb', line 61 def truncate(str, limit) s = str.to_s s.length <= limit ? s : s[0, limit] end |