brainzlab 0.1.0
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 +52 -0
- data/LICENSE +26 -0
- data/README.md +311 -0
- data/lib/brainzlab/configuration.rb +215 -0
- data/lib/brainzlab/context.rb +91 -0
- data/lib/brainzlab/instrumentation/action_mailer.rb +181 -0
- data/lib/brainzlab/instrumentation/active_record.rb +111 -0
- data/lib/brainzlab/instrumentation/delayed_job.rb +236 -0
- data/lib/brainzlab/instrumentation/elasticsearch.rb +210 -0
- data/lib/brainzlab/instrumentation/faraday.rb +182 -0
- data/lib/brainzlab/instrumentation/grape.rb +293 -0
- data/lib/brainzlab/instrumentation/graphql.rb +251 -0
- data/lib/brainzlab/instrumentation/httparty.rb +194 -0
- data/lib/brainzlab/instrumentation/mongodb.rb +187 -0
- data/lib/brainzlab/instrumentation/net_http.rb +109 -0
- data/lib/brainzlab/instrumentation/redis.rb +331 -0
- data/lib/brainzlab/instrumentation/sidekiq.rb +264 -0
- data/lib/brainzlab/instrumentation.rb +132 -0
- data/lib/brainzlab/pulse/client.rb +132 -0
- data/lib/brainzlab/pulse/instrumentation.rb +364 -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 +224 -0
- data/lib/brainzlab/rails/log_formatter.rb +801 -0
- data/lib/brainzlab/rails/log_subscriber.rb +341 -0
- data/lib/brainzlab/rails/railtie.rb +590 -0
- data/lib/brainzlab/recall/buffer.rb +64 -0
- data/lib/brainzlab/recall/client.rb +86 -0
- data/lib/brainzlab/recall/logger.rb +118 -0
- data/lib/brainzlab/recall/provisioner.rb +113 -0
- data/lib/brainzlab/recall.rb +155 -0
- data/lib/brainzlab/reflex/breadcrumbs.rb +55 -0
- data/lib/brainzlab/reflex/client.rb +85 -0
- data/lib/brainzlab/reflex/provisioner.rb +116 -0
- data/lib/brainzlab/reflex.rb +374 -0
- data/lib/brainzlab/version.rb +5 -0
- data/lib/brainzlab-sdk.rb +3 -0
- data/lib/brainzlab.rb +140 -0
- data/lib/generators/brainzlab/install/install_generator.rb +61 -0
- data/lib/generators/brainzlab/install/templates/brainzlab.rb.tt +77 -0
- metadata +159 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dea1ca28ddfd1614e67dd24d5a8cb36663e4cf3b75d00dabfa9ff9e01d95e2cc
|
|
4
|
+
data.tar.gz: 753cf41c654d57476fdcd915e1bc088c0fe9a2242cf4464431b520c375012884
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 91382c001767b6f0ca913cb5e12956d7a3664d0a098c94540948796d89db44e6b08c12a5fa9bdf8666c5909822d97bfda68638aaf20de945614a0ee329ed20e5
|
|
7
|
+
data.tar.gz: ec17f7bf47291eb1cb00f0173972e866f61634d21d4608e432f7412c866273abb975cd6e9e32a50022377129c489f6819edd21fcbed3d6de6e753113fb79cd0d
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.1.0] - 2025-01-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Recall** - Structured logging
|
|
10
|
+
- Log levels (debug, info, warn, error, fatal)
|
|
11
|
+
- Buffered batch sending
|
|
12
|
+
- Auto-provisioning support
|
|
13
|
+
- Rails integration
|
|
14
|
+
|
|
15
|
+
- **Reflex** - Error tracking
|
|
16
|
+
- Exception capturing with context
|
|
17
|
+
- Breadcrumbs support
|
|
18
|
+
- User context tracking
|
|
19
|
+
- Sample rate and before_send hooks
|
|
20
|
+
- Custom fingerprinting
|
|
21
|
+
- Auto-provisioning support
|
|
22
|
+
|
|
23
|
+
- **Pulse** - APM & Distributed Tracing
|
|
24
|
+
- Request tracing with breakdown
|
|
25
|
+
- Distributed tracing (W3C Trace Context + B3)
|
|
26
|
+
- Auto-provisioning support
|
|
27
|
+
|
|
28
|
+
- **Instrumentation** (13 libraries)
|
|
29
|
+
- Rails/Rack middleware
|
|
30
|
+
- Active Record (SQL queries)
|
|
31
|
+
- Net::HTTP
|
|
32
|
+
- Faraday
|
|
33
|
+
- HTTParty
|
|
34
|
+
- Redis (v4 and v5+)
|
|
35
|
+
- Sidekiq (server and client middleware)
|
|
36
|
+
- Delayed::Job
|
|
37
|
+
- GraphQL (query and field tracing)
|
|
38
|
+
- Grape API
|
|
39
|
+
- MongoDB/Mongoid
|
|
40
|
+
- Elasticsearch/OpenSearch
|
|
41
|
+
- ActionMailer
|
|
42
|
+
|
|
43
|
+
- **Configuration**
|
|
44
|
+
- Environment variable support
|
|
45
|
+
- Per-product enable/disable
|
|
46
|
+
- Sensitive field scrubbing
|
|
47
|
+
- Debug mode
|
|
48
|
+
|
|
49
|
+
- **Rails Integration**
|
|
50
|
+
- Automatic setup via Railtie
|
|
51
|
+
- Request context propagation
|
|
52
|
+
- User context from current_user
|
data/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Ossassy License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Brainz Lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
No licensee or downstream recipient may use the Software (including any modified
|
|
16
|
+
or derivative versions) to directly compete with the original Licensor by
|
|
17
|
+
offering it to third parties as a hosted, managed, or Software-as-a-Service
|
|
18
|
+
(SaaS) product or cloud service where the primary value of the service is the
|
|
19
|
+
functionality of the Software itself.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
23
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
24
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
25
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
26
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# BrainzLab Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for [BrainzLab](https://brainzlab.ai) - the complete observability platform.
|
|
4
|
+
|
|
5
|
+
- **Recall** - Structured logging
|
|
6
|
+
- **Reflex** - Error tracking
|
|
7
|
+
- **Pulse** - APM & distributed tracing
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add to your Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'brainzlab'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bundle install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### Configuration
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# config/initializers/brainzlab.rb
|
|
29
|
+
BrainzLab.configure do |config|
|
|
30
|
+
# Authentication (required)
|
|
31
|
+
config.secret_key = ENV['BRAINZLAB_SECRET_KEY']
|
|
32
|
+
|
|
33
|
+
# Environment
|
|
34
|
+
config.environment = Rails.env
|
|
35
|
+
config.service = 'my-app'
|
|
36
|
+
|
|
37
|
+
# Enable/disable products
|
|
38
|
+
config.recall_enabled = true # Logging
|
|
39
|
+
config.reflex_enabled = true # Error tracking
|
|
40
|
+
config.pulse_enabled = true # APM
|
|
41
|
+
|
|
42
|
+
# Auto-provisioning (creates projects automatically)
|
|
43
|
+
config.recall_auto_provision = true
|
|
44
|
+
config.reflex_auto_provision = true
|
|
45
|
+
config.pulse_auto_provision = true
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Recall - Structured Logging
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# Log levels
|
|
53
|
+
BrainzLab::Recall.debug("Debug message", details: "...")
|
|
54
|
+
BrainzLab::Recall.info("User signed up", user_id: user.id)
|
|
55
|
+
BrainzLab::Recall.warn("Rate limit approaching", current: 95, limit: 100)
|
|
56
|
+
BrainzLab::Recall.error("Payment failed", error: e.message, amount: 99.99)
|
|
57
|
+
BrainzLab::Recall.fatal("Database connection lost")
|
|
58
|
+
|
|
59
|
+
# With context
|
|
60
|
+
BrainzLab::Recall.info("Order created",
|
|
61
|
+
order_id: order.id,
|
|
62
|
+
user_id: user.id,
|
|
63
|
+
total: order.total,
|
|
64
|
+
items: order.items.count
|
|
65
|
+
)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Configuration Options
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
config.recall_min_level = :info # Minimum log level (:debug, :info, :warn, :error, :fatal)
|
|
72
|
+
config.recall_buffer_size = 50 # Batch size before flush
|
|
73
|
+
config.recall_flush_interval = 5 # Seconds between flushes
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Reflex - Error Tracking
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
# Capture exceptions
|
|
80
|
+
begin
|
|
81
|
+
risky_operation
|
|
82
|
+
rescue => e
|
|
83
|
+
BrainzLab::Reflex.capture(e,
|
|
84
|
+
user_id: current_user.id,
|
|
85
|
+
order_id: order.id
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Add breadcrumbs for context
|
|
90
|
+
BrainzLab::Reflex.add_breadcrumb("User clicked checkout",
|
|
91
|
+
category: "ui.click",
|
|
92
|
+
data: { button: "checkout" }
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Set user context
|
|
96
|
+
BrainzLab::Reflex.set_user(
|
|
97
|
+
id: user.id,
|
|
98
|
+
email: user.email,
|
|
99
|
+
plan: user.plan
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Add tags
|
|
103
|
+
BrainzLab::Reflex.set_tags(
|
|
104
|
+
environment: "production",
|
|
105
|
+
region: "us-east-1"
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Configuration Options
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
config.reflex_excluded_exceptions = ['ActiveRecord::RecordNotFound']
|
|
113
|
+
config.reflex_sample_rate = 1.0 # 1.0 = 100%, 0.5 = 50%
|
|
114
|
+
config.reflex_before_send = ->(event) {
|
|
115
|
+
# Modify or filter events
|
|
116
|
+
event[:tags][:custom] = 'value'
|
|
117
|
+
event # Return nil to drop the event
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Pulse - APM & Distributed Tracing
|
|
122
|
+
|
|
123
|
+
Pulse automatically instruments your application to track performance.
|
|
124
|
+
|
|
125
|
+
### Automatic Instrumentation
|
|
126
|
+
|
|
127
|
+
The SDK automatically instruments:
|
|
128
|
+
|
|
129
|
+
| Library | Description |
|
|
130
|
+
|---------|-------------|
|
|
131
|
+
| Rails/Rack | Request tracing with breakdown |
|
|
132
|
+
| Active Record | SQL queries with timing |
|
|
133
|
+
| Net::HTTP | Outbound HTTP calls |
|
|
134
|
+
| Faraday | HTTP client requests |
|
|
135
|
+
| HTTParty | HTTP client requests |
|
|
136
|
+
| Redis | Redis commands |
|
|
137
|
+
| Sidekiq | Background job processing |
|
|
138
|
+
| Delayed::Job | Background job processing |
|
|
139
|
+
| GraphQL | Query and field resolution |
|
|
140
|
+
| Grape | API endpoint tracing |
|
|
141
|
+
| MongoDB | Database operations |
|
|
142
|
+
| Elasticsearch | Search operations |
|
|
143
|
+
| ActionMailer | Email delivery |
|
|
144
|
+
|
|
145
|
+
### Configuration Options
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# Enable/disable specific instrumentations
|
|
149
|
+
config.instrument_http = true # Net::HTTP, Faraday, HTTParty
|
|
150
|
+
config.instrument_active_record = true # SQL queries
|
|
151
|
+
config.instrument_redis = true # Redis commands
|
|
152
|
+
config.instrument_sidekiq = true # Sidekiq jobs
|
|
153
|
+
config.instrument_graphql = true # GraphQL queries
|
|
154
|
+
config.instrument_mongodb = true # MongoDB operations
|
|
155
|
+
config.instrument_elasticsearch = true # Elasticsearch queries
|
|
156
|
+
config.instrument_action_mailer = true # Email delivery
|
|
157
|
+
config.instrument_delayed_job = true # Delayed::Job
|
|
158
|
+
config.instrument_grape = true # Grape API
|
|
159
|
+
|
|
160
|
+
# Filtering
|
|
161
|
+
config.http_ignore_hosts = ['localhost', '127.0.0.1']
|
|
162
|
+
config.redis_ignore_commands = ['ping', 'info']
|
|
163
|
+
config.pulse_excluded_paths = ['/health', '/ping', '/up', '/assets']
|
|
164
|
+
config.pulse_sample_rate = 1.0 # 1.0 = 100%
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Distributed Tracing
|
|
168
|
+
|
|
169
|
+
Pulse supports distributed tracing across services using W3C Trace Context and B3 propagation.
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
# Extracting trace context from incoming requests (automatic in Rails)
|
|
173
|
+
context = BrainzLab::Pulse.extract!(request.headers)
|
|
174
|
+
|
|
175
|
+
# Injecting trace context into outgoing requests (automatic with instrumentation)
|
|
176
|
+
BrainzLab::Pulse.inject!(headers)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Custom Spans
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
BrainzLab::Pulse.trace("process_payment", kind: "payment") do |span|
|
|
183
|
+
span[:data] = { amount: 99.99, currency: "USD" }
|
|
184
|
+
process_payment(order)
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Rails Integration
|
|
189
|
+
|
|
190
|
+
The SDK automatically integrates with Rails when loaded:
|
|
191
|
+
|
|
192
|
+
- Request context (request_id, path, method, params)
|
|
193
|
+
- Exception reporting to Reflex
|
|
194
|
+
- Performance tracing with Pulse
|
|
195
|
+
- User context from `current_user`
|
|
196
|
+
|
|
197
|
+
### Setting User Context
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
class ApplicationController < ActionController::Base
|
|
201
|
+
before_action :set_brainzlab_context
|
|
202
|
+
|
|
203
|
+
private
|
|
204
|
+
|
|
205
|
+
def set_brainzlab_context
|
|
206
|
+
if current_user
|
|
207
|
+
BrainzLab.set_user(
|
|
208
|
+
id: current_user.id,
|
|
209
|
+
email: current_user.email,
|
|
210
|
+
name: current_user.name
|
|
211
|
+
)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Sidekiq Integration
|
|
218
|
+
|
|
219
|
+
For Sidekiq, the SDK automatically:
|
|
220
|
+
|
|
221
|
+
- Traces job execution with queue wait time
|
|
222
|
+
- Propagates trace context between web and worker
|
|
223
|
+
- Captures job failures to Reflex
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
# config/initializers/sidekiq.rb
|
|
227
|
+
# Instrumentation is automatic, but you can configure:
|
|
228
|
+
|
|
229
|
+
BrainzLab.configure do |config|
|
|
230
|
+
config.instrument_sidekiq = true
|
|
231
|
+
end
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Grape API Integration
|
|
235
|
+
|
|
236
|
+
For Grape APIs, you can use the middleware:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
class API < Grape::API
|
|
240
|
+
use BrainzLab::Instrumentation::GrapeInstrumentation::Middleware
|
|
241
|
+
|
|
242
|
+
# Your API endpoints...
|
|
243
|
+
end
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## GraphQL Integration
|
|
247
|
+
|
|
248
|
+
For GraphQL-Ruby 2.0+, add the tracer:
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
class MySchema < GraphQL::Schema
|
|
252
|
+
trace_with BrainzLab::Instrumentation::GraphQLInstrumentation::Tracer
|
|
253
|
+
|
|
254
|
+
# Your schema...
|
|
255
|
+
end
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Environment Variables
|
|
259
|
+
|
|
260
|
+
| Variable | Description |
|
|
261
|
+
|----------|-------------|
|
|
262
|
+
| `BRAINZLAB_SECRET_KEY` | API key for authentication |
|
|
263
|
+
| `BRAINZLAB_ENVIRONMENT` | Environment name (default: auto-detect) |
|
|
264
|
+
| `BRAINZLAB_SERVICE` | Service name |
|
|
265
|
+
| `BRAINZLAB_APP_NAME` | App name for auto-provisioning |
|
|
266
|
+
| `BRAINZLAB_DEBUG` | Enable debug logging (`true`/`false`) |
|
|
267
|
+
| `RECALL_URL` | Custom Recall endpoint |
|
|
268
|
+
| `REFLEX_URL` | Custom Reflex endpoint |
|
|
269
|
+
| `PULSE_URL` | Custom Pulse endpoint |
|
|
270
|
+
|
|
271
|
+
## Scrubbing Sensitive Data
|
|
272
|
+
|
|
273
|
+
The SDK automatically scrubs sensitive fields:
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
config.scrub_fields = [:password, :password_confirmation, :token, :api_key, :secret]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Debug Mode
|
|
280
|
+
|
|
281
|
+
Enable debug mode to see SDK activity:
|
|
282
|
+
|
|
283
|
+
```ruby
|
|
284
|
+
config.debug = true
|
|
285
|
+
# Or set BRAINZLAB_DEBUG=true
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Self-Hosted
|
|
289
|
+
|
|
290
|
+
For self-hosted BrainzLab installations:
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
BrainzLab.configure do |config|
|
|
294
|
+
config.recall_url = 'https://recall.your-domain.com'
|
|
295
|
+
config.reflex_url = 'https://reflex.your-domain.com'
|
|
296
|
+
config.pulse_url = 'https://pulse.your-domain.com'
|
|
297
|
+
end
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Documentation
|
|
301
|
+
|
|
302
|
+
Full documentation: [docs.brainzlab.ai](https://docs.brainzlab.ai)
|
|
303
|
+
|
|
304
|
+
- [Installation Guide](https://docs.brainzlab.ai/sdk/ruby/installation)
|
|
305
|
+
- [Recall (Logging)](https://docs.brainzlab.ai/sdk/ruby/recall)
|
|
306
|
+
- [Reflex (Errors)](https://docs.brainzlab.ai/sdk/ruby/reflex)
|
|
307
|
+
- [Pulse (APM)](https://docs.brainzlab.ai/sdk/ruby/pulse)
|
|
308
|
+
|
|
309
|
+
## License
|
|
310
|
+
|
|
311
|
+
Ossassy License - see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
class Configuration
|
|
5
|
+
LEVELS = %i[debug info warn error fatal].freeze
|
|
6
|
+
|
|
7
|
+
attr_accessor :secret_key,
|
|
8
|
+
:environment,
|
|
9
|
+
:service,
|
|
10
|
+
:host,
|
|
11
|
+
:commit,
|
|
12
|
+
:branch,
|
|
13
|
+
:app_name,
|
|
14
|
+
:debug,
|
|
15
|
+
:recall_enabled,
|
|
16
|
+
:recall_url,
|
|
17
|
+
:recall_min_level,
|
|
18
|
+
:recall_buffer_size,
|
|
19
|
+
:recall_flush_interval,
|
|
20
|
+
:recall_master_key,
|
|
21
|
+
:recall_auto_provision,
|
|
22
|
+
:reflex_enabled,
|
|
23
|
+
:reflex_url,
|
|
24
|
+
:reflex_api_key,
|
|
25
|
+
:reflex_master_key,
|
|
26
|
+
:reflex_auto_provision,
|
|
27
|
+
:reflex_excluded_exceptions,
|
|
28
|
+
:reflex_before_send,
|
|
29
|
+
:reflex_sample_rate,
|
|
30
|
+
:reflex_fingerprint,
|
|
31
|
+
:pulse_enabled,
|
|
32
|
+
:pulse_url,
|
|
33
|
+
:pulse_api_key,
|
|
34
|
+
:pulse_master_key,
|
|
35
|
+
:pulse_auto_provision,
|
|
36
|
+
:pulse_buffer_size,
|
|
37
|
+
:pulse_flush_interval,
|
|
38
|
+
:pulse_sample_rate,
|
|
39
|
+
:pulse_excluded_paths,
|
|
40
|
+
:scrub_fields,
|
|
41
|
+
:logger,
|
|
42
|
+
:instrument_http,
|
|
43
|
+
:instrument_active_record,
|
|
44
|
+
:instrument_redis,
|
|
45
|
+
:instrument_sidekiq,
|
|
46
|
+
:instrument_graphql,
|
|
47
|
+
:instrument_mongodb,
|
|
48
|
+
:instrument_elasticsearch,
|
|
49
|
+
:instrument_action_mailer,
|
|
50
|
+
:instrument_delayed_job,
|
|
51
|
+
:instrument_grape,
|
|
52
|
+
:http_ignore_hosts,
|
|
53
|
+
:redis_ignore_commands,
|
|
54
|
+
:log_formatter_enabled,
|
|
55
|
+
:log_formatter_colors,
|
|
56
|
+
:log_formatter_hide_assets,
|
|
57
|
+
:log_formatter_compact_assets,
|
|
58
|
+
:log_formatter_show_params
|
|
59
|
+
|
|
60
|
+
def initialize
|
|
61
|
+
# Authentication
|
|
62
|
+
@secret_key = ENV["BRAINZLAB_SECRET_KEY"]
|
|
63
|
+
|
|
64
|
+
# Environment
|
|
65
|
+
@environment = ENV["BRAINZLAB_ENVIRONMENT"] || detect_environment
|
|
66
|
+
@service = ENV["BRAINZLAB_SERVICE"]
|
|
67
|
+
@host = ENV["BRAINZLAB_HOST"] || detect_host
|
|
68
|
+
|
|
69
|
+
# App name for auto-provisioning
|
|
70
|
+
@app_name = ENV["BRAINZLAB_APP_NAME"]
|
|
71
|
+
|
|
72
|
+
# Git context
|
|
73
|
+
@commit = ENV["GIT_COMMIT"] || ENV["COMMIT_SHA"] || detect_git_commit
|
|
74
|
+
@branch = ENV["GIT_BRANCH"] || ENV["BRANCH_NAME"] || detect_git_branch
|
|
75
|
+
|
|
76
|
+
# Debug mode - enables verbose logging
|
|
77
|
+
@debug = ENV["BRAINZLAB_DEBUG"] == "true"
|
|
78
|
+
|
|
79
|
+
# Recall settings
|
|
80
|
+
@recall_enabled = true
|
|
81
|
+
@recall_url = ENV["RECALL_URL"] || "https://recall.brainzlab.ai"
|
|
82
|
+
@recall_min_level = :debug
|
|
83
|
+
@recall_buffer_size = 50
|
|
84
|
+
@recall_flush_interval = 5
|
|
85
|
+
@recall_master_key = ENV["RECALL_MASTER_KEY"]
|
|
86
|
+
@recall_auto_provision = true
|
|
87
|
+
|
|
88
|
+
# Reflex settings
|
|
89
|
+
@reflex_enabled = true
|
|
90
|
+
@reflex_url = ENV["REFLEX_URL"] || "https://reflex.brainzlab.ai"
|
|
91
|
+
@reflex_api_key = ENV["REFLEX_API_KEY"]
|
|
92
|
+
@reflex_master_key = ENV["REFLEX_MASTER_KEY"]
|
|
93
|
+
@reflex_auto_provision = true
|
|
94
|
+
@reflex_excluded_exceptions = []
|
|
95
|
+
@reflex_before_send = nil
|
|
96
|
+
@reflex_sample_rate = nil
|
|
97
|
+
@reflex_fingerprint = nil # Custom fingerprint callback
|
|
98
|
+
|
|
99
|
+
# Pulse settings
|
|
100
|
+
@pulse_enabled = true
|
|
101
|
+
@pulse_url = ENV["PULSE_URL"] || "https://pulse.brainzlab.ai"
|
|
102
|
+
@pulse_api_key = ENV["PULSE_API_KEY"]
|
|
103
|
+
@pulse_master_key = ENV["PULSE_MASTER_KEY"]
|
|
104
|
+
@pulse_auto_provision = true
|
|
105
|
+
@pulse_buffer_size = 50
|
|
106
|
+
@pulse_flush_interval = 5
|
|
107
|
+
@pulse_sample_rate = nil
|
|
108
|
+
@pulse_excluded_paths = %w[/health /ping /up /assets]
|
|
109
|
+
|
|
110
|
+
# Filtering
|
|
111
|
+
@scrub_fields = %i[password password_confirmation token api_key secret]
|
|
112
|
+
|
|
113
|
+
# Internal logger for debugging SDK issues
|
|
114
|
+
@logger = nil
|
|
115
|
+
|
|
116
|
+
# Instrumentation
|
|
117
|
+
@instrument_http = true # Enable HTTP client instrumentation (Net::HTTP, Faraday, HTTParty)
|
|
118
|
+
@instrument_active_record = true # AR breadcrumbs for Reflex
|
|
119
|
+
@instrument_redis = true # Redis command instrumentation
|
|
120
|
+
@instrument_sidekiq = true # Sidekiq job instrumentation
|
|
121
|
+
@instrument_graphql = true # GraphQL query instrumentation
|
|
122
|
+
@instrument_mongodb = true # MongoDB/Mongoid instrumentation
|
|
123
|
+
@instrument_elasticsearch = true # Elasticsearch instrumentation
|
|
124
|
+
@instrument_action_mailer = true # ActionMailer instrumentation
|
|
125
|
+
@instrument_delayed_job = true # Delayed::Job instrumentation
|
|
126
|
+
@instrument_grape = true # Grape API instrumentation
|
|
127
|
+
@http_ignore_hosts = %w[localhost 127.0.0.1]
|
|
128
|
+
@redis_ignore_commands = %w[ping info] # Commands to skip tracking
|
|
129
|
+
|
|
130
|
+
# Log formatter settings
|
|
131
|
+
@log_formatter_enabled = true
|
|
132
|
+
@log_formatter_colors = nil # auto-detect TTY
|
|
133
|
+
@log_formatter_hide_assets = false
|
|
134
|
+
@log_formatter_compact_assets = true
|
|
135
|
+
@log_formatter_show_params = true
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def recall_min_level=(level)
|
|
139
|
+
level = level.to_sym
|
|
140
|
+
raise ArgumentError, "Invalid level: #{level}" unless LEVELS.include?(level)
|
|
141
|
+
|
|
142
|
+
@recall_min_level = level
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def level_enabled?(level)
|
|
146
|
+
LEVELS.index(level.to_sym) >= LEVELS.index(@recall_min_level)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def valid?
|
|
150
|
+
!@secret_key.nil? && !@secret_key.empty?
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def reflex_valid?
|
|
154
|
+
key = reflex_api_key || secret_key
|
|
155
|
+
!key.nil? && !key.empty?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reflex_auth_key
|
|
159
|
+
reflex_api_key || secret_key
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def pulse_valid?
|
|
163
|
+
key = pulse_api_key || secret_key
|
|
164
|
+
!key.nil? && !key.empty?
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def pulse_auth_key
|
|
168
|
+
pulse_api_key || secret_key
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def debug?
|
|
172
|
+
@debug == true
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def debug_log(message)
|
|
176
|
+
return unless debug?
|
|
177
|
+
|
|
178
|
+
log_message = "[BrainzLab::Debug] #{message}"
|
|
179
|
+
if logger
|
|
180
|
+
logger.debug(log_message)
|
|
181
|
+
else
|
|
182
|
+
$stderr.puts(log_message)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
private
|
|
187
|
+
|
|
188
|
+
def detect_environment
|
|
189
|
+
return ::Rails.env.to_s if defined?(::Rails) && ::Rails.respond_to?(:env)
|
|
190
|
+
return ENV["RACK_ENV"] if ENV["RACK_ENV"]
|
|
191
|
+
return ENV["RUBY_ENV"] if ENV["RUBY_ENV"]
|
|
192
|
+
|
|
193
|
+
"development"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def detect_host
|
|
197
|
+
require "socket"
|
|
198
|
+
Socket.gethostname
|
|
199
|
+
rescue StandardError
|
|
200
|
+
nil
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def detect_git_commit
|
|
204
|
+
`git rev-parse HEAD 2>/dev/null`.strip.presence
|
|
205
|
+
rescue StandardError
|
|
206
|
+
nil
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def detect_git_branch
|
|
210
|
+
`git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip.presence
|
|
211
|
+
rescue StandardError
|
|
212
|
+
nil
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
class Context
|
|
5
|
+
THREAD_KEY = :brainzlab_context
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def current
|
|
9
|
+
Thread.current[THREAD_KEY] ||= new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def clear!
|
|
13
|
+
Thread.current[THREAD_KEY] = nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_accessor :user, :request_id, :session_id
|
|
18
|
+
attr_accessor :request_method, :request_path, :request_url, :request_params, :request_headers
|
|
19
|
+
attr_accessor :controller, :action
|
|
20
|
+
attr_reader :extra, :tags, :breadcrumbs
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@user = {}
|
|
24
|
+
@extra = {}
|
|
25
|
+
@tags = {}
|
|
26
|
+
@request_id = nil
|
|
27
|
+
@session_id = nil
|
|
28
|
+
@request_method = nil
|
|
29
|
+
@request_path = nil
|
|
30
|
+
@request_url = nil
|
|
31
|
+
@request_params = nil
|
|
32
|
+
@request_headers = nil
|
|
33
|
+
@controller = nil
|
|
34
|
+
@action = nil
|
|
35
|
+
@stack = []
|
|
36
|
+
@breadcrumbs = Reflex::Breadcrumbs.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def set_user(id: nil, email: nil, name: nil, **extra)
|
|
40
|
+
@user = { id: id, email: email, name: name }.compact.merge(extra)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def set_context(**data)
|
|
44
|
+
@extra.merge!(data)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def set_tags(**data)
|
|
48
|
+
@tags.merge!(data)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def with_context(**data)
|
|
52
|
+
push_context(data)
|
|
53
|
+
yield
|
|
54
|
+
ensure
|
|
55
|
+
pop_context
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_hash
|
|
59
|
+
result = {}
|
|
60
|
+
result[:request_id] = @request_id if @request_id
|
|
61
|
+
result[:session_id] = @session_id if @session_id
|
|
62
|
+
|
|
63
|
+
merged_extra = @extra.dup
|
|
64
|
+
@stack.each { |ctx| merged_extra.merge!(ctx) }
|
|
65
|
+
|
|
66
|
+
result[:user] = @user unless @user.empty?
|
|
67
|
+
result[:tags] = @tags unless @tags.empty?
|
|
68
|
+
result[:context] = merged_extra unless merged_extra.empty?
|
|
69
|
+
|
|
70
|
+
result
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def data_hash
|
|
74
|
+
merged = @extra.dup
|
|
75
|
+
@stack.each { |ctx| merged.merge!(ctx) }
|
|
76
|
+
merged[:user] = @user unless @user.empty?
|
|
77
|
+
merged[:tags] = @tags unless @tags.empty?
|
|
78
|
+
merged
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def push_context(data)
|
|
84
|
+
@stack.push(data)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def pop_context
|
|
88
|
+
@stack.pop
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|