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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97098a98bb87f20519522678294b7872e1ff75c9a340dc85c0681bce794bdc27
4
- data.tar.gz: 8842b97cc1d4f27c772df7463c2bca85b13b4d0cf91a0d2fd46b00f900e813f5
3
+ metadata.gz: 85a5cf3a64e256a569be39f7caa1ec6268755616ac6037db51f1543ee74dd50c
4
+ data.tar.gz: '09c531bb0793d26db1f62faf41f9ead294130a7036f6e5f6ab270fd8c6c46040'
5
5
  SHA512:
6
- metadata.gz: 1f940b97715f5b5592a834fbedcf409888d31f8421c35ad6435c71bde91068d351270092e816b26f6a1d97bc274bcb457bc612a3599b8bba143478be23564d0a
7
- data.tar.gz: ba2d7b3de3190e84ac3301173a0c2f4f6db3acc7bcbec5c84d73844cbb6f8b745db5d9a5c8c7064a4f6a2bb8a556b998df2d4ae1dfad044ecd9e9c46542ccfe0
6
+ metadata.gz: 28c2f10fc4457d4f2f34c5bcf4d63c14a062d4fcd8437b13cbcebc1a062976361b2014f5567e0fff82682a054fa83363f7e955a23d3d59ae285a9f06500c79e6
7
+ data.tar.gz: 5d130bf7242d57c3149803649262995daf298483c406002ab99e01495efb14df7a6874b8eb485daa90c79da8903ad37f6516ba0ca7e2c8930ff069a1b536a206
data/README.md CHANGED
@@ -47,12 +47,21 @@ bundle config set --global rubygems.pkg.github.com USERNAME:TOKEN
47
47
 
48
48
  ## Quick Start
49
49
 
