Class: Uncaught::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/uncaught/client.rb

Overview

The main SDK client. Captures errors and sends them through the transport pipeline.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Client

Returns a new instance of Client.

Parameters:



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/uncaught/client.rb', line 15

def initialize(config)
  @config = config
  @breadcrumbs = BreadcrumbStore.new(config.max_breadcrumbs || 20)
  @transport = Uncaught.create_transport(config)
  @rate_limiter = RateLimiter.new(
    global_max: config.max_events_per_minute || 30
  )
  @session_id = SecureRandom.uuid
  @seen_fingerprints = Set.new
  @user = nil
  @mutex = Mutex.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



12
13
14
# File 'lib/uncaught/client.rb', line 12

def config
  @config
end

Instance Method Details

#add_breadcrumb(type:, category:, message:, data: nil, level: nil) ⇒ Object

Add a breadcrumb to the ring buffer.

Parameters:

  • type (String)
  • category (String)
  • message (String)
  • data (Hash, nil) (defaults to: nil)
  • level (String, nil) (defaults to: nil)


142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/uncaught/client.rb', line 142

def add_breadcrumb(type:, category:, message:, data: nil, level: nil)
  return unless @config.enabled

  @breadcrumbs.add(
    type: type,
    category: category,
    message: message,
    data: data,
    level: level
  )
rescue => e
  debug_log("add_breadcrumb failed: #{e.message}")
end

#capture_error(error, request: nil, operation: nil, component_stack: nil, level: "error") ⇒ String?

Capture an error and send it through the transport pipeline.

Parameters:

  • error (Exception, String, Hash)

    The error to capture.

  • request (RequestInfo, nil) (defaults to: nil)
  • operation (OperationInfo, nil) (defaults to: nil)
  • component_stack (String, nil) (defaults to: nil)
  • level (String) (defaults to: "error")

    Severity level. Defaults to “error”.

Returns:

  • (String, nil)

    The event ID, or nil if the event was dropped.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/uncaught/client.rb', line 36

def capture_error(error, request: nil, operation: nil, component_stack: nil, level: "error")
  return nil unless @config.enabled

  # --- Normalise error ---
  error_info = normalise_error(error)
  error_info.component_stack = component_stack if component_stack

  # --- Check ignoreErrors ---
  if should_ignore?(error_info.message)
    debug_log("Event ignored by ignoreErrors filter")
    return nil
  end

  # --- Fingerprint ---
  fingerprint = Fingerprint.generate(
    type: error_info.type,
    message: error_info.message,
    stack: error_info.stack
  )

  # --- Rate limit ---
  unless @rate_limiter.should_allow?(fingerprint)
    debug_log("Rate-limited: #{fingerprint}")
    return nil
  end

  # --- Collect breadcrumbs ---
  crumbs = @breadcrumbs.get_all

  # --- Detect environment ---
  environment = EnvDetector.detect

  # Attach deployment environment from config
  environment.deploy = @config.environment if @config.environment
  environment.framework = @config.framework if @config.framework
  environment.framework_version = @config.framework_version if @config.framework_version

  # --- Build event ---
  event_id = SecureRandom.uuid
  event = UncaughtEvent.new(
    event_id: event_id,
    timestamp: Time.now.utc.iso8601(3),
    project_key: @config.project_key,
    level: level,
    fingerprint: fingerprint,
    release: @config.release,
    error: error_info,
    breadcrumbs: crumbs,
    request: request,
    operation: operation,
    environment: environment,
    user: ,
    fix_prompt: "",
    sdk: SdkInfo.new(name: SDK_NAME, version: VERSION)
  )

  # --- Sanitise ---
  event = Sanitizer.sanitize(event, @config.sanitize_keys)

  # --- Build fix prompt ---
  event.fix_prompt = PromptBuilder.build(event)

  # --- beforeSend hook ---
  if @config.before_send
    result = @config.before_send.call(event)
    if result.nil?
      debug_log("Event dropped by beforeSend")
      return nil
    end
    event = result
  end

  # --- Send ---
  @transport.send_event(event)
  debug_log("Captured event: #{event_id} (#{fingerprint})")

  # --- Track seen fingerprints ---
  @mutex.synchronize do
    @seen_fingerprints.add(fingerprint)
  end

  event_id
rescue => e
  debug_log("capture_error failed: #{e.message}")
  nil
end

#capture_message(message, level: "info") ⇒ String?

Capture a plain message (not backed by an Exception instance).

Parameters:

  • message (String)
  • level (String) (defaults to: "info")

    Defaults to “info”.

Returns:

  • (String, nil)

    The event ID, or nil if the event was dropped.



128
129
130
131
132
133
# File 'lib/uncaught/client.rb', line 128

def capture_message(message, level: "info")
  capture_error(RuntimeError.new(message), level: level)
rescue => e
  debug_log("capture_message failed: #{e.message}")
  nil
end

#flushObject

Flush all queued events to the transport.



178
179
180
181
182
# File 'lib/uncaught/client.rb', line 178

def flush
  @transport.flush
rescue => e
  debug_log("flush failed: #{e.message}")
end

#set_user(user) ⇒ Object

Set user context that will be attached to subsequent events.

Parameters:



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/uncaught/client.rb', line 159

def set_user(user)
  @mutex.synchronize do
    if user.nil?
      @user = nil
    elsif user.is_a?(UserInfo)
      @user = user.dup
    elsif user.is_a?(Hash)
      @user = UserInfo.new(
        id: user[:id] || user["id"],
        email: user[:email] || user["email"],
        username: user[:username] || user["username"]
      )
    end
  end
rescue => e
  debug_log("set_user failed: #{e.message}")
end