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.
132 133 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 |
# File 'lib/shipeasy/sdk/see.rb', line 132 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.
175 176 177 178 179 180 181 182 |
# File 'lib/shipeasy/sdk/see.rb', line 175 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
104 105 106 107 108 109 |
# File 'lib/shipeasy/sdk/see.rb', line 104 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.
94 95 96 97 98 99 100 101 102 |
# File 'lib/shipeasy/sdk/see.rb', line 94 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.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/shipeasy/sdk/see.rb', line 67 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
219 220 221 222 223 224 225 226 227 |
# File 'lib/shipeasy/sdk/see.rb', line 219 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
59 60 61 62 |
# File 'lib/shipeasy/sdk/see.rb', line 59 def truncate(str, limit) s = str.to_s s.length <= limit ? s : s[0, limit] end |