brainzlab 0.1.1 → 0.1.2

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/lib/brainzlab/beacon/client.rb +209 -0
  4. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  5. data/lib/brainzlab/beacon.rb +215 -0
  6. data/lib/brainzlab/configuration.rb +341 -3
  7. data/lib/brainzlab/cortex/cache.rb +59 -0
  8. data/lib/brainzlab/cortex/client.rb +141 -0
  9. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  10. data/lib/brainzlab/cortex.rb +227 -0
  11. data/lib/brainzlab/dendrite/client.rb +232 -0
  12. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  13. data/lib/brainzlab/dendrite.rb +195 -0
  14. data/lib/brainzlab/devtools/assets/devtools.css +1106 -0
  15. data/lib/brainzlab/devtools/assets/devtools.js +322 -0
  16. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  17. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +500 -0
  18. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  19. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  20. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  21. data/lib/brainzlab/devtools/middleware/database_handler.rb +180 -0
  22. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  23. data/lib/brainzlab/devtools/middleware/error_page.rb +376 -0
  24. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +155 -0
  25. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +94 -0
  26. data/lib/brainzlab/devtools.rb +75 -0
  27. data/lib/brainzlab/flux/buffer.rb +96 -0
  28. data/lib/brainzlab/flux/client.rb +70 -0
  29. data/lib/brainzlab/flux/provisioner.rb +57 -0
  30. data/lib/brainzlab/flux.rb +174 -0
  31. data/lib/brainzlab/instrumentation/active_record.rb +18 -1
  32. data/lib/brainzlab/instrumentation/aws.rb +179 -0
  33. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  34. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  35. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  36. data/lib/brainzlab/instrumentation/resque.rb +115 -0
  37. data/lib/brainzlab/instrumentation/solid_queue.rb +198 -0
  38. data/lib/brainzlab/instrumentation/stripe.rb +164 -0
  39. data/lib/brainzlab/instrumentation/typhoeus.rb +104 -0
  40. data/lib/brainzlab/instrumentation.rb +72 -0
  41. data/lib/brainzlab/nerve/client.rb +217 -0
  42. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  43. data/lib/brainzlab/nerve.rb +219 -0
  44. data/lib/brainzlab/pulse/instrumentation.rb +35 -2
  45. data/lib/brainzlab/pulse/propagation.rb +1 -1
  46. data/lib/brainzlab/pulse/tracer.rb +1 -1
  47. data/lib/brainzlab/pulse.rb +1 -1
  48. data/lib/brainzlab/rails/log_subscriber.rb +1 -2
  49. data/lib/brainzlab/rails/railtie.rb +36 -3
  50. data/lib/brainzlab/recall/provisioner.rb +17 -0
  51. data/lib/brainzlab/recall.rb +6 -1
  52. data/lib/brainzlab/reflex.rb +2 -2
  53. data/lib/brainzlab/sentinel/client.rb +218 -0
  54. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  55. data/lib/brainzlab/sentinel.rb +165 -0
  56. data/lib/brainzlab/signal/client.rb +62 -0
  57. data/lib/brainzlab/signal/provisioner.rb +55 -0
  58. data/lib/brainzlab/signal.rb +136 -0
  59. data/lib/brainzlab/synapse/client.rb +290 -0
  60. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  61. data/lib/brainzlab/synapse.rb +270 -0
  62. data/lib/brainzlab/utilities/circuit_breaker.rb +265 -0
  63. data/lib/brainzlab/utilities/health_check.rb +296 -0
  64. data/lib/brainzlab/utilities/log_formatter.rb +256 -0
  65. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  66. data/lib/brainzlab/utilities.rb +17 -0
  67. data/lib/brainzlab/vault/cache.rb +80 -0
  68. data/lib/brainzlab/vault/client.rb +198 -0
  69. data/lib/brainzlab/vault/provisioner.rb +49 -0
  70. data/lib/brainzlab/vault.rb +268 -0
  71. data/lib/brainzlab/version.rb +1 -1
  72. data/lib/brainzlab/vision/client.rb +128 -0
  73. data/lib/brainzlab/vision/provisioner.rb +136 -0
  74. data/lib/brainzlab/vision.rb +157 -0
  75. data/lib/brainzlab.rb +101 -0
  76. metadata +60 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a23ea95f03ef25ac3386a0fbf07ea352a01440afc23ec340625f9932bc1cf6a6
