fluyenta-ruby 0.1.14
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 +7 -0
- data/CHANGELOG.md +68 -0
- data/LICENSE +11 -0
- data/README.md +571 -0
- data/lib/brainzlab/beacon/client.rb +227 -0
- data/lib/brainzlab/beacon/provisioner.rb +44 -0
- data/lib/brainzlab/beacon.rb +215 -0
- data/lib/brainzlab/configuration.rb +676 -0
- data/lib/brainzlab/context.rb +90 -0
- data/lib/brainzlab/cortex/cache.rb +59 -0
- data/lib/brainzlab/cortex/client.rb +159 -0
- data/lib/brainzlab/cortex/provisioner.rb +49 -0
- data/lib/brainzlab/cortex.rb +223 -0
- data/lib/brainzlab/debug.rb +305 -0
- data/lib/brainzlab/dendrite/client.rb +250 -0
- data/lib/brainzlab/dendrite/provisioner.rb +44 -0
- data/lib/brainzlab/dendrite.rb +195 -0
- 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 +1329 -0
- data/lib/brainzlab/devtools/assets/devtools.js +396 -0
- data/lib/brainzlab/devtools/assets/logo.svg +6 -0
- data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +511 -0
- data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
- data/lib/brainzlab/devtools/data/collector.rb +248 -0
- data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
- data/lib/brainzlab/devtools/middleware/database_handler.rb +177 -0
- data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
- data/lib/brainzlab/devtools/middleware/error_page.rb +377 -0
- data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +159 -0
- data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +98 -0
- data/lib/brainzlab/devtools.rb +75 -0
- data/lib/brainzlab/errors.rb +490 -0
- data/lib/brainzlab/flux/buffer.rb +96 -0
- data/lib/brainzlab/flux/client.rb +68 -0
- data/lib/brainzlab/flux/provisioner.rb +124 -0
- data/lib/brainzlab/flux.rb +184 -0
- data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
- data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
- data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
- data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
- data/lib/brainzlab/instrumentation/action_mailer.rb +182 -0
- data/lib/brainzlab/instrumentation/action_view.rb +380 -0
- data/lib/brainzlab/instrumentation/active_job.rb +569 -0
- data/lib/brainzlab/instrumentation/active_record.rb +559 -0
- data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
- data/lib/brainzlab/instrumentation/active_support_cache.rb +730 -0
- data/lib/brainzlab/instrumentation/aws.rb +183 -0
- data/lib/brainzlab/instrumentation/dalli.rb +108 -0
- data/lib/brainzlab/instrumentation/delayed_job.rb +234 -0
- data/lib/brainzlab/instrumentation/elasticsearch.rb +209 -0
- data/lib/brainzlab/instrumentation/excon.rb +152 -0
- data/lib/brainzlab/instrumentation/faraday.rb +181 -0
- data/lib/brainzlab/instrumentation/good_job.rb +102 -0
- data/lib/brainzlab/instrumentation/grape.rb +293 -0
- data/lib/brainzlab/instrumentation/graphql.rb +252 -0
- data/lib/brainzlab/instrumentation/httparty.rb +193 -0
- data/lib/brainzlab/instrumentation/mongodb.rb +187 -0
- data/lib/brainzlab/instrumentation/net_http.rb +114 -0
- data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
- data/lib/brainzlab/instrumentation/railties.rb +134 -0
- data/lib/brainzlab/instrumentation/redis.rb +324 -0
- data/lib/brainzlab/instrumentation/resque.rb +114 -0
- data/lib/brainzlab/instrumentation/sidekiq.rb +265 -0
- data/lib/brainzlab/instrumentation/solid_queue.rb +194 -0
- data/lib/brainzlab/instrumentation/stripe.rb +163 -0
- data/lib/brainzlab/instrumentation/typhoeus.rb +106 -0
- data/lib/brainzlab/instrumentation.rb +360 -0
- data/lib/brainzlab/nerve/client.rb +235 -0
- data/lib/brainzlab/nerve/provisioner.rb +44 -0
- data/lib/brainzlab/nerve.rb +219 -0
- data/lib/brainzlab/pulse/client.rb +203 -0
- data/lib/brainzlab/pulse/instrumentation.rb +401 -0
- data/lib/brainzlab/pulse/propagation.rb +241 -0
- data/lib/brainzlab/pulse/provisioner.rb +114 -0
- data/lib/brainzlab/pulse/tracer.rb +111 -0
- data/lib/brainzlab/pulse.rb +294 -0
- data/lib/brainzlab/rails/log_formatter.rb +807 -0
- data/lib/brainzlab/rails/log_subscriber.rb +334 -0
- data/lib/brainzlab/rails/railtie.rb +606 -0
- data/lib/brainzlab/recall/buffer.rb +66 -0
- data/lib/brainzlab/recall/client.rb +158 -0
- data/lib/brainzlab/recall/logger.rb +116 -0
- data/lib/brainzlab/recall/provisioner.rb +130 -0
- data/lib/brainzlab/recall.rb +175 -0
- data/lib/brainzlab/reflex/breadcrumbs.rb +55 -0
- data/lib/brainzlab/reflex/client.rb +150 -0
- data/lib/brainzlab/reflex/provisioner.rb +116 -0
- data/lib/brainzlab/reflex.rb +421 -0
- data/lib/brainzlab/sentinel/client.rb +236 -0
- data/lib/brainzlab/sentinel/provisioner.rb +44 -0
- data/lib/brainzlab/sentinel.rb +165 -0
- data/lib/brainzlab/signal/client.rb +60 -0
- data/lib/brainzlab/signal/provisioner.rb +115 -0
- data/lib/brainzlab/signal.rb +136 -0
- data/lib/brainzlab/synapse/client.rb +308 -0
- data/lib/brainzlab/synapse/provisioner.rb +44 -0
- data/lib/brainzlab/synapse.rb +270 -0
- 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 +290 -0
- data/lib/brainzlab/utilities/health_check.rb +294 -0
- data/lib/brainzlab/utilities/log_formatter.rb +254 -0
- data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
- data/lib/brainzlab/utilities.rb +17 -0
- data/lib/brainzlab/vault/cache.rb +80 -0
- data/lib/brainzlab/vault/client.rb +216 -0
- data/lib/brainzlab/vault/provisioner.rb +49 -0
- data/lib/brainzlab/vault.rb +262 -0
- data/lib/brainzlab/version.rb +5 -0
- data/lib/brainzlab/vision/client.rb +175 -0
- data/lib/brainzlab/vision/provisioner.rb +136 -0
- data/lib/brainzlab/vision.rb +155 -0
- data/lib/brainzlab-sdk.rb +3 -0
- data/lib/brainzlab.rb +306 -0
- data/lib/generators/brainzlab/install/install_generator.rb +63 -0
- data/lib/generators/brainzlab/install/templates/brainzlab.rb.tt +77 -0
- metadata +251 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Nerve
|
|
5
|
+
class Provisioner
|
|
6
|
+
def initialize(config)
|
|
7
|
+
@config = config
|
|
8
|
+
@provisioned = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def ensure_project!
|
|
12
|
+
return if @provisioned
|
|
13
|
+
return unless @config.nerve_auto_provision
|
|
14
|
+
return unless valid_auth?
|
|
15
|
+
|
|
16
|
+
@provisioned = true
|
|
17
|
+
|
|
18
|
+
project_id = detect_project_id
|
|
19
|
+
return unless project_id
|
|
20
|
+
|
|
21
|
+
client = Client.new(@config)
|
|
22
|
+
client.provision(
|
|
23
|
+
project_id: project_id,
|
|
24
|
+
app_name: @config.app_name || @config.service
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
BrainzLab.debug_log("[Nerve::Provisioner] Project provisioned: #{project_id}")
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
BrainzLab.debug_log("[Nerve::Provisioner] Provisioning failed: #{e.message}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def valid_auth?
|
|
35
|
+
key = @config.nerve_api_key || @config.nerve_master_key || @config.secret_key
|
|
36
|
+
!key.nil? && !key.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def detect_project_id
|
|
40
|
+
ENV.fetch('BRAINZLAB_PROJECT_ID', nil)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'nerve/client'
|
|
4
|
+
require_relative 'nerve/provisioner'
|
|
5
|
+
|
|
6
|
+
module BrainzLab
|
|
7
|
+
module Nerve
|
|
8
|
+
class << self
|
|
9
|
+
# Report a completed job
|
|
10
|
+
# @param job_class [String] Job class name
|
|
11
|
+
# @param job_id [String] Job ID
|
|
12
|
+
# @param queue [String] Queue name
|
|
13
|
+
# @param started_at [Time] When job started
|
|
14
|
+
# @param ended_at [Time] When job ended (defaults to now)
|
|
15
|
+
# @param attributes [Hash] Additional attributes
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# BrainzLab::Nerve.report_success(
|
|
19
|
+
# job_class: "ProcessOrderJob",
|
|
20
|
+
# job_id: "abc-123",
|
|
21
|
+
# queue: "default",
|
|
22
|
+
# started_at: 1.minute.ago
|
|
23
|
+
# )
|
|
24
|
+
#
|
|
25
|
+
def report_success(job_class:, job_id:, queue:, started_at:, ended_at: Time.now, **attributes)
|
|
26
|
+
return false unless enabled?
|
|
27
|
+
|
|
28
|
+
ensure_provisioned!
|
|
29
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
30
|
+
|
|
31
|
+
client.report_job(
|
|
32
|
+
job_class: job_class,
|
|
33
|
+
job_id: job_id,
|
|
34
|
+
queue: queue,
|
|
35
|
+
status: 'completed',
|
|
36
|
+
started_at: started_at,
|
|
37
|
+
ended_at: ended_at,
|
|
38
|
+
**attributes
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Report a failed job
|
|
43
|
+
def report_failure(job_class:, job_id:, queue:, error:, started_at: nil, **attributes)
|
|
44
|
+
return false unless enabled?
|
|
45
|
+
|
|
46
|
+
ensure_provisioned!
|
|
47
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
48
|
+
|
|
49
|
+
client.report_failure(
|
|
50
|
+
job_class: job_class,
|
|
51
|
+
job_id: job_id,
|
|
52
|
+
queue: queue,
|
|
53
|
+
error_class: error.class.name,
|
|
54
|
+
error_message: error.message,
|
|
55
|
+
backtrace: error.backtrace,
|
|
56
|
+
started_at: started_at,
|
|
57
|
+
**attributes
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Report a job that's currently running
|
|
62
|
+
def report_started(job_class:, job_id:, queue:, **attributes)
|
|
63
|
+
return false unless enabled?
|
|
64
|
+
|
|
65
|
+
ensure_provisioned!
|
|
66
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
67
|
+
|
|
68
|
+
client.report_job(
|
|
69
|
+
job_class: job_class,
|
|
70
|
+
job_id: job_id,
|
|
71
|
+
queue: queue,
|
|
72
|
+
status: 'running',
|
|
73
|
+
started_at: Time.now,
|
|
74
|
+
ended_at: Time.now,
|
|
75
|
+
**attributes
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Get job statistics
|
|
80
|
+
# @param queue [String] Filter by queue (optional)
|
|
81
|
+
# @param job_class [String] Filter by job class (optional)
|
|
82
|
+
# @param period [String] Time period: "1h", "24h", "7d", "30d"
|
|
83
|
+
def stats(queue: nil, job_class: nil, period: '1h')
|
|
84
|
+
return nil unless enabled?
|
|
85
|
+
|
|
86
|
+
ensure_provisioned!
|
|
87
|
+
return nil unless BrainzLab.configuration.nerve_valid?
|
|
88
|
+
|
|
89
|
+
client.stats(queue: queue, job_class: job_class, period: period)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# List recent jobs
|
|
93
|
+
def jobs(queue: nil, status: nil, limit: 100)
|
|
94
|
+
return [] unless enabled?
|
|
95
|
+
|
|
96
|
+
ensure_provisioned!
|
|
97
|
+
return [] unless BrainzLab.configuration.nerve_valid?
|
|
98
|
+
|
|
99
|
+
client.list_jobs(queue: queue, status: status, limit: limit)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# List all queues
|
|
103
|
+
def queues
|
|
104
|
+
return [] unless enabled?
|
|
105
|
+
|
|
106
|
+
ensure_provisioned!
|
|
107
|
+
return [] unless BrainzLab.configuration.nerve_valid?
|
|
108
|
+
|
|
109
|
+
client.list_queues
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Get queue details
|
|
113
|
+
def queue(name)
|
|
114
|
+
return nil unless enabled?
|
|
115
|
+
|
|
116
|
+
ensure_provisioned!
|
|
117
|
+
return nil unless BrainzLab.configuration.nerve_valid?
|
|
118
|
+
|
|
119
|
+
client.get_queue(name)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Retry a failed job
|
|
123
|
+
def retry(job_id)
|
|
124
|
+
return false unless enabled?
|
|
125
|
+
|
|
126
|
+
ensure_provisioned!
|
|
127
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
128
|
+
|
|
129
|
+
client.retry_job(job_id)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Delete a job
|
|
133
|
+
def delete(job_id)
|
|
134
|
+
return false unless enabled?
|
|
135
|
+
|
|
136
|
+
ensure_provisioned!
|
|
137
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
138
|
+
|
|
139
|
+
client.delete_job(job_id)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Report queue metrics (for custom job backends)
|
|
143
|
+
def report_metrics(queue:, size:, latency_ms: nil, workers: nil)
|
|
144
|
+
return false unless enabled?
|
|
145
|
+
|
|
146
|
+
ensure_provisioned!
|
|
147
|
+
return false unless BrainzLab.configuration.nerve_valid?
|
|
148
|
+
|
|
149
|
+
client.report_metrics(
|
|
150
|
+
queue: queue,
|
|
151
|
+
size: size,
|
|
152
|
+
latency_ms: latency_ms,
|
|
153
|
+
workers: workers
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Track a job execution (block helper)
|
|
158
|
+
# @example
|
|
159
|
+
# BrainzLab::Nerve.track(job_class: "MyJob", job_id: "123", queue: "default") do
|
|
160
|
+
# # job work
|
|
161
|
+
# end
|
|
162
|
+
#
|
|
163
|
+
def track(job_class:, job_id:, queue: 'default', **attributes)
|
|
164
|
+
started_at = Time.now
|
|
165
|
+
|
|
166
|
+
begin
|
|
167
|
+
result = yield
|
|
168
|
+
report_success(
|
|
169
|
+
job_class: job_class,
|
|
170
|
+
job_id: job_id,
|
|
171
|
+
queue: queue,
|
|
172
|
+
started_at: started_at,
|
|
173
|
+
**attributes
|
|
174
|
+
)
|
|
175
|
+
result
|
|
176
|
+
rescue StandardError => e
|
|
177
|
+
report_failure(
|
|
178
|
+
job_class: job_class,
|
|
179
|
+
job_id: job_id,
|
|
180
|
+
queue: queue,
|
|
181
|
+
error: e,
|
|
182
|
+
started_at: started_at,
|
|
183
|
+
**attributes
|
|
184
|
+
)
|
|
185
|
+
raise
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# === INTERNAL ===
|
|
190
|
+
|
|
191
|
+
def ensure_provisioned!
|
|
192
|
+
return if @provisioned
|
|
193
|
+
|
|
194
|
+
@provisioned = true
|
|
195
|
+
provisioner.ensure_project!
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def provisioner
|
|
199
|
+
@provisioner ||= Provisioner.new(BrainzLab.configuration)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def client
|
|
203
|
+
@client ||= Client.new(BrainzLab.configuration)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def reset!
|
|
207
|
+
@client = nil
|
|
208
|
+
@provisioner = nil
|
|
209
|
+
@provisioned = false
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
private
|
|
213
|
+
|
|
214
|
+
def enabled?
|
|
215
|
+
BrainzLab.configuration.nerve_enabled
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module BrainzLab
|
|
8
|
+
module Pulse
|
|
9
|
+
class Client
|
|
10
|
+
MAX_RETRIES = 3
|
|
11
|
+
RETRY_DELAY = 0.5
|
|
12
|
+
|
|
13
|
+
def initialize(config)
|
|
14
|
+
@config = config
|
|
15
|
+
@buffer = []
|
|
16
|
+
@mutex = Mutex.new
|
|
17
|
+
@flush_thread = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def send_trace(payload)
|
|
21
|
+
return unless @config.pulse_enabled && @config.pulse_valid?
|
|
22
|
+
|
|
23
|
+
if @config.pulse_buffer_size > 1
|
|
24
|
+
buffer_trace(payload)
|
|
25
|
+
else
|
|
26
|
+
post('/api/v1/traces', payload)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def send_batch(payloads)
|
|
31
|
+
return unless @config.pulse_enabled && @config.pulse_valid?
|
|
32
|
+
return if payloads.empty?
|
|
33
|
+
|
|
34
|
+
post('/api/v1/traces/batch', { traces: payloads })
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def send_metric(payload)
|
|
38
|
+
return unless @config.pulse_enabled && @config.pulse_valid?
|
|
39
|
+
|
|
40
|
+
post('/api/v1/metrics', payload)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def send_span(payload)
|
|
44
|
+
return unless @config.pulse_enabled && @config.pulse_valid?
|
|
45
|
+
|
|
46
|
+
post('/api/v1/spans', payload)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def flush
|
|
50
|
+
traces_to_send = nil
|
|
51
|
+
|
|
52
|
+
@mutex.synchronize do
|
|
53
|
+
return if @buffer.empty?
|
|
54
|
+
|
|
55
|
+
traces_to_send = @buffer.dup
|
|
56
|
+
@buffer.clear
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
send_batch(traces_to_send) if traces_to_send&.any?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def buffer_trace(payload)
|
|
65
|
+
should_flush = false
|
|
66
|
+
|
|
67
|
+
@mutex.synchronize do
|
|
68
|
+
@buffer << payload
|
|
69
|
+
should_flush = @buffer.size >= @config.pulse_buffer_size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
start_flush_timer unless @flush_thread&.alive?
|
|
73
|
+
flush if should_flush
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def start_flush_timer
|
|
77
|
+
@flush_thread = Thread.new do
|
|
78
|
+
loop do
|
|
79
|
+
sleep(@config.pulse_flush_interval)
|
|
80
|
+
flush
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def post(path, body)
|
|
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
|
+
|
|
94
|
+
request = Net::HTTP::Post.new(uri)
|
|
95
|
+
request['Content-Type'] = 'application/json'
|
|
96
|
+
request['Authorization'] = "Bearer #{@config.pulse_auth_key}"
|
|
97
|
+
request['User-Agent'] = "brainzlab-sdk-ruby/#{BrainzLab::VERSION}"
|
|
98
|
+
request.body = JSON.generate(body)
|
|
99
|
+
|
|
100
|
+
execute_with_retry(uri, request, path)
|
|
101
|
+
rescue StandardError => e
|
|
102
|
+
handle_error(e, context: { path: path, body_size: body.to_s.length })
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def execute_with_retry(uri, request, path)
|
|
107
|
+
retries = 0
|
|
108
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
109
|
+
|
|
110
|
+
begin
|
|
111
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
112
|
+
http.use_ssl = uri.scheme == 'https'
|
|
113
|
+
http.open_timeout = 5
|
|
114
|
+
http.read_timeout = 10
|
|
115
|
+
|
|
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)
|
|
121
|
+
|
|
122
|
+
case response.code.to_i
|
|
123
|
+
when 200..299
|
|
124
|
+
begin
|
|
125
|
+
JSON.parse(response.body)
|
|
126
|
+
rescue StandardError
|
|
127
|
+
{}
|
|
128
|
+
end
|
|
129
|
+
when 429, 500..599
|
|
130
|
+
raise RetryableError, "Server error: #{response.code}"
|
|
131
|
+
else
|
|
132
|
+
handle_error(
|
|
133
|
+
StandardError.new("Pulse API error: #{response.code}"),
|
|
134
|
+
context: { path: path, status: response.code, body: response.body }
|
|
135
|
+
)
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
rescue RetryableError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
139
|
+
retries += 1
|
|
140
|
+
if retries <= MAX_RETRIES
|
|
141
|
+
sleep(RETRY_DELAY * retries)
|
|
142
|
+
retry
|
|
143
|
+
end
|
|
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 })
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
end
|
|
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
|
+
|
|
192
|
+
def log_error(message)
|
|
193
|
+
BrainzLab::Debug.log(message, level: :error) if BrainzLab::Debug.enabled?
|
|
194
|
+
|
|
195
|
+
return unless @config.logger
|
|
196
|
+
|
|
197
|
+
@config.logger.error("[BrainzLab::Pulse] #{message}")
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class RetryableError < StandardError; end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|