50
+ ### Get Your API Key
51
+
52
+ 1. Sign up at [platform.brainzlab.ai](https://platform.brainzlab.ai)
53
+ 2. Create or select a project
54
+ 3. Copy your API key (`sk_live_xxx` or `sk_test_xxx`)
55
+ 4. Set it as `BRAINZLAB_SECRET_KEY` environment variable
56
+
57
+ **One key, all products**: Your Platform API key works across Recall, Reflex, Pulse, and all BrainzLab products. No separate keys needed.
58
+
50
59
  ### Configuration
51
60
 
52
61
  ```ruby
53
62
  # config/initializers/brainzlab.rb
54
63
  BrainzLab.configure do |config|
55
- # Authentication (required)
64
+ # Authentication (required) - Your Platform API key
56
65
  config.secret_key = ENV['BRAINZLAB_SECRET_KEY']
57
66
 
58
67
  # Environment
@@ -289,6 +298,8 @@ end
289
298
  | `BRAINZLAB_SERVICE` | Service name |
290
299
  | `BRAINZLAB_APP_NAME` | App name for auto-provisioning |
291
300
  | `BRAINZLAB_DEBUG` | Enable debug logging (`true`/`false`) |
301
+ | `BRAINZLAB_MODE` | SDK mode: `production` (default) or `development` (offline) |
302
+ | `BRAINZLAB_DEV_DB_PATH` | SQLite database path for development mode |
292
303
  | `RECALL_URL` | Custom Recall endpoint |
293
304
  | `REFLEX_URL` | Custom Reflex endpoint |
294
305
  | `PULSE_URL` | Custom Pulse endpoint |
@@ -303,12 +314,217 @@ config.scrub_fields = [:password, :password_confirmation, :token, :api_key, :sec
303
314
 
304
315
  ## Debug Mode
305
316
 
306
- Enable debug mode to see SDK activity:
317
+ Debug mode provides detailed visibility into SDK operations, including all API requests and responses with timing information. This is invaluable for troubleshooting integration issues.
318
+
319
+ ### Enabling Debug Mode
320
+
321
+ ```ruby
322
+ # In your initializer
323
+ BrainzLab.configure do |config|
324
+ config.debug = true
325
+ end
326
+
327
+ # Or via environment variable
328
+ # BRAINZLAB_DEBUG=true
329
+ ```
330
+
331
+ ### Debug Output Format
332
+
333
+ When debug mode is enabled, you'll see colorized output in your terminal:
334
+
335
+ ```
336
+ [BrainzLab] 12:34:56 -> Recall POST /api/v1/logs (count: 5)
337
+ [BrainzLab] 12:34:56 <- Recall 200 OK (45ms)
338
+
339
+ [BrainzLab] 12:34:57 -> Reflex POST /api/v1/errors (exception: RuntimeError)
340
+ [BrainzLab] 12:34:57 <- Reflex 201 Created (23ms)
341
+
342
+ [BrainzLab] 12:34:58 -> Pulse POST /api/v1/traces (name: GET /users)
343
+ [BrainzLab] 12:34:58 <- Pulse 200 OK (18ms)
344
+ ```
345
+
346
+ The output includes:
347
+ - Timestamp for each operation
348
+ - Service name (Recall, Reflex, Pulse, etc.)
349
+ - Request method and path
350
+ - Payload summary (log count, exception type, etc.)
351
+ - Response status code and message
352
+ - Request duration with color coding (green < 100ms, yellow < 1s, red > 1s)
353
+
354
+ ### Custom Logger
355
+
356
+ You can provide your own logger to capture debug output:
357
+
358
+ ```ruby
359
+ BrainzLab.configure do |config|
360
+ config.debug = true
361
+ config.logger = Rails.logger
362
+ # Or any Logger-compatible object
363
+ config.logger = Logger.new('log/brainzlab.log')
364
+ end
365
+ ```
366
+
367
+ ### Debug Callbacks
368
+
369
+ For advanced debugging and monitoring, you can hook into SDK operations:
370
+
371
+ ```ruby
372
+ BrainzLab.configure do |config|
373
+ # Called before each API request
374
+ config.on_send = ->(service, method, path, payload) {
375
+ Rails.logger.debug "[BrainzLab] Sending to #{service}: #{method} #{path}"
376
+
377
+ # You can use this to:
378
+ # - Log all outgoing requests
379
+ # - Send metrics to your monitoring system
380
+ # - Add custom tracing
381
+ }
382
+
383
+ # Called when an SDK error occurs
384
+ config.on_error = ->(error, context) {
385
+ Rails.logger.error "[BrainzLab] Error in #{context[:service]}: #{error.message}"
386
+
387
+ # You can use this to:
388
+ # - Alert on SDK failures
389
+ # - Track error rates
390
+ # - Fallback to alternative logging
391
+
392
+ # Note: This is for SDK errors, not application errors
393
+ # Application errors are sent to Reflex as normal
394
+ }
395
+ end
396
+ ```
397
+
398
+ ### Programmatic Debug Logging
399
+
400
+ You can also use the Debug module directly:
401
+
402
+ ```ruby
403
+ # Log a debug message (only outputs when debug=true)
404
+ BrainzLab::Debug.log("Custom message", level: :info)
405
+ BrainzLab::Debug.log("Something went wrong", level: :error, error_code: 500)
406
+
407
+ # Measure operation timing
408
+ BrainzLab::Debug.measure(:custom, "expensive_operation") do
409
+ # Your code here
410
+ end
411
+
412
+ # Check if debug mode is enabled
413
+ if BrainzLab::Debug.enabled?
414
+ # Perform additional debug operations
415
+ end
416
+ ```
417
+
418
+ ### Debug Output Levels
419
+
420
+ Debug messages are color-coded by level:
421
+ - **DEBUG** (gray) - Verbose internal operations
422
+ - **INFO** (cyan) - Normal operations
423
+ - **WARN** (yellow) - Potential issues
424
+ - **ERROR** (red) - Failed operations
425
+
426
+ ## Development Mode
427
+
428
+ Development mode allows you to use the SDK without a BrainzLab server connection. Events are logged to stdout in a readable format and stored locally in a SQLite database.
429
+
430
+ ### Configuration
431
+
432
+ ```ruby
433
+ # config/initializers/brainzlab.rb
434
+ BrainzLab.configure do |config|
435
+ # Enable development mode (works offline)
436
+ config.mode = :development
437
+
438
+ # Optional: customize the SQLite database path (default: tmp/brainzlab.sqlite3)
439
+ config.development_db_path = 'tmp/brainzlab_dev.sqlite3'
440
+
441
+ # Other settings still apply
442
+ config.environment = Rails.env
443
+ config.service = 'my-app'
444
+ end
445
+ ```
446
+
447
+ Or use the environment variable:
448
+
449
+ ```bash
450
+ export BRAINZLAB_MODE=development
451
+ ```
452
+
453
+ ### Features
454
+
455
+ In development mode:
456
+
457
+ - **No server connection required** - Works completely offline
458
+ - **Stdout logging** - All events are pretty-printed to the console with colors
459
+ - **Local storage** - Events are stored in SQLite at `tmp/brainzlab.sqlite3`
460
+ - **Queryable** - Use `BrainzLab.development_events` to query stored events
461
+
462
+ ### Querying Events
307
463
 
308
464
  ```ruby
309
- config.debug = true
310
- # Or set BRAINZLAB_DEBUG=true
465
+ # Get all events
466
+ events = BrainzLab.development_events
467
+
468
+ # Filter by service
469
+ logs = BrainzLab.development_events(service: :recall)
470
+ errors = BrainzLab.development_events(service: :reflex)
471
+ traces = BrainzLab.development_events(service: :pulse)
472
+
473
+ # Filter by event type
474
+ BrainzLab.development_events(event_type: 'log')
475
+ BrainzLab.development_events(event_type: 'error')
476
+ BrainzLab.development_events(event_type: 'trace')
477
+
478
+ # Filter by time
479
+ BrainzLab.development_events(since: 1.hour.ago)
480
+
481
+ # Limit results
482
+ BrainzLab.development_events(limit: 10)
483
+
484
+ # Combine filters
485
+ BrainzLab.development_events(
486
+ service: :recall,
487
+ since: 30.minutes.ago,
488
+ limit: 50
489
+ )
490
+
491
+ # Get stats by service
492
+ BrainzLab.development_stats
493
+ # => { recall: 42, reflex: 3, pulse: 15 }
494
+
495
+ # Clear all stored events
496
+ BrainzLab.clear_development_events!
497
+ ```
498
+
499
+ ### Console Output
500
+
501
+ In development mode, events are pretty-printed to stdout:
502
+
311
503
  ```
504
+ [14:32:15.123] [RECALL] log [INFO] User signed up
505
+ user_id: 123
506
+ data: {email: "user@example.com"}
507
+
508
+ [14:32:16.456] [REFLEX] error RuntimeError: Something went wrong
509
+ error_class: "RuntimeError"
510
+ environment: "development"
511
+ request_id: "abc-123"
512
+
513
+ [14:32:17.789] [PULSE] trace GET /users (45.2ms)
514
+ request_method: "GET"
515
+ request_path: "/users"
516
+ status: 200
517
+ db_ms: 12.3
518
+ ```
519
+
520
+ ### Use Cases
521
+
522
+ Development mode is useful for:
523
+
524
+ - **Local development** without setting up a BrainzLab account
525
+ - **Testing** SDK integration in CI/CD pipelines
526
+ - **Debugging** to inspect exactly what events would be sent
527
+ - **Offline development** when working without internet access
312
528
 
313
529
  ## Self-Hosted
314
530
 
@@ -200,7 +200,27 @@ module BrainzLab
200
200
  end
201
201
 
202
202
  def log_error(operation, error)
203
- BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{error.message}")
203
+ structured_error = ErrorHandler.wrap(error, service: 'Beacon', operation: operation)
204
+ BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{structured_error.message}")
205
+
206
+ # Call on_error callback if configured
207
+ if @config.on_error
208
+ @config.on_error.call(structured_error, { service: 'Beacon', operation: operation })
209
+ end
210
+ end
211
+
212
+ def handle_response_error(response, operation)
213
+ return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent)
214
+
215
+ structured_error = ErrorHandler.from_response(response, service: 'Beacon', operation: operation)
216
+ BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{structured_error.message}")
217
+
218
+ # Call on_error callback if configured
219
+ if @config.on_error
220
+ @config.on_error.call(structured_error, { service: 'Beacon', operation: operation })
221
+ end
222
+
223
+ structured_error
204
224
  end
205
225
  end
206
226
  end
@@ -7,6 +7,9 @@ module BrainzLab
7
7
  # recall_min_level has a custom setter with validation
8
8
  attr_reader :recall_min_level
9
9
 
10
+ # mode has a custom setter with validation
11
+ attr_reader :mode
12
+
10
13
  attr_accessor :secret_key,
11
14
  :environment,
12
15
  :service,
@@ -106,6 +109,8 @@ module BrainzLab
106
109
  :synapse_auto_provision,
107
110
  :scrub_fields,
108
111
  :logger,
112
+ :on_error,
113
+ :on_send,
109
114
  :instrument_http,
110
115
  :instrument_active_record,
111
116
  :instrument_redis,
@@ -150,7 +155,9 @@ module BrainzLab
150
155
  :devtools_asset_path,
151
156
  :devtools_panel_position,
152
157
  :devtools_expand_by_default,
153
- :rails_instrumentation_handled_externally
158
+ :rails_instrumentation_handled_externally,
159
+ :development_db_path,
160
+ :development_log_output
154
161
 
155
162
  # Services that should not track themselves to avoid circular dependencies
156
163
  SELF_TRACKING_SERVICES = {
@@ -180,6 +187,13 @@ module BrainzLab
180
187
  # Debug mode - enables verbose logging
181
188
  @debug = ENV['BRAINZLAB_DEBUG'] == 'true'
182
189
 
190
+ # SDK mode - :production (default) or :development (offline, local storage)
191
+ @mode = ENV['BRAINZLAB_MODE']&.to_sym || :production
192
+
193
+ # Development mode settings
194
+ @development_db_path = ENV['BRAINZLAB_DEV_DB_PATH'] || 'tmp/brainzlab.sqlite3'
195
+ @development_log_output = $stdout
196
+
183
197
  # Disable self-tracking - prevents services from tracking to themselves
184
198
  # e.g., Recall won't log to itself, Reflex won't track errors to itself
185
199
  @disable_self_tracking = ENV.fetch('BRAINZLAB_DISABLE_SELF_TRACKING', 'true') == 'true'
@@ -306,6 +320,12 @@ module BrainzLab
306
320
  # Internal logger for debugging SDK issues
307
321
  @logger = nil
308
322
 
323
+ # Debug callbacks
324
+ # Called when an SDK error occurs (lambda/proc receiving error object and context hash)
325
+ @on_error = nil
326
+ # Called before each API request (lambda/proc receiving service, method, path, and payload)
327
+ @on_send = nil
328
+
309
329
  # Instrumentation
310
330
  @instrument_http = true # Enable HTTP client instrumentation (Net::HTTP, Faraday, HTTParty)
311
331
  @instrument_active_record = true # AR breadcrumbs for Reflex
@@ -363,11 +383,40 @@ module BrainzLab
363
383
 
364
384
  def recall_min_level=(level)
365
385
  level = level.to_sym
366
- raise ArgumentError, "Invalid level: #{level}" unless LEVELS.include?(level)
386
+ unless LEVELS.include?(level)
387
+ raise ValidationError.new(
388
+ "Invalid log level: #{level}",
389
+ hint: "Valid log levels are: #{LEVELS.join(', ')}",
390
+ code: 'invalid_log_level',
391
+ field: 'recall_min_level',
392
+ context: { provided: level, valid_values: LEVELS }
393
+ )
394
+ end
367
395
 
368
396
  @recall_min_level = level
369
397
  end
370
398
 
399
+ MODES = %i[production development].freeze
400
+
401
+ def mode=(mode)
402
+ mode = mode.to_sym
403
+ unless MODES.include?(mode)
404
+ raise ValidationError.new(
405
+ "Invalid mode: #{mode}",
406
+ hint: "Valid modes are: #{MODES.join(', ')}. Use :development for offline mode with local storage.",
407
+ code: 'invalid_mode',
408
+ field: 'mode',
409
+ context: { provided: mode, valid_values: MODES }
410
+ )
411
+ end
412
+
413
+ @mode = mode
414
+ end
415
+
416
+ def development_mode?
417
+ @mode == :development
418
+ end
419
+
371
420
  def level_enabled?(level)
372
421
  LEVELS.index(level.to_sym) >= LEVELS.index(@recall_min_level)
373
422
  end
@@ -132,7 +132,27 @@ module BrainzLab
132
132
  end
133
133
 
134
134
  def log_error(operation, error)
135
- BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{error.message}")
135
+ structured_error = ErrorHandler.wrap(error, service: 'Cortex', operation: operation)
136
+ BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{structured_error.message}")
137
+
138
+ # Call on_error callback if configured
139
+ if @config.on_error
140
+ @config.on_error.call(structured_error, { service: 'Cortex', operation: operation })
141
+ end
142
+ end
143
+
144
+ def handle_response_error(response, operation)
145
+ return if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated) || response.is_a?(Net::HTTPNoContent)
146
+
147
+ structured_error = ErrorHandler.from_response(response, service: 'Cortex', operation: operation)
148
+ BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{structured_error.message}")
149
+
150
+ # Call on_error callback if configured
151
+ if @config.on_error
152
+ @config.on_error.call(structured_error, { service: 'Cortex', operation: operation })
153
+ end
154
+
155
+ structured_error
136
156
  end
137
157
  end
138
158
  end