4
- data.tar.gz: c6f73bc4d7c99a1c4eae51dd34a525e8e3d48b4d0ad4a5502a96baf931f5e44a
3
+ metadata.gz: 243321421ac058cfe6ae6c11dc69c2c5d527ff6b3a3ee2095da72953b1f343d8
4
+ data.tar.gz: d1a93e801d19c363305114e3d9a25f441a4983dbc8ef478dca4ed104821a8450
5
5
  SHA512:
6
- metadata.gz: abf6406497997a9de152038c1f7764ba66165681091ce9f2c91fe3414097beb2599976bcd0ffb0f5bd175cf2fc8d1909a8931cd50794f7a049569a6fa3914140
7
- data.tar.gz: 5dde805af2709726635c798aec9029dff143ffd63ca1de0166328d7ab68c892ab507a02b9127e6d1af3abd89d18774cf4f4d197dfd7656ae474d493821a54433
6
+ metadata.gz: 2b2a91631a4595d11d106bcb44c0fb7341a43b79f481566ffaf8879458f976a98e5cd3a1d99556f7533bb32d18a9d8c9046bcbb1fddd1b62b3100ea08afb3ce5
7
+ data.tar.gz: 285c523bc3e26fa846783e853d1878211999ed6d9b0fcdc4cd8a177272dffe71f7a303544bf371fda71c7ecfffb0681abc6388049f740c11c72d7a79559bfb35
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/brainzlab.svg)](https://rubygems.org/gems/brainzlab)
4
4
  [![CI](https://github.com/brainz-lab/brainzlab-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/brainz-lab/brainzlab-ruby/actions/workflows/ci.yml)
5
+ [![Docs](https://img.shields.io/badge/docs-brainzlab.ai-orange)](https://docs.brainzlab.ai/sdk/ruby/installation)
5
6
  [![License](https://img.shields.io/badge/license-Ossassy-blue.svg)](LICENSE)
6
7
 
7
8
  Official Ruby SDK for [BrainzLab](https://brainzlab.ai) - the complete observability platform.
@@ -328,6 +329,13 @@ Full documentation: [docs.brainzlab.ai](https://docs.brainzlab.ai)
328
329
  - [Reflex (Errors)](https://docs.brainzlab.ai/sdk/ruby/reflex)
329
330
  - [Pulse (APM)](https://docs.brainzlab.ai/sdk/ruby/pulse)
330
331
 
332
+ ## Related
333
+
334
+ - [Recall](https://github.com/brainz-lab/recall) - Logging service
335
+ - [Reflex](https://github.com/brainz-lab/reflex) - Error tracking service
336
+ - [Pulse](https://github.com/brainz-lab/pulse) - APM service
337
+ - [Stack](https://github.com/brainz-lab/stack) - Self-hosted deployment
338
+
331
339
  ## License
332
340
 
333
341
  Ossassy License - see [LICENSE](LICENSE) for details.
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "json"
5
+ require "uri"
6
+ require "cgi"
7
+
8
+ module BrainzLab
9
+ module Beacon
10
+ class Client
11
+ def initialize(config)
12
+ @config = config
13
+ @base_url = config.beacon_url || "https://beacon.brainzlab.ai"
14
+ end
15
+
16
+ # Create a new monitor
17
+ def create_monitor(name:, url:, type: "http", interval: 60, **options)
18
+ response = request(
19
+ :post,
20
+ "/api/v1/monitors",
21
+ body: {
22
+ name: name,
23
+ url: url,
24
+ monitor_type: type,
25
+ interval: interval,
26
+ **options
27
+ }
28
+ )
29
+
30
+ return nil unless response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
31
+
32
+ JSON.parse(response.body, symbolize_names: true)
33
+ rescue StandardError => e
34
+ log_error("create_monitor", e)
35
+ nil
36
+ end
37
+
38
+ # Get monitor status
39
+ def get_monitor(id)
40
+ response = request(:get, "/api/v1/monitors/#{id}")
41
+
42
+ return nil unless response.is_a?(Net::HTTPSuccess)
43
+
44
+ JSON.parse(response.body, symbolize_names: true)
45
+ rescue StandardError => e
46
+ log_error("get_monitor", e)
47
+ nil
48
+ end
49
+
50
+ # List all monitors
51
+ def list_monitors
52
+ response = request(:get, "/api/v1/monitors")
53
+
54
+ return [] unless response.is_a?(Net::HTTPSuccess)
55
+
56
+ data = JSON.parse(response.body, symbolize_names: true)
57
+ data[:monitors] || []
58
+ rescue StandardError => e
59
+ log_error("list_monitors", e)
60
+ []
61
+ end
62
+
63
+ # Update a monitor
64
+ def update_monitor(id, **attributes)
65
+ response = request(
66
+ :put,
67
+ "/api/v1/monitors/#{id}",
68
+ body: attributes
69
+ )
70
+
71
+ response.is_a?(Net::HTTPSuccess)
72
+ rescue StandardError => e
73
+ log_error("update_monitor", e)
74
+ false
75
+ end
76
+
77
+ # Delete a monitor
78
+ def delete_monitor(id)
79
+ response = request(:delete, "/api/v1/monitors/#{id}")
80
+ response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPNoContent)
81
+ rescue StandardError => e
82
+ log_error("delete_monitor", e)
83
+ false
84
+ end
85
+
86
+ # Pause a monitor
87
+ def pause_monitor(id)
88
+ response = request(:post, "/api/v1/monitors/#{id}/pause")
89
+ response.is_a?(Net::HTTPSuccess)
90
+ rescue StandardError => e
91
+ log_error("pause_monitor", e)
92
+ false
93
+ end
94
+
95
+ # Resume a monitor
96
+ def resume_monitor(id)
97
+ response = request(:post, "/api/v1/monitors/#{id}/resume")
98
+ response.is_a?(Net::HTTPSuccess)
99
+ rescue StandardError => e
100
+ log_error("resume_monitor", e)
101
+ false
102
+ end
103
+
104
+ # Get check history
105
+ def check_history(monitor_id, limit: 100)
106
+ response = request(
107
+ :get,
108
+ "/api/v1/monitors/#{monitor_id}/checks",
109
+ params: { limit: limit }
110
+ )
111
+
112
+ return [] unless response.is_a?(Net::HTTPSuccess)
113
+
114
+ data = JSON.parse(response.body, symbolize_names: true)
115
+ data[:checks] || []
116
+ rescue StandardError => e
117
+ log_error("check_history", e)
118
+ []
119
+ end
120
+
121
+ # Get current status summary
122
+ def status_summary
123
+ response = request(:get, "/api/v1/status")
124
+
125
+ return nil unless response.is_a?(Net::HTTPSuccess)
126
+
127
+ JSON.parse(response.body, symbolize_names: true)
128
+ rescue StandardError => e
129
+ log_error("status_summary", e)
130
+ nil
131
+ end
132
+
133
+ # List active incidents
134
+ def list_incidents(status: nil)
135
+ params = {}
136
+ params[:status] = status if status
137
+
138
+ response = request(:get, "/api/v1/incidents", params: params)
139
+
140
+ return [] unless response.is_a?(Net::HTTPSuccess)
141
+
142
+ data = JSON.parse(response.body, symbolize_names: true)
143
+ data[:incidents] || []
144
+ rescue StandardError => e
145
+ log_error("list_incidents", e)
146
+ []
147
+ end
148
+
149
+ def provision(project_id:, app_name:)
150
+ response = request(
151
+ :post,
152
+ "/api/v1/projects/provision",
153
+ body: { project_id: project_id, app_name: app_name },
154
+ use_service_key: true
155
+ )
156
+
157
+ response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
158
+ rescue StandardError => e
159
+ log_error("provision", e)
160
+ false
161
+ end
162
+
163
+ private
164
+
165
+ def request(method, path, headers: {}, body: nil, params: nil, use_service_key: false)
166
+ uri = URI.parse("#{@base_url}#{path}")
167
+
168
+ if params
169
+ uri.query = URI.encode_www_form(params)
170
+ end
171
+
172
+ http = Net::HTTP.new(uri.host, uri.port)
173
+ http.use_ssl = uri.scheme == "https"
174
+ http.open_timeout = 10
175
+ http.read_timeout = 30
176
+
177
+ request = case method
178
+ when :get
179
+ Net::HTTP::Get.new(uri)
180
+ when :post
181
+ Net::HTTP::Post.new(uri)
182
+ when :put
183
+ Net::HTTP::Put.new(uri)
184
+ when :delete
185
+ Net::HTTP::Delete.new(uri)
186
+ end
187
+
188
+ request["Content-Type"] = "application/json"
189
+ request["Accept"] = "application/json"
190
+
191
+ if use_service_key
192
+ request["X-Service-Key"] = @config.beacon_master_key || @config.secret_key
193
+ else
194
+ auth_key = @config.beacon_api_key || @config.secret_key
195
+ request["Authorization"] = "Bearer #{auth_key}" if auth_key
196
+ end
197
+
198
+ headers.each { |k, v| request[k] = v }
199
+ request.body = body.to_json if body
200
+
201
+ http.request(request)
202
+ end
203
+
204
+ def log_error(operation, error)
205
+ BrainzLab.debug_log("[Beacon::Client] #{operation} failed: #{error.message}")
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrainzLab
4
+ module Beacon
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.beacon_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("[Beacon::Provisioner] Project provisioned: #{project_id}")
28
+ rescue StandardError => e
29
+ BrainzLab.debug_log("[Beacon::Provisioner] Provisioning failed: #{e.message}")
30
+ end
31
+
32
+ private
33
+
34
+ def valid_auth?
35
+ key = @config.beacon_api_key || @config.beacon_master_key || @config.secret_key
36
+ !key.nil? && !key.empty?
37
+ end
38
+
39
+ def detect_project_id
40
+ ENV["BRAINZLAB_PROJECT_ID"]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,215 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "beacon/client"
4
+ require_relative "beacon/provisioner"
5
+
6
+ module BrainzLab
7
+ module Beacon
8
+ class << self
9
+ # Create an HTTP monitor
10
+ # @param name [String] Monitor name
11
+ # @param url [String] URL to monitor
12
+ # @param interval [Integer] Check interval in seconds (default: 60)
13
+ # @param options [Hash] Additional options
14
+ # @return [Hash, nil] Created monitor or nil
15
+ #
16
+ # @example
17
+ # BrainzLab::Beacon.create_http_monitor(
18
+ # "Production API",
19
+ # "https://api.example.com/health",
20
+ # interval: 30,
21
+ # expected_status: 200,
22
+ # timeout: 5
23
+ # )
24
+ #
25
+ def create_http_monitor(name, url, interval: 60, **options)
26
+ return nil unless enabled?
27
+
28
+ ensure_provisioned!
29
+ return nil unless BrainzLab.configuration.beacon_valid?
30
+
31
+ client.create_monitor(
32
+ name: name,
33
+ url: url,
34
+ type: "http",
35
+ interval: interval,
36
+ **options
37
+ )
38
+ end
39
+
40
+ # Create an SSL certificate monitor
41
+ def create_ssl_monitor(name, domain, warn_days: 30, **options)
42
+ return nil unless enabled?
43
+
44
+ ensure_provisioned!
45
+ return nil unless BrainzLab.configuration.beacon_valid?
46
+
47
+ client.create_monitor(
48
+ name: name,
49
+ url: "https://#{domain}",
50
+ type: "ssl",
51
+ ssl_warn_days: warn_days,
52
+ **options
53
+ )
54
+ end
55
+
56
+ # Create a TCP port monitor
57
+ def create_tcp_monitor(name, host, port, **options)
58
+ return nil unless enabled?
59
+
60
+ ensure_provisioned!
61
+ return nil unless BrainzLab.configuration.beacon_valid?
62
+
63
+ client.create_monitor(
64
+ name: name,
65
+ url: "#{host}:#{port}",
66
+ type: "tcp",
67
+ **options
68
+ )
69
+ end
70
+
71
+ # Create a DNS monitor
72
+ def create_dns_monitor(name, domain, expected_record: nil, **options)
73
+ return nil unless enabled?
74
+
75
+ ensure_provisioned!
76
+ return nil unless BrainzLab.configuration.beacon_valid?
77
+
78
+ client.create_monitor(
79
+ name: name,
80
+ url: domain,
81
+ type: "dns",
82
+ expected_record: expected_record,
83
+ **options
84
+ )
85
+ end
86
+
87
+ # Get monitor by ID
88
+ def get(id)
89
+ return nil unless enabled?
90
+
91
+ ensure_provisioned!
92
+ return nil unless BrainzLab.configuration.beacon_valid?
93
+
94
+ client.get_monitor(id)
95
+ end
96
+
97
+ # List all monitors
98
+ def list
99
+ return [] unless enabled?
100
+
101
+ ensure_provisioned!
102
+ return [] unless BrainzLab.configuration.beacon_valid?
103
+
104
+ client.list_monitors
105
+ end
106
+
107
+ # Update a monitor
108
+ def update(id, **attributes)
109
+ return false unless enabled?
110
+
111
+ ensure_provisioned!
112
+ return false unless BrainzLab.configuration.beacon_valid?
113
+
114
+ client.update_monitor(id, **attributes)
115
+ end
116
+
117
+ # Delete a monitor
118
+ def delete(id)
119
+ return false unless enabled?
120
+
121
+ ensure_provisioned!
122
+ return false unless BrainzLab.configuration.beacon_valid?
123
+
124
+ client.delete_monitor(id)
125
+ end
126
+
127
+ # Pause a monitor
128
+ def pause(id)
129
+ return false unless enabled?
130
+
131
+ ensure_provisioned!
132
+ return false unless BrainzLab.configuration.beacon_valid?
133
+
134
+ client.pause_monitor(id)
135
+ end
136
+
137
+ # Resume a paused monitor
138
+ def resume(id)
139
+ return false unless enabled?
140
+
141
+ ensure_provisioned!
142
+ return false unless BrainzLab.configuration.beacon_valid?
143
+
144
+ client.resume_monitor(id)
145
+ end
146
+
147
+ # Get check history for a monitor
148
+ def history(monitor_id, limit: 100)
149
+ return [] unless enabled?
150
+
151
+ ensure_provisioned!
152
+ return [] unless BrainzLab.configuration.beacon_valid?
153
+
154
+ client.check_history(monitor_id, limit: limit)
155
+ end
156
+
157
+ # Get overall status summary
158
+ def status
159
+ return nil unless enabled?
160
+
161
+ ensure_provisioned!
162
+ return nil unless BrainzLab.configuration.beacon_valid?
163
+
164
+ client.status_summary
165
+ end
166
+
167
+ # Check if all monitors are up
168
+ def all_up?
169
+ summary = status
170
+ return false unless summary
171
+
172
+ summary[:status] == "up" || summary[:status] == "operational"
173
+ end
174
+
175
+ # List active incidents
176
+ def incidents(status: nil)
177
+ return [] unless enabled?
178
+
179
+ ensure_provisioned!
180
+ return [] unless BrainzLab.configuration.beacon_valid?
181
+
182
+ client.list_incidents(status: status)
183
+ end
184
+
185
+ # === INTERNAL ===
186
+
187
+ def ensure_provisioned!
188
+ return if @provisioned
189
+
190
+ @provisioned = true
191
+ provisioner.ensure_project!
192
+ end
193
+
194
+ def provisioner
195
+ @provisioner ||= Provisioner.new(BrainzLab.configuration)
196
+ end
197
+
198
+ def client
199
+ @client ||= Client.new(BrainzLab.configuration)
200
+ end
201
+
202
+ def reset!
203
+ @client = nil
204
+ @provisioner = nil
205
+ @provisioned = false
206
+ end
207
+
208
+ private
209
+
210
+ def enabled?
211
+ BrainzLab.configuration.beacon_enabled
212
+ end
213
+ end
214
+ end
215
+ end