brainzlab 0.1.5 → 0.1.7
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/lib/brainzlab/configuration.rb +27 -5
- data/lib/brainzlab/flux/provisioner.rb +80 -13
- data/lib/brainzlab/instrumentation.rb +60 -0
- data/lib/brainzlab/pulse.rb +33 -9
- data/lib/brainzlab/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0816189a1797239a3394e13fb0e08c33eddc061e146d4a1d3874b5b67777c967'
|
|
4
|
+
data.tar.gz: 3ff73e1d6babbd0b4f54f53eab270267be9e822d305cafeb59748b02f88f9b45
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e4733d1814297ebfd9cfe85aa909eddb3607e88efc024e07f55ea23ae5ee31d815538688a2fca1a67bba7a89ceb7b02a73aaa521f3114142539291c055dfff7a
|
|
7
|
+
data.tar.gz: 43a1826589f522ffdf678515798124729500e265c29f7860322db9a0a831e3b9433f738545024421c2667b029d96dfe2dcc7fd13dbd22cf2a990807585c57ceb
|
|
@@ -148,7 +148,8 @@ module BrainzLab
|
|
|
148
148
|
:devtools_allowed_ips,
|
|
149
149
|
:devtools_asset_path,
|
|
150
150
|
:devtools_panel_position,
|
|
151
|
-
:devtools_expand_by_default
|
|
151
|
+
:devtools_expand_by_default,
|
|
152
|
+
:rails_instrumentation_handled_externally
|
|
152
153
|
|
|
153
154
|
# Services that should not track themselves to avoid circular dependencies
|
|
154
155
|
SELF_TRACKING_SERVICES = {
|
|
@@ -184,7 +185,7 @@ module BrainzLab
|
|
|
184
185
|
|
|
185
186
|
# Recall settings
|
|
186
187
|
@recall_enabled = true
|
|
187
|
-
@recall_url = ENV['RECALL_URL'] || '
|
|
188
|
+
@recall_url = ENV['RECALL_URL'] || detect_product_url('recall')
|
|
188
189
|
@recall_min_level = :debug
|
|
189
190
|
@recall_buffer_size = 50
|
|
190
191
|
@recall_flush_interval = 5
|
|
@@ -193,7 +194,7 @@ module BrainzLab
|
|
|
193
194
|
|
|
194
195
|
# Reflex settings
|
|
195
196
|
@reflex_enabled = true
|
|
196
|
-
@reflex_url = ENV['REFLEX_URL'] || '
|
|
197
|
+
@reflex_url = ENV['REFLEX_URL'] || detect_product_url('reflex')
|
|
197
198
|
@reflex_api_key = ENV.fetch('REFLEX_API_KEY', nil)
|
|
198
199
|
@reflex_master_key = ENV.fetch('REFLEX_MASTER_KEY', nil)
|
|
199
200
|
@reflex_auto_provision = true
|
|
@@ -204,7 +205,7 @@ module BrainzLab
|
|
|
204
205
|
|
|
205
206
|
# Pulse settings
|
|
206
207
|
@pulse_enabled = true
|
|
207
|
-
@pulse_url = ENV['PULSE_URL'] || '
|
|
208
|
+
@pulse_url = ENV['PULSE_URL'] || detect_product_url('pulse')
|
|
208
209
|
@pulse_api_key = ENV.fetch('PULSE_API_KEY', nil)
|
|
209
210
|
@pulse_master_key = ENV.fetch('PULSE_MASTER_KEY', nil)
|
|
210
211
|
@pulse_auto_provision = true
|
|
@@ -225,7 +226,7 @@ module BrainzLab
|
|
|
225
226
|
|
|
226
227
|
# Signal settings
|
|
227
228
|
@signal_enabled = true
|
|
228
|
-
@signal_url = ENV['SIGNAL_URL'] || '
|
|
229
|
+
@signal_url = ENV['SIGNAL_URL'] || detect_product_url('signal')
|
|
229
230
|
@signal_api_key = ENV.fetch('SIGNAL_API_KEY', nil)
|
|
230
231
|
@signal_master_key = ENV.fetch('SIGNAL_MASTER_KEY', nil)
|
|
231
232
|
@signal_auto_provision = true
|
|
@@ -351,6 +352,11 @@ module BrainzLab
|
|
|
351
352
|
@devtools_asset_path = '/__brainzlab__'
|
|
352
353
|
@devtools_panel_position = 'bottom-right'
|
|
353
354
|
@devtools_expand_by_default = false
|
|
355
|
+
|
|
356
|
+
# Rails instrumentation delegation
|
|
357
|
+
# When true, brainzlab-rails gem handles Rails-specific instrumentation
|
|
358
|
+
# SDK will only install non-Rails instrumentation (HTTP clients, Redis, etc.)
|
|
359
|
+
@rails_instrumentation_handled_externally = false
|
|
354
360
|
end
|
|
355
361
|
|
|
356
362
|
def recall_min_level=(level)
|
|
@@ -571,5 +577,21 @@ module BrainzLab
|
|
|
571
577
|
rescue StandardError
|
|
572
578
|
nil
|
|
573
579
|
end
|
|
580
|
+
|
|
581
|
+
def detect_product_url(product)
|
|
582
|
+
# In development, use .localhost domains (works with Traefik)
|
|
583
|
+
# In production, use the real brainzlab.ai domains
|
|
584
|
+
if development?
|
|
585
|
+
"http://#{product}.localhost"
|
|
586
|
+
else
|
|
587
|
+
"https://#{product}.brainzlab.ai"
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def development?
|
|
592
|
+
@environment == 'development' ||
|
|
593
|
+
ENV['RAILS_ENV'] == 'development' ||
|
|
594
|
+
ENV['RACK_ENV'] == 'development'
|
|
595
|
+
end
|
|
574
596
|
end
|
|
575
597
|
end
|
|
@@ -3,54 +3,121 @@
|
|
|
3
3
|
require 'net/http'
|
|
4
4
|
require 'json'
|
|
5
5
|
require 'uri'
|
|
6
|
+
require 'fileutils'
|
|
6
7
|
|
|
7
8
|
module BrainzLab
|
|
8
9
|
module Flux
|
|
9
10
|
class Provisioner
|
|
11
|
+
CACHE_DIR = ENV.fetch('BRAINZLAB_CACHE_DIR') { File.join(Dir.home, '.brainzlab') }
|
|
12
|
+
|
|
10
13
|
def initialize(config)
|
|
11
14
|
@config = config
|
|
12
15
|
end
|
|
13
16
|
|
|
14
17
|
def ensure_project!
|
|
15
|
-
return
|
|
16
|
-
(@config.flux_api_key && !@config.flux_api_key.to_s.empty?)
|
|
17
|
-
return unless @config.flux_url && !@config.flux_url.to_s.empty?
|
|
18
|
-
return unless @config.secret_key && !@config.secret_key.to_s.empty?
|
|
18
|
+
return unless should_provision?
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
# Try cached credentials first
|
|
21
|
+
if (cached = load_cached_credentials)
|
|
22
|
+
apply_credentials(cached)
|
|
23
|
+
return cached
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Provision new project
|
|
27
|
+
project = provision_project
|
|
28
|
+
return unless project
|
|
29
|
+
|
|
30
|
+
# Cache and apply credentials
|
|
31
|
+
cache_credentials(project)
|
|
32
|
+
apply_credentials(project)
|
|
33
|
+
|
|
34
|
+
project
|
|
22
35
|
end
|
|
23
36
|
|
|
24
37
|
private
|
|
25
38
|
|
|
39
|
+
def should_provision?
|
|
40
|
+
# Already have credentials
|
|
41
|
+
return false if @config.flux_ingest_key.to_s.strip.length.positive?
|
|
42
|
+
return false if @config.flux_api_key.to_s.strip.length.positive?
|
|
43
|
+
|
|
44
|
+
# Need auto_provision enabled
|
|
45
|
+
return false unless @config.flux_auto_provision
|
|
46
|
+
|
|
47
|
+
# Need app_name for project name
|
|
48
|
+
return false unless @config.app_name.to_s.strip.length.positive?
|
|
49
|
+
|
|
50
|
+
# Need master key for provisioning
|
|
51
|
+
return false unless @config.flux_master_key.to_s.strip.length.positive?
|
|
52
|
+
|
|
53
|
+
# Need flux_url
|
|
54
|
+
return false unless @config.flux_url.to_s.strip.length.positive?
|
|
55
|
+
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
26
59
|
def provision_project
|
|
60
|
+
BrainzLab.debug_log('[Flux] Auto-provisioning project...')
|
|
61
|
+
|
|
27
62
|
uri = URI.parse("#{@config.flux_url}/api/v1/projects/provision")
|
|
28
63
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
29
64
|
http.use_ssl = uri.scheme == 'https'
|
|
30
|
-
http.open_timeout =
|
|
31
|
-
http.read_timeout =
|
|
65
|
+
http.open_timeout = 5
|
|
66
|
+
http.read_timeout = 10
|
|
32
67
|
|
|
33
68
|
request = Net::HTTP::Post.new(uri.path)
|
|
34
69
|
request['Content-Type'] = 'application/json'
|
|
35
|
-
request['
|
|
70
|
+
request['X-Master-Key'] = @config.flux_master_key
|
|
36
71
|
request['User-Agent'] = "brainzlab-sdk/#{BrainzLab::VERSION}"
|
|
37
72
|
request.body = {
|
|
38
|
-
name: @config.
|
|
73
|
+
name: @config.app_name,
|
|
39
74
|
environment: @config.environment
|
|
40
75
|
}.to_json
|
|
41
76
|
|
|
42
77
|
response = http.request(request)
|
|
43
78
|
|
|
44
79
|
if response.is_a?(Net::HTTPSuccess)
|
|
45
|
-
data = JSON.parse(response.body)
|
|
46
|
-
@config.flux_ingest_key = data['ingest_key']
|
|
47
|
-
@config.flux_api_key = data['api_key']
|
|
80
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
48
81
|
BrainzLab.debug_log('[Flux] Project provisioned successfully')
|
|
82
|
+
data
|
|
49
83
|
else
|
|
50
84
|
BrainzLab.debug_log("[Flux] Provisioning failed: #{response.code} - #{response.body}")
|
|
85
|
+
nil
|
|
51
86
|
end
|
|
52
87
|
rescue StandardError => e
|
|
53
88
|
BrainzLab.debug_log("[Flux] Provisioning error: #{e.message}")
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def load_cached_credentials
|
|
93
|
+
path = cache_file_path
|
|
94
|
+
return nil unless File.exist?(path)
|
|
95
|
+
|
|
96
|
+
data = JSON.parse(File.read(path), symbolize_names: true)
|
|
97
|
+
|
|
98
|
+
# Validate cached data has required keys
|
|
99
|
+
return nil unless data[:ingest_key]
|
|
100
|
+
|
|
101
|
+
data
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
BrainzLab.debug_log("[Flux] Failed to load cached credentials: #{e.message}")
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def cache_credentials(project)
|
|
108
|
+
FileUtils.mkdir_p(CACHE_DIR)
|
|
109
|
+
File.write(cache_file_path, JSON.generate(project))
|
|
110
|
+
rescue StandardError => e
|
|
111
|
+
BrainzLab.debug_log("[Flux] Failed to cache credentials: #{e.message}")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def cache_file_path
|
|
115
|
+
File.join(CACHE_DIR, "#{@config.app_name}.flux.json")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def apply_credentials(project)
|
|
119
|
+
@config.flux_ingest_key = project[:ingest_key]
|
|
120
|
+
@config.flux_api_key = project[:api_key]
|
|
54
121
|
end
|
|
55
122
|
end
|
|
56
123
|
end
|
|
@@ -6,6 +6,14 @@ module BrainzLab
|
|
|
6
6
|
def install!
|
|
7
7
|
config = BrainzLab.configuration
|
|
8
8
|
|
|
9
|
+
# Skip Rails-specific instrumentation if brainzlab-rails gem is handling it
|
|
10
|
+
# This prevents double-tracking of events
|
|
11
|
+
if config.rails_instrumentation_handled_externally
|
|
12
|
+
BrainzLab.debug_log('[Instrumentation] Rails instrumentation handled by brainzlab-rails gem, skipping SDK instrumentation')
|
|
13
|
+
install_non_rails_instrumentation!(config)
|
|
14
|
+
return
|
|
15
|
+
end
|
|
16
|
+
|
|
9
17
|
# HTTP client instrumentation
|
|
10
18
|
if config.instrument_http
|
|
11
19
|
install_net_http!
|
|
@@ -289,6 +297,58 @@ module BrainzLab
|
|
|
289
297
|
install_faraday!
|
|
290
298
|
install_httparty!
|
|
291
299
|
end
|
|
300
|
+
|
|
301
|
+
private
|
|
302
|
+
|
|
303
|
+
# Install only non-Rails-specific instrumentation
|
|
304
|
+
# Used when brainzlab-rails gem handles Rails events via ActiveSupport::Notifications
|
|
305
|
+
def install_non_rails_instrumentation!(config)
|
|
306
|
+
# HTTP client instrumentation (not Rails-specific)
|
|
307
|
+
if config.instrument_http
|
|
308
|
+
install_net_http!
|
|
309
|
+
install_faraday!
|
|
310
|
+
install_httparty!
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# Redis instrumentation (not Rails-specific)
|
|
314
|
+
install_redis! if config.instrument_redis
|
|
315
|
+
|
|
316
|
+
# Sidekiq instrumentation (not Rails-specific, has its own hooks)
|
|
317
|
+
install_sidekiq! if config.instrument_sidekiq
|
|
318
|
+
|
|
319
|
+
# GraphQL instrumentation (not Rails-specific)
|
|
320
|
+
install_graphql! if config.instrument_graphql
|
|
321
|
+
|
|
322
|
+
# MongoDB instrumentation (not Rails-specific)
|
|
323
|
+
install_mongodb! if config.instrument_mongodb
|
|
324
|
+
|
|
325
|
+
# Elasticsearch instrumentation (not Rails-specific)
|
|
326
|
+
install_elasticsearch! if config.instrument_elasticsearch
|
|
327
|
+
|
|
328
|
+
# Delayed::Job instrumentation (not Rails-specific, has its own hooks)
|
|
329
|
+
install_delayed_job! if config.instrument_delayed_job
|
|
330
|
+
|
|
331
|
+
# Grape API instrumentation (not Rails-specific)
|
|
332
|
+
install_grape! if config.instrument_grape
|
|
333
|
+
|
|
334
|
+
# Modern job queue instrumentation (have their own hooks)
|
|
335
|
+
install_solid_queue! if config.instrument_solid_queue
|
|
336
|
+
install_good_job! if config.instrument_good_job
|
|
337
|
+
install_resque! if config.instrument_resque
|
|
338
|
+
|
|
339
|
+
# Additional HTTP clients (not Rails-specific)
|
|
340
|
+
install_excon! if config.instrument_excon
|
|
341
|
+
install_typhoeus! if config.instrument_typhoeus
|
|
342
|
+
|
|
343
|
+
# Dalli/Memcached (not Rails-specific, has its own hooks)
|
|
344
|
+
install_dalli! if config.instrument_dalli
|
|
345
|
+
|
|
346
|
+
# Cloud & Payment (not Rails-specific)
|
|
347
|
+
install_aws! if config.instrument_aws
|
|
348
|
+
install_stripe! if config.instrument_stripe
|
|
349
|
+
|
|
350
|
+
BrainzLab.debug_log('[Instrumentation] Non-Rails instrumentation installed (HTTP, Redis, Sidekiq, GraphQL, etc.)')
|
|
351
|
+
end
|
|
292
352
|
end
|
|
293
353
|
end
|
|
294
354
|
end
|
data/lib/brainzlab/pulse.rb
CHANGED
|
@@ -97,19 +97,43 @@ module BrainzLab
|
|
|
97
97
|
ensure_provisioned!
|
|
98
98
|
return unless BrainzLab.configuration.pulse_valid?
|
|
99
99
|
|
|
100
|
+
# Parse timestamp or use current time
|
|
101
|
+
started_at = if timestamp
|
|
102
|
+
Time.parse(timestamp) rescue Time.now.utc
|
|
103
|
+
else
|
|
104
|
+
Time.now.utc
|
|
105
|
+
end
|
|
106
|
+
|
|
100
107
|
span_data = {
|
|
108
|
+
span_id: SecureRandom.uuid,
|
|
101
109
|
name: name,
|
|
102
|
-
|
|
110
|
+
kind: category,
|
|
111
|
+
started_at: started_at,
|
|
112
|
+
ended_at: started_at, # Same as started_at since we only have duration
|
|
103
113
|
duration_ms: duration_ms,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
service: BrainzLab.configuration.service,
|
|
108
|
-
host: BrainzLab.configuration.host,
|
|
109
|
-
request_id: Context.current.request_id
|
|
110
|
-
}.compact
|
|
114
|
+
error: false,
|
|
115
|
+
data: attributes
|
|
116
|
+
}
|
|
111
117
|
|
|
112
|
-
|
|
118
|
+
# If there's an active trace, add the span to it (will be sent with finish_trace)
|
|
119
|
+
# Otherwise, send it directly to the API as a standalone span
|
|
120
|
+
if tracer.current_trace
|
|
121
|
+
tracer.current_spans << span_data
|
|
122
|
+
else
|
|
123
|
+
# Send as standalone span (backward compatibility)
|
|
124
|
+
api_span_data = {
|
|
125
|
+
name: name,
|
|
126
|
+
category: category,
|
|
127
|
+
duration_ms: duration_ms,
|
|
128
|
+
timestamp: timestamp || Time.now.utc.iso8601(3),
|
|
129
|
+
attributes: attributes,
|
|
130
|
+
environment: BrainzLab.configuration.environment,
|
|
131
|
+
service: BrainzLab.configuration.service,
|
|
132
|
+
host: BrainzLab.configuration.host,
|
|
133
|
+
request_id: Context.current.request_id
|
|
134
|
+
}.compact
|
|
135
|
+
client.send_span(api_span_data)
|
|
136
|
+
end
|
|
113
137
|
end
|
|
114
138
|
|
|
115
139
|
def ensure_provisioned!
|
data/lib/brainzlab/version.rb
CHANGED