brainzlab 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +220 -4
- data/lib/brainzlab/beacon/client.rb +21 -1
- data/lib/brainzlab/configuration.rb +51 -2
- data/lib/brainzlab/cortex/client.rb +21 -1
- data/lib/brainzlab/debug.rb +305 -0
- data/lib/brainzlab/dendrite/client.rb +21 -1
- data/lib/brainzlab/development/logger.rb +150 -0
- data/lib/brainzlab/development/store.rb +121 -0
- data/lib/brainzlab/development.rb +72 -0
- data/lib/brainzlab/devtools/assets/devtools.css +326 -103
- data/lib/brainzlab/devtools/assets/devtools.js +79 -5
- data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +11 -0
- data/lib/brainzlab/errors.rb +490 -0
- data/lib/brainzlab/nerve/client.rb +21 -1
- data/lib/brainzlab/pulse/client.rb +66 -5
- data/lib/brainzlab/pulse.rb +17 -4
- data/lib/brainzlab/recall/client.rb +74 -6
- data/lib/brainzlab/recall.rb +19 -2
- data/lib/brainzlab/reflex/client.rb +66 -5
- data/lib/brainzlab/reflex.rb +40 -8
- data/lib/brainzlab/sentinel/client.rb +21 -1
- data/lib/brainzlab/synapse/client.rb +21 -1
- data/lib/brainzlab/testing/event_store.rb +377 -0
- data/lib/brainzlab/testing/helpers.rb +650 -0
- data/lib/brainzlab/testing/matchers.rb +391 -0
- data/lib/brainzlab/testing.rb +327 -0
- data/lib/brainzlab/utilities/circuit_breaker.rb +32 -3
- data/lib/brainzlab/vault/client.rb +21 -1
- data/lib/brainzlab/version.rb +1 -1
- data/lib/brainzlab/vision/client.rb +53 -6
- data/lib/brainzlab.rb +42 -0
- metadata +24 -1
|
@@ -84,20 +84,29 @@ module BrainzLab
|
|
|
84
84
|
|
|
85
85
|
def post(path, body)
|
|
86
86
|
uri = URI.join(@config.pulse_url, path)
|
|
87
|
+
|
|
88
|
+
# Call on_send callback if configured
|
|
89
|
+
invoke_on_send(:pulse, :post, path, body)
|
|
90
|
+
|
|
91
|
+
# Log debug output for request
|
|
92
|
+
log_debug_request(path, body)
|
|
93
|
+
|
|
87
94
|
request = Net::HTTP::Post.new(uri)
|
|
88
95
|
request['Content-Type'] = 'application/json'
|
|
89
96
|
request['Authorization'] = "Bearer #{@config.pulse_auth_key}"
|
|
90
97
|
request['User-Agent'] = "brainzlab-sdk-ruby/#{BrainzLab::VERSION}"
|
|
91
98
|
request.body = JSON.generate(body)
|
|
92
99
|
|
|
93
|
-
execute_with_retry(uri, request)
|
|
100
|
+
execute_with_retry(uri, request, path)
|
|
94
101
|
rescue StandardError => e
|
|
95
|
-
|
|
102
|
+
handle_error(e, context: { path: path, body_size: body.to_s.length })
|
|
96
103
|
nil
|
|
97
104
|
end
|
|
98
105
|
|
|
99
|
-
def execute_with_retry(uri, request)
|
|
106
|
+
def execute_with_retry(uri, request, path)
|
|
100
107
|
retries = 0
|
|
108
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
109
|
+
|
|
101
110
|
begin
|
|
102
111
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
103
112
|
http.use_ssl = uri.scheme == 'https'
|
|
@@ -105,6 +114,10 @@ module BrainzLab
|
|
|
105
114
|
http.read_timeout = 10
|
|
106
115
|
|
|
107
116
|
response = http.request(request)
|
|
117
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
118
|
+
|
|
119
|
+
# Log debug output for response
|
|
120
|
+
log_debug_response(response.code.to_i, duration_ms)
|
|
108
121
|
|
|
109
122
|
case response.code.to_i
|
|
110
123
|
when 200..299
|
|
@@ -116,7 +129,10 @@ module BrainzLab
|
|
|
116
129
|
when 429, 500..599
|
|
117
130
|
raise RetryableError, "Server error: #{response.code}"
|
|
118
131
|
else
|
|
119
|
-
|
|
132
|
+
handle_error(
|
|
133
|
+
StandardError.new("Pulse API error: #{response.code}"),
|
|
134
|
+
context: { path: path, status: response.code, body: response.body }
|
|
135
|
+
)
|
|
120
136
|
nil
|
|
121
137
|
end
|
|
122
138
|
rescue RetryableError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
@@ -125,12 +141,57 @@ module BrainzLab
|
|
|
125
141
|
sleep(RETRY_DELAY * retries)
|
|
126
142
|
retry
|
|
127
143
|
end
|
|
128
|
-
|
|
144
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
145
|
+
log_debug_response(0, duration_ms, error: e.message)
|
|
146
|
+
handle_error(e, context: { path: path, retries: retries })
|
|
129
147
|
nil
|
|
130
148
|
end
|
|
131
149
|
end
|
|
132
150
|
|
|
151
|
+
def log_debug_request(path, body)
|
|
152
|
+
return unless BrainzLab::Debug.enabled?
|
|
153
|
+
|
|
154
|
+
data = if body.is_a?(Hash) && body[:traces]
|
|
155
|
+
{ count: body[:traces].size }
|
|
156
|
+
elsif body.is_a?(Hash) && body[:name]
|
|
157
|
+
{ name: body[:name] }
|
|
158
|
+
else
|
|
159
|
+
{}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
BrainzLab::Debug.log_request(:pulse, 'POST', path, data: data)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def log_debug_response(status, duration_ms, error: nil)
|
|
166
|
+
return unless BrainzLab::Debug.enabled?
|
|
167
|
+
|
|
168
|
+
BrainzLab::Debug.log_response(:pulse, status, duration_ms, error: error)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def invoke_on_send(service, method, path, payload)
|
|
172
|
+
return unless @config.on_send
|
|
173
|
+
|
|
174
|
+
@config.on_send.call(service, method, path, payload)
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
# Don't let callback errors break the SDK
|
|
177
|
+
log_error("on_send callback error: #{e.message}")
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def handle_error(error, context: {})
|
|
181
|
+
log_error("#{error.message}")
|
|
182
|
+
|
|
183
|
+
# Call on_error callback if configured
|
|
184
|
+
return unless @config.on_error
|
|
185
|
+
|
|
186
|
+
@config.on_error.call(error, context.merge(service: :pulse))
|
|
187
|
+
rescue StandardError => e
|
|
188
|
+
# Don't let callback errors break the SDK
|
|
189
|
+
log_error("on_error callback error: #{e.message}")
|
|
190
|
+
end
|
|
191
|
+
|
|
133
192
|
def log_error(message)
|
|
193
|
+
BrainzLab::Debug.log(message, level: :error) if BrainzLab::Debug.enabled?
|
|
194
|
+
|
|
134
195
|
return unless @config.logger
|
|
135
196
|
|
|
136
197
|
@config.logger.error("[BrainzLab::Pulse] #{message}")
|
data/lib/brainzlab/pulse.rb
CHANGED
|
@@ -47,10 +47,17 @@ module BrainzLab
|
|
|
47
47
|
def record_trace(name, started_at:, ended_at:, kind: 'request', **attributes)
|
|
48
48
|
return unless enabled?
|
|
49
49
|
|
|
50
|
+
payload = build_trace_payload(name, kind, started_at, ended_at, attributes)
|
|
51
|
+
|
|
52
|
+
# In development mode, log locally instead of sending to server
|
|
53
|
+
if BrainzLab.configuration.development_mode?
|
|
54
|
+
Development.record(service: :pulse, event_type: 'trace', payload: payload)
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
50
58
|
ensure_provisioned!
|
|
51
59
|
return unless BrainzLab.configuration.pulse_valid?
|
|
52
60
|
|
|
53
|
-
payload = build_trace_payload(name, kind, started_at, ended_at, attributes)
|
|
54
61
|
client.send_trace(payload)
|
|
55
62
|
end
|
|
56
63
|
|
|
@@ -58,9 +65,6 @@ module BrainzLab
|
|
|
58
65
|
def record_metric(name, value:, kind: 'gauge', tags: {})
|
|
59
66
|
return unless enabled?
|
|
60
67
|
|
|
61
|
-
ensure_provisioned!
|
|
62
|
-
return unless BrainzLab.configuration.pulse_valid?
|
|
63
|
-
|
|
64
68
|
payload = {
|
|
65
69
|
name: name,
|
|
66
70
|
value: value,
|
|
@@ -69,6 +73,15 @@ module BrainzLab
|
|
|
69
73
|
tags: tags
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
# In development mode, log locally instead of sending to server
|
|
77
|
+
if BrainzLab.configuration.development_mode?
|
|
78
|
+
Development.record(service: :pulse, event_type: 'metric', payload: payload)
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
ensure_provisioned!
|
|
83
|
+
return unless BrainzLab.configuration.pulse_valid?
|
|
84
|
+
|
|
72
85
|
client.send_metric(payload)
|
|
73
86
|
end
|
|
74
87
|
|
|
@@ -32,20 +32,29 @@ module BrainzLab
|
|
|
32
32
|
|
|
33
33
|
def post(path, body)
|
|
34
34
|
uri = URI.join(@config.recall_url, path)
|
|
35
|
+
|
|
36
|
+
# Call on_send callback if configured
|
|
37
|
+
invoke_on_send(:recall, :post, path, body)
|
|
38
|
+
|
|
39
|
+
# Log debug output for request
|
|
40
|
+
log_debug_request(path, body)
|
|
41
|
+
|
|
35
42
|
request = Net::HTTP::Post.new(uri)
|
|
36
43
|
request['Content-Type'] = 'application/json'
|
|
37
44
|
request['Authorization'] = "Bearer #{@config.secret_key}"
|
|
38
45
|
request['User-Agent'] = "brainzlab-sdk-ruby/#{BrainzLab::VERSION}"
|
|
39
46
|
request.body = JSON.generate(body)
|
|
40
47
|
|
|
41
|
-
execute_with_retry(uri, request)
|
|
48
|
+
execute_with_retry(uri, request, path)
|
|
42
49
|
rescue StandardError => e
|
|
43
|
-
|
|
50
|
+
handle_error(e, context: { path: path, body_size: body.to_s.length })
|
|
44
51
|
nil
|
|
45
52
|
end
|
|
46
53
|
|
|
47
|
-
def execute_with_retry(uri, request)
|
|
54
|
+
def execute_with_retry(uri, request, path)
|
|
48
55
|
retries = 0
|
|
56
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
57
|
+
|
|
49
58
|
begin
|
|
50
59
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
51
60
|
http.use_ssl = uri.scheme == 'https'
|
|
@@ -53,6 +62,10 @@ module BrainzLab
|
|
|
53
62
|
http.read_timeout = 10
|
|
54
63
|
|
|
55
64
|
response = http.request(request)
|
|
65
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
66
|
+
|
|
67
|
+
# Log debug output for response
|
|
68
|
+
log_debug_response(response.code.to_i, duration_ms)
|
|
56
69
|
|
|
57
70
|
case response.code.to_i
|
|
58
71
|
when 200..299
|
|
@@ -64,7 +77,10 @@ module BrainzLab
|
|
|
64
77
|
when 429, 500..599
|
|
65
78
|
raise RetryableError, "Server error: #{response.code}"
|
|
66
79
|
else
|
|
67
|
-
|
|
80
|
+
handle_error(
|
|
81
|
+
StandardError.new("Recall API error: #{response.code}"),
|
|
82
|
+
context: { path: path, status: response.code, body: response.body }
|
|
83
|
+
)
|
|
68
84
|
nil
|
|
69
85
|
end
|
|
70
86
|
rescue RetryableError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
@@ -73,15 +89,67 @@ module BrainzLab
|
|
|
73
89
|
sleep(RETRY_DELAY * retries)
|
|
74
90
|
retry
|
|
75
91
|
end
|
|
76
|
-
|
|
92
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
93
|
+
log_debug_response(0, duration_ms, error: e.message)
|
|
94
|
+
handle_error(e, context: { path: path, retries: retries })
|
|
77
95
|
nil
|
|
78
96
|
end
|
|
79
97
|
end
|
|
80
98
|
|
|
99
|
+
def log_debug_request(path, body)
|
|
100
|
+
return unless BrainzLab::Debug.enabled?
|
|
101
|
+
|
|
102
|
+
data = if body.is_a?(Hash) && body[:logs]
|
|
103
|
+
{ count: body[:logs].size }
|
|
104
|
+
elsif body.is_a?(Hash) && body[:message]
|
|
105
|
+
{ message: body[:message] }
|
|
106
|
+
else
|
|
107
|
+
{}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
BrainzLab::Debug.log_request(:recall, 'POST', path, data: data)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def log_debug_response(status, duration_ms, error: nil)
|
|
114
|
+
return unless BrainzLab::Debug.enabled?
|
|
115
|
+
|
|
116
|
+
BrainzLab::Debug.log_response(:recall, status, duration_ms, error: error)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def invoke_on_send(service, method, path, payload)
|
|
120
|
+
return unless @config.on_send
|
|
121
|
+
|
|
122
|
+
@config.on_send.call(service, method, path, payload)
|
|
123
|
+
rescue StandardError => e
|
|
124
|
+
# Don't let callback errors break the SDK
|
|
125
|
+
log_error("on_send callback error: #{e.message}")
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def handle_error(error, context: {})
|
|
129
|
+
# Wrap the error in a structured error if it's not already one
|
|
130
|
+
structured_error = if error.is_a?(BrainzLab::Error)
|
|
131
|
+
error
|
|
132
|
+
else
|
|
133
|
+
ErrorHandler.wrap(error, service: 'Recall', operation: context[:path] || 'unknown')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
log_error(structured_error.message)
|
|
137
|
+
|
|
138
|
+
# Call on_error callback if configured
|
|
139
|
+
return unless @config.on_error
|
|
140
|
+
|
|
141
|
+
@config.on_error.call(structured_error, context.merge(service: :recall))
|
|
142
|
+
rescue StandardError => e
|
|
143
|
+
# Don't let callback errors break the SDK
|
|
144
|
+
log_error("on_error callback error: #{e.message}")
|
|
145
|
+
end
|
|
146
|
+
|
|
81
147
|
def log_error(message)
|
|
148
|
+
BrainzLab::Debug.log(message, level: :error) if BrainzLab::Debug.enabled?
|
|
149
|
+
|
|
82
150
|
return unless @config.logger
|
|
83
151
|
|
|
84
|
-
@config.logger.error("[BrainzLab] #{message}")
|
|
152
|
+
@config.logger.error("[BrainzLab::Recall] #{message}")
|
|
85
153
|
end
|
|
86
154
|
|
|
87
155
|
class RetryableError < StandardError; end
|
data/lib/brainzlab/recall.rb
CHANGED
|
@@ -31,14 +31,24 @@ module BrainzLab
|
|
|
31
31
|
def log(level, message, **data)
|
|
32
32
|
config = BrainzLab.configuration
|
|
33
33
|
return unless config.recall_effectively_enabled?
|
|
34
|
+
return unless config.level_enabled?(level)
|
|
35
|
+
|
|
36
|
+
entry = build_entry(level, message, data)
|
|
37
|
+
|
|
38
|
+
# Log debug output for the operation
|
|
39
|
+
log_debug_operation(level, message, data)
|
|
40
|
+
|
|
41
|
+
# In development mode, log locally instead of sending to server
|
|
42
|
+
if config.development_mode?
|
|
43
|
+
Development.record(service: :recall, event_type: 'log', payload: entry)
|
|
44
|
+
return
|
|
45
|
+
end
|
|
34
46
|
|
|
35
47
|
# Auto-provision project on first log if app_name is configured
|
|
36
48
|
ensure_provisioned!
|
|
37
49
|
|
|
38
|
-
return unless config.level_enabled?(level)
|
|
39
50
|
return unless config.valid?
|
|
40
51
|
|
|
41
|
-
entry = build_entry(level, message, data)
|
|
42
52
|
buffer.push(entry)
|
|
43
53
|
end
|
|
44
54
|
|
|
@@ -153,6 +163,13 @@ module BrainzLab
|
|
|
153
163
|
end
|
|
154
164
|
end
|
|
155
165
|
end
|
|
166
|
+
|
|
167
|
+
def log_debug_operation(level, message, data)
|
|
168
|
+
return unless BrainzLab::Debug.enabled?
|
|
169
|
+
|
|
170
|
+
truncated_message = message.to_s.length > 50 ? "#{message.to_s[0..47]}..." : message.to_s
|
|
171
|
+
BrainzLab::Debug.log_operation(:recall, "#{level.to_s.upcase} \"#{truncated_message}\"", **data.slice(*data.keys.first(3)))
|
|
172
|
+
end
|
|
156
173
|
end
|
|
157
174
|
end
|
|
158
175
|
end
|
|
@@ -31,20 +31,29 @@ module BrainzLab
|
|
|
31
31
|
|
|
32
32
|
def post(path, body)
|
|
33
33
|
uri = URI.join(@config.reflex_url, path)
|
|
34
|
+
|
|
35
|
+
# Call on_send callback if configured
|
|
36
|
+
invoke_on_send(:reflex, :post, path, body)
|
|
37
|
+
|
|
38
|
+
# Log debug output for request
|
|
39
|
+
log_debug_request(path, body)
|
|
40
|
+
|
|
34
41
|
request = Net::HTTP::Post.new(uri)
|
|
35
42
|
request['Content-Type'] = 'application/json'
|
|
36
43
|
request['Authorization'] = "Bearer #{@config.reflex_auth_key}"
|
|
37
44
|
request['User-Agent'] = "brainzlab-sdk-ruby/#{BrainzLab::VERSION}"
|
|
38
45
|
request.body = JSON.generate(body)
|
|
39
46
|
|
|
40
|
-
execute_with_retry(uri, request)
|
|
47
|
+
execute_with_retry(uri, request, path)
|
|
41
48
|
rescue StandardError => e
|
|
42
|
-
|
|
49
|
+
handle_error(e, context: { path: path, body_size: body.to_s.length })
|
|
43
50
|
nil
|
|
44
51
|
end
|
|
45
52
|
|
|
46
|
-
def execute_with_retry(uri, request)
|
|
53
|
+
def execute_with_retry(uri, request, path)
|
|
47
54
|
retries = 0
|
|
55
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
56
|
+
|
|
48
57
|
begin
|
|
49
58
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
50
59
|
http.use_ssl = uri.scheme == 'https'
|
|
@@ -52,6 +61,10 @@ module BrainzLab
|
|
|
52
61
|
http.read_timeout = 10
|
|
53
62
|
|
|
54
63
|
response = http.request(request)
|
|
64
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
65
|
+
|
|
66
|
+
# Log debug output for response
|
|
67
|
+
log_debug_response(response.code.to_i, duration_ms)
|
|
55
68
|
|
|
56
69
|
case response.code.to_i
|
|
57
70
|
when 200..299
|
|
@@ -63,7 +76,10 @@ module BrainzLab
|
|
|
63
76
|
when 429, 500..599
|
|
64
77
|
raise RetryableError, "Server error: #{response.code}"
|
|
65
78
|
else
|
|
66
|
-
|
|
79
|
+
handle_error(
|
|
80
|
+
StandardError.new("Reflex API error: #{response.code}"),
|
|
81
|
+
context: { path: path, status: response.code, body: response.body }
|
|
82
|
+
)
|
|
67
83
|
nil
|
|
68
84
|
end
|
|
69
85
|
rescue RetryableError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
@@ -72,12 +88,57 @@ module BrainzLab
|
|
|
72
88
|
sleep(RETRY_DELAY * retries)
|
|
73
89
|
retry
|
|
74
90
|
end
|
|
75
|
-
|
|
91
|
+
duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time) * 1000).round(2)
|
|
92
|
+
log_debug_response(0, duration_ms, error: e.message)
|
|
93
|
+
handle_error(e, context: { path: path, retries: retries })
|
|
76
94
|
nil
|
|
77
95
|
end
|
|
78
96
|
end
|
|
79
97
|
|
|
98
|
+
def log_debug_request(path, body)
|
|
99
|
+
return unless BrainzLab::Debug.enabled?
|
|
100
|
+
|
|
101
|
+
data = if body.is_a?(Hash) && body[:errors]
|
|
102
|
+
{ count: body[:errors].size }
|
|
103
|
+
elsif body.is_a?(Hash) && body[:exception]
|
|
104
|
+
{ exception: body[:exception][:type] }
|
|
105
|
+
else
|
|
106
|
+
{}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
BrainzLab::Debug.log_request(:reflex, 'POST', path, data: data)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def log_debug_response(status, duration_ms, error: nil)
|
|
113
|
+
return unless BrainzLab::Debug.enabled?
|
|
114
|
+
|
|
115
|
+
BrainzLab::Debug.log_response(:reflex, status, duration_ms, error: error)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def invoke_on_send(service, method, path, payload)
|
|
119
|
+
return unless @config.on_send
|
|
120
|
+
|
|
121
|
+
@config.on_send.call(service, method, path, payload)
|
|
122
|
+
rescue StandardError => e
|
|
123
|
+
# Don't let callback errors break the SDK
|
|
124
|
+
log_error("on_send callback error: #{e.message}")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def handle_error(error, context: {})
|
|
128
|
+
log_error("#{error.message}")
|
|
129
|
+
|
|
130
|
+
# Call on_error callback if configured
|
|
131
|
+
return unless @config.on_error
|
|
132
|
+
|
|
133
|
+
@config.on_error.call(error, context.merge(service: :reflex))
|
|
134
|
+
rescue StandardError => e
|
|
135
|
+
# Don't let callback errors break the SDK
|
|
136
|
+
log_error("on_error callback error: #{e.message}")
|
|
137
|
+
end
|
|
138
|
+
|
|
80
139
|
def log_error(message)
|
|
140
|
+
BrainzLab::Debug.log(message, level: :error) if BrainzLab::Debug.enabled?
|
|
141
|
+
|
|
81
142
|
return unless @config.logger
|
|
82
143
|
|
|
83
144
|
@config.logger.error("[BrainzLab::Reflex] #{message}")
|
data/lib/brainzlab/reflex.rb
CHANGED
|
@@ -15,15 +15,24 @@ module BrainzLab
|
|
|
15
15
|
return if excluded?(exception)
|
|
16
16
|
return if sampled_out?
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return unless BrainzLab.configuration.reflex_valid?
|
|
18
|
+
# Log debug output for the operation
|
|
19
|
+
log_debug_capture(exception)
|
|
22
20
|
|
|
23
21
|
payload = build_payload(exception, context)
|
|
24
22
|
payload = run_before_send(payload, exception)
|
|
25
23
|
return if payload.nil?
|
|
26
24
|
|
|
25
|
+
# In development mode, log locally instead of sending to server
|
|
26
|
+
if BrainzLab.configuration.development_mode?
|
|
27
|
+
Development.record(service: :reflex, event_type: 'error', payload: payload)
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Auto-provision project on first capture if app_name is configured
|
|
32
|
+
ensure_provisioned!
|
|
33
|
+
|
|
34
|
+
return unless BrainzLab.configuration.reflex_valid?
|
|
35
|
+
|
|
27
36
|
client.send_error(payload)
|
|
28
37
|
end
|
|
29
38
|
|
|
@@ -32,15 +41,24 @@ module BrainzLab
|
|
|
32
41
|
return if capture_disabled?
|
|
33
42
|
return if sampled_out?
|
|
34
43
|
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return unless BrainzLab.configuration.reflex_valid?
|
|
44
|
+
# Log debug output for the operation
|
|
45
|
+
log_debug_message(message, level)
|
|
39
46
|
|
|
40
47
|
payload = build_message_payload(message, level, context)
|
|
41
48
|
payload = run_before_send(payload, nil)
|
|
42
49
|
return if payload.nil?
|
|
43
50
|
|
|
51
|
+
# In development mode, log locally instead of sending to server
|
|
52
|
+
if BrainzLab.configuration.development_mode?
|
|
53
|
+
Development.record(service: :reflex, event_type: 'message', payload: payload)
|
|
54
|
+
return
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Auto-provision project on first capture if app_name is configured
|
|
58
|
+
ensure_provisioned!
|
|
59
|
+
|
|
60
|
+
return unless BrainzLab.configuration.reflex_valid?
|
|
61
|
+
|
|
44
62
|
client.send_error(payload)
|
|
45
63
|
end
|
|
46
64
|
|
|
@@ -384,6 +402,20 @@ module BrainzLab
|
|
|
384
402
|
frame
|
|
385
403
|
end
|
|
386
404
|
end
|
|
405
|
+
|
|
406
|
+
def log_debug_capture(exception)
|
|
407
|
+
return unless BrainzLab::Debug.enabled?
|
|
408
|
+
|
|
409
|
+
truncated_message = exception.message.to_s.length > 40 ? "#{exception.message.to_s[0..37]}..." : exception.message.to_s
|
|
410
|
+
BrainzLab::Debug.log_operation(:reflex, "capture #{exception.class.name}: \"#{truncated_message}\"")
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def log_debug_message(message, level)
|
|
414
|
+
return unless BrainzLab::Debug.enabled?
|
|
415
|
+
|
|
416
|
+
truncated_message = message.to_s.length > 40 ? "#{message.to_s[0..37]}..." : message.to_s
|
|
417
|
+
BrainzLab::Debug.log_operation(:reflex, "message [#{level.to_s.upcase}] \"#{truncated_message}\"")
|
|
418
|
+
end
|
|
387
419
|
end
|
|
388
420
|
end
|
|
389
421
|
end
|
|
@@ -209,7 +209,27 @@ module BrainzLab
|
|
|
209
209
|
end
|
|
210
210
|
|
|
211
211
|
def log_error(operation, error)
|
|
212
|
-
|
|
212
|
+
structured_error = ErrorHandler.wrap(error, service: 'Sentinel', operation: operation)
|
|
213
|
+
BrainzLab.debug_log("[Sentinel::Client] #{operation} failed: #{structured_error.message}")
|
|
214
|
+
|
|
215
|
+
# Call on_error callback if configured
|
|
216
|
+
if @config.on_error
|
|
217
|
+
@config.on_error.call(structured_error, { service: 'Sentinel', operation: operation })
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def handle_response_error(response, operation)
|
|
222
|
+
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent) || response.is_a?(Net::HTTPAccepted)
|
|
223
|
+
|
|
224
|
+
structured_error = ErrorHandler.from_response(response, service: 'Sentinel', operation: operation)
|
|
225
|
+
BrainzLab.debug_log("[Sentinel::Client] #{operation} failed: #{structured_error.message}")
|
|
226
|
+
|
|
227
|
+
# Call on_error callback if configured
|
|
228
|
+
if @config.on_error
|
|
229
|
+
@config.on_error.call(structured_error, { service: 'Sentinel', operation: operation })
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
structured_error
|
|
213
233
|
end
|
|
214
234
|
end
|
|
215
235
|
end
|
|
@@ -281,7 +281,27 @@ module BrainzLab
|
|
|
281
281
|
end
|
|
282
282
|
|
|
283
283
|
def log_error(operation, error)
|
|
284
|
-
|
|
284
|
+
structured_error = ErrorHandler.wrap(error, service: 'Synapse', operation: operation)
|
|
285
|
+
BrainzLab.debug_log("[Synapse::Client] #{operation} failed: #{structured_error.message}")
|
|
286
|
+
|
|
287
|
+
# Call on_error callback if configured
|
|
288
|
+
if @config.on_error
|
|
289
|
+
@config.on_error.call(structured_error, { service: 'Synapse', operation: operation })
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def handle_response_error(response, operation)
|
|
294
|
+
return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent) || response.is_a?(Net::HTTPAccepted)
|
|
295
|
+
|
|
296
|
+
structured_error = ErrorHandler.from_response(response, service: 'Synapse', operation: operation)
|
|
297
|
+
BrainzLab.debug_log("[Synapse::Client] #{operation} failed: #{structured_error.message}")
|
|
298
|
+
|
|
299
|
+
# Call on_error callback if configured
|
|
300
|
+
if @config.on_error
|
|
301
|
+
@config.on_error.call(structured_error, { service: 'Synapse', operation: operation })
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
structured_error
|
|
285
305
|
end
|
|
286
306
|
end
|
|
287
307
|
end
|