Class: Findbug::Capture::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/findbug/capture/context.rb

Overview

Context stores request-scoped data that gets attached to errors.

THREAD-LOCAL STORAGE

In a multi-threaded server like Puma, multiple requests run concurrently. Each request needs its OWN context - we can’t share a global variable or Request A’s user would appear on Request B’s errors!

Solution: Thread.current - a hash specific to each thread.

Thread 1 (Request A):
  Context.set_user(id: 1)
  # Thread.current[:findbug_context] = { user: { id: 1 } }

Thread 2 (Request B):
  Context.set_user(id: 2)
  # Thread.current[:findbug_context] = { user: { id: 2 } }

Thread 1: Context.current[:user] → { id: 1 }  ✓ Correct!
Thread 2: Context.current[:user] → { id: 2 }  ✓ Correct!

WHAT GETS STORED?

  1. User - who was affected

  2. Tags - short key-value pairs for filtering

  3. Extra - arbitrary data about the request

  4. Breadcrumbs - trail of events before the error

  5. Request - HTTP request details (auto-captured)

Constant Summary collapse

THREAD_KEY =
:findbug_context
MAX_BREADCRUMBS =
50

Class Method Summary collapse

Class Method Details

.add_breadcrumb(breadcrumb) ⇒ Object

Add a breadcrumb

Breadcrumbs are a chronological trail of events leading to an error. Think of them like a log, but attached to the error.

Examples:

Context.add_breadcrumb(
  message: "User clicked checkout",
  category: "ui",
  data: { button: "checkout_btn" }
)

Parameters:

  • breadcrumb (Hash)

    breadcrumb data

Options Hash (breadcrumb):

  • :message (String)

    what happened

  • :category (String)

    grouping category

  • :data (Hash)

    additional data

  • :timestamp (String)

    when it happened



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/findbug/capture/context.rb', line 141

def add_breadcrumb(breadcrumb)
  crumbs = current[:breadcrumbs]

  # Add timestamp if not provided
  breadcrumb[:timestamp] ||= Time.now.utc.iso8601(3)

  crumbs << breadcrumb

  # Keep only the most recent breadcrumbs
  # This prevents memory issues from long-running requests
  crumbs.shift while crumbs.size > MAX_BREADCRUMBS
end

.add_tag(key, value) ⇒ Object

Add a tag

Tags are short key-value pairs optimized for filtering. Unlike extra data, tags are indexed and searchable.

Examples:

Context.add_tag(:environment, "production")
Context.add_tag(:plan, "enterprise")

Parameters:

  • key (String, Symbol)

    tag name

  • value (String, Numeric, Boolean)

    tag value



89
90
91
# File 'lib/findbug/capture/context.rb', line 89

def add_tag(key, value)
  current[:tags][key.to_sym] = value
end

Get all breadcrumbs

Returns:

  • (Array<Hash>)

    breadcrumbs in chronological order



158
159
160
# File 'lib/findbug/capture/context.rb', line 158

def breadcrumbs
  current[:breadcrumbs]
end

.clear!Object

Clear the context (call between requests)

This MUST be called after each request to prevent context leaking. The Railtie sets this up via after_action.



54
55
56
# File 'lib/findbug/capture/context.rb', line 54

def clear!
  Thread.current[THREAD_KEY] = nil
end

.currentHash

Get the current context hash

Returns:

  • (Hash)

    the current thread’s context



45
46
47
# File 'lib/findbug/capture/context.rb', line 45

def current
  Thread.current[THREAD_KEY] ||= default_context
end

.extraHash

Get extra data

Returns:

  • (Hash)

    current extra data



119
120
121
# File 'lib/findbug/capture/context.rb', line 119

def extra
  current[:extra]
end

.from_rack_request(rack_request) ⇒ Hash

Create context from a Rack request

This extracts useful information from the HTTP request. Called automatically by the middleware.

Parameters:

  • rack_request (Rack::Request)

    the Rack request object

Returns:

  • (Hash)

    extracted request data



202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/findbug/capture/context.rb', line 202

def from_rack_request(rack_request)
  {
    method: rack_request.request_method,
    url: rack_request.url,
    path: rack_request.path,
    query_string: scrub_query_string(rack_request.query_string),
    headers: scrub_headers(extract_headers(rack_request)),
    ip: rack_request.ip,
    user_agent: rack_request.user_agent,
    content_type: rack_request.content_type,
    content_length: rack_request.content_length,
    request_id: rack_request.env["action_dispatch.request_id"]
  }
end

.merge(data) ⇒ Object

Merge extra data into context

Extra data is arbitrary key-value pairs that provide more detail. Use this for non-indexed, detailed information.

Examples:

Context.merge(order_id: 456, cart_size: 3)

Parameters:

  • data (Hash)

    data to merge



111
112
113
# File 'lib/findbug/capture/context.rb', line 111

def merge(data)
  current[:extra].merge!(data)
end

.requestHash

Get request data

Returns:

  • (Hash)

    HTTP request information



174
175
176
# File 'lib/findbug/capture/context.rb', line 174

def request
  current[:request]
end

.set_request(request_data) ⇒ Object

Set request data (auto-populated by middleware)

Parameters:

  • request_data (Hash)

    HTTP request information



166
167
168
# File 'lib/findbug/capture/context.rb', line 166

def set_request(request_data)
  current[:request] = request_data
end

.set_user(user_data) ⇒ Object

Set user information

Examples:

Context.set_user(id: 123, email: "user@example.com")

Parameters:

  • user_data (Hash)

    user attributes (id, email, username, etc.)



65
66
67
# File 'lib/findbug/capture/context.rb', line 65

def set_user(user_data)
  current[:user] = scrub_user_data(user_data)
end

.tagsHash

Get all tags

Returns:

  • (Hash)

    current tags



97
98
99
# File 'lib/findbug/capture/context.rb', line 97

def tags
  current[:tags]
end

.to_hHash

Get the complete context for capturing

This returns all context data in a format ready for storage.

Returns:

  • (Hash)

    complete context



184
185
186
187
188
189
190
191
192
# File 'lib/findbug/capture/context.rb', line 184

def to_h
  ctx = current.dup
  ctx.compact! # Remove nil values

  # Convert breadcrumbs to array (it's already an array, but be explicit)
  ctx[:breadcrumbs] = ctx[:breadcrumbs].dup if ctx[:breadcrumbs]

  ctx
end

.userHash?

Get current user

Returns:

  • (Hash, nil)

    the current user data



73
74
75
# File 'lib/findbug/capture/context.rb', line 73

def user
  current[:user]
end