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,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'synapse/client'
|
|
4
|
+
require_relative 'synapse/provisioner'
|
|
5
|
+
|
|
6
|
+
module BrainzLab
|
|
7
|
+
module Synapse
|
|
8
|
+
class << self
|
|
9
|
+
# List all projects
|
|
10
|
+
# @param status [String] Filter by status (running, stopped, deploying)
|
|
11
|
+
# @return [Array<Hash>] List of projects
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# projects = BrainzLab::Synapse.projects(status: "running")
|
|
15
|
+
#
|
|
16
|
+
def projects(status: nil, page: 1, per_page: 20)
|
|
17
|
+
return [] unless enabled?
|
|
18
|
+
|
|
19
|
+
ensure_provisioned!
|
|
20
|
+
return [] unless BrainzLab.configuration.synapse_valid?
|
|
21
|
+
|
|
22
|
+
client.list_projects(status: status, page: page, per_page: per_page)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get project details
|
|
26
|
+
# @param project_id [String] Project ID
|
|
27
|
+
# @return [Hash, nil] Project details
|
|
28
|
+
def project(project_id)
|
|
29
|
+
return nil unless enabled?
|
|
30
|
+
|
|
31
|
+
ensure_provisioned!
|
|
32
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
33
|
+
|
|
34
|
+
client.get_project(project_id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Create a new project
|
|
38
|
+
# @param name [String] Project name
|
|
39
|
+
# @param repos [Array<Hash>] Repository configurations
|
|
40
|
+
# @param description [String] Project description
|
|
41
|
+
# @return [Hash, nil] Created project
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# project = BrainzLab::Synapse.create_project(
|
|
45
|
+
# name: "My App",
|
|
46
|
+
# repos: [
|
|
47
|
+
# { url: "https://github.com/org/api", type: "rails" },
|
|
48
|
+
# { url: "https://github.com/org/frontend", type: "react" }
|
|
49
|
+
# ]
|
|
50
|
+
# )
|
|
51
|
+
#
|
|
52
|
+
def create_project(name:, repos: [], description: nil, **)
|
|
53
|
+
return nil unless enabled?
|
|
54
|
+
|
|
55
|
+
ensure_provisioned!
|
|
56
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
57
|
+
|
|
58
|
+
client.create_project(name: name, repos: repos, description: description, **)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Start project containers
|
|
62
|
+
# @param project_id [String] Project ID
|
|
63
|
+
# @return [Boolean] True if started
|
|
64
|
+
def up(project_id)
|
|
65
|
+
return false unless enabled?
|
|
66
|
+
|
|
67
|
+
ensure_provisioned!
|
|
68
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
69
|
+
|
|
70
|
+
client.start_project(project_id)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Stop project containers
|
|
74
|
+
# @param project_id [String] Project ID
|
|
75
|
+
# @return [Boolean] True if stopped
|
|
76
|
+
def down(project_id)
|
|
77
|
+
return false unless enabled?
|
|
78
|
+
|
|
79
|
+
ensure_provisioned!
|
|
80
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
81
|
+
|
|
82
|
+
client.stop_project(project_id)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Restart project containers
|
|
86
|
+
# @param project_id [String] Project ID
|
|
87
|
+
# @return [Boolean] True if restarted
|
|
88
|
+
def restart(project_id)
|
|
89
|
+
return false unless enabled?
|
|
90
|
+
|
|
91
|
+
ensure_provisioned!
|
|
92
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
93
|
+
|
|
94
|
+
client.restart_project(project_id)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Deploy project to environment
|
|
98
|
+
# @param project_id [String] Project ID
|
|
99
|
+
# @param environment [Symbol] Target environment (:staging, :production)
|
|
100
|
+
# @param options [Hash] Deployment options
|
|
101
|
+
# @return [Hash, nil] Deployment info
|
|
102
|
+
#
|
|
103
|
+
# @example
|
|
104
|
+
# deployment = BrainzLab::Synapse.deploy(project_id, environment: :staging)
|
|
105
|
+
#
|
|
106
|
+
def deploy(project_id, environment:, **options)
|
|
107
|
+
return nil unless enabled?
|
|
108
|
+
|
|
109
|
+
ensure_provisioned!
|
|
110
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
111
|
+
|
|
112
|
+
client.deploy(project_id, environment: environment, options: options)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Get deployment status
|
|
116
|
+
# @param deployment_id [String] Deployment ID
|
|
117
|
+
# @return [Hash, nil] Deployment details
|
|
118
|
+
def deployment(deployment_id)
|
|
119
|
+
return nil unless enabled?
|
|
120
|
+
|
|
121
|
+
ensure_provisioned!
|
|
122
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
123
|
+
|
|
124
|
+
client.get_deployment(deployment_id)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Create an AI development task
|
|
128
|
+
# @param project_id [String] Project ID
|
|
129
|
+
# @param description [String] Task description in natural language
|
|
130
|
+
# @param type [Symbol] Task type (:feature, :bugfix, :refactor, :test)
|
|
131
|
+
# @param priority [Symbol] Priority (:low, :medium, :high, :urgent)
|
|
132
|
+
# @return [Hash, nil] Created task
|
|
133
|
+
#
|
|
134
|
+
# @example
|
|
135
|
+
# task = BrainzLab::Synapse.task(
|
|
136
|
+
# project_id: project_id,
|
|
137
|
+
# description: "Add user authentication with OAuth",
|
|
138
|
+
# type: :feature,
|
|
139
|
+
# priority: :high
|
|
140
|
+
# )
|
|
141
|
+
#
|
|
142
|
+
def task(project_id:, description:, type: nil, priority: nil, **)
|
|
143
|
+
return nil unless enabled?
|
|
144
|
+
|
|
145
|
+
ensure_provisioned!
|
|
146
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
147
|
+
|
|
148
|
+
client.create_task(
|
|
149
|
+
project_id: project_id,
|
|
150
|
+
description: description,
|
|
151
|
+
type: type,
|
|
152
|
+
priority: priority,
|
|
153
|
+
**
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Get task details
|
|
158
|
+
# @param task_id [String] Task ID
|
|
159
|
+
# @return [Hash, nil] Task details
|
|
160
|
+
def get_task(task_id)
|
|
161
|
+
return nil unless enabled?
|
|
162
|
+
|
|
163
|
+
ensure_provisioned!
|
|
164
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
165
|
+
|
|
166
|
+
client.get_task(task_id)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Get task status and progress
|
|
170
|
+
# @param task_id [String] Task ID
|
|
171
|
+
# @return [Hash, nil] Task status with progress
|
|
172
|
+
def task_status(task_id)
|
|
173
|
+
return nil unless enabled?
|
|
174
|
+
|
|
175
|
+
ensure_provisioned!
|
|
176
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
177
|
+
|
|
178
|
+
client.get_task_status(task_id)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# List tasks
|
|
182
|
+
# @param project_id [String] Optional filter by project
|
|
183
|
+
# @param status [String] Filter by status (pending, running, completed, failed)
|
|
184
|
+
# @return [Array<Hash>] List of tasks
|
|
185
|
+
def tasks(project_id: nil, status: nil, page: 1, per_page: 20)
|
|
186
|
+
return [] unless enabled?
|
|
187
|
+
|
|
188
|
+
ensure_provisioned!
|
|
189
|
+
return [] unless BrainzLab.configuration.synapse_valid?
|
|
190
|
+
|
|
191
|
+
client.list_tasks(project_id: project_id, status: status, page: page, per_page: per_page)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Cancel a running task
|
|
195
|
+
# @param task_id [String] Task ID
|
|
196
|
+
# @return [Boolean] True if cancelled
|
|
197
|
+
def cancel_task(task_id)
|
|
198
|
+
return false unless enabled?
|
|
199
|
+
|
|
200
|
+
ensure_provisioned!
|
|
201
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
202
|
+
|
|
203
|
+
client.cancel_task(task_id)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Get container logs
|
|
207
|
+
# @param project_id [String] Project ID
|
|
208
|
+
# @param container [String] Optional container name
|
|
209
|
+
# @param lines [Integer] Number of lines (default: 100)
|
|
210
|
+
# @param since [String] Start time (ISO8601)
|
|
211
|
+
# @return [Hash, nil] Log data
|
|
212
|
+
def logs(project_id, container: nil, lines: 100, since: nil)
|
|
213
|
+
return nil unless enabled?
|
|
214
|
+
|
|
215
|
+
ensure_provisioned!
|
|
216
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
217
|
+
|
|
218
|
+
client.get_logs(project_id, container: container, lines: lines, since: since)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Execute command in container
|
|
222
|
+
# @param project_id [String] Project ID
|
|
223
|
+
# @param command [String] Command to execute
|
|
224
|
+
# @param container [String] Optional container name
|
|
225
|
+
# @param timeout [Integer] Timeout in seconds
|
|
226
|
+
# @return [Hash, nil] Command output
|
|
227
|
+
#
|
|
228
|
+
# @example
|
|
229
|
+
# result = BrainzLab::Synapse.exec(project_id, command: "rails db:migrate")
|
|
230
|
+
#
|
|
231
|
+
def exec(project_id, command:, container: nil, timeout: 30)
|
|
232
|
+
return nil unless enabled?
|
|
233
|
+
|
|
234
|
+
ensure_provisioned!
|
|
235
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
236
|
+
|
|
237
|
+
client.exec(project_id, command: command, container: container, timeout: timeout)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# === INTERNAL ===
|
|
241
|
+
|
|
242
|
+
def ensure_provisioned!
|
|
243
|
+
return if @provisioned
|
|
244
|
+
|
|
245
|
+
@provisioned = true
|
|
246
|
+
provisioner.ensure_project!
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def provisioner
|
|
250
|
+
@provisioner ||= Provisioner.new(BrainzLab.configuration)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def client
|
|
254
|
+
@client ||= Client.new(BrainzLab.configuration)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def reset!
|
|
258
|
+
@client = nil
|
|
259
|
+
@provisioner = nil
|
|
260
|
+
@provisioned = false
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def enabled?
|
|
266
|
+
BrainzLab.configuration.synapse_enabled
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Testing
|
|
5
|
+
# Thread-safe store for captured events, logs, errors, and metrics during tests
|
|
6
|
+
#
|
|
7
|
+
# This class is used internally by the testing helpers to store all
|
|
8
|
+
# captured data from stubbed SDK calls.
|
|
9
|
+
class EventStore
|
|
10
|
+
def initialize
|
|
11
|
+
@mutex = Mutex.new
|
|
12
|
+
@events = []
|
|
13
|
+
@metrics = []
|
|
14
|
+
@logs = []
|
|
15
|
+
@errors = []
|
|
16
|
+
@error_messages = []
|
|
17
|
+
@traces = []
|
|
18
|
+
@alerts = []
|
|
19
|
+
@notifications = []
|
|
20
|
+
@triggers = []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# === Events (Flux) ===
|
|
24
|
+
|
|
25
|
+
def record_event(name, properties = {})
|
|
26
|
+
@mutex.synchronize do
|
|
27
|
+
@events << {
|
|
28
|
+
name: name.to_s,
|
|
29
|
+
properties: properties,
|
|
30
|
+
timestamp: Time.now.utc
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def events
|
|
36
|
+
@mutex.synchronize { @events.dup }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def events_named(name)
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
@events.select { |e| e[:name] == name.to_s }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def event_tracked?(name, properties = nil)
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
@events.any? do |event|
|
|
48
|
+
next false unless event[:name] == name.to_s
|
|
49
|
+
next true if properties.nil?
|
|
50
|
+
|
|
51
|
+
properties_match?(event[:properties], properties)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def last_event
|
|
57
|
+
@mutex.synchronize { @events.last }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def clear_events!
|
|
61
|
+
@mutex.synchronize { @events.clear }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# === Metrics (Flux) ===
|
|
65
|
+
|
|
66
|
+
def record_metric(type, name, value, opts = {})
|
|
67
|
+
@mutex.synchronize do
|
|
68
|
+
@metrics << {
|
|
69
|
+
type: type.to_sym,
|
|
70
|
+
name: name.to_s,
|
|
71
|
+
value: value,
|
|
72
|
+
tags: opts[:tags] || {},
|
|
73
|
+
timestamp: Time.now.utc
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def metrics
|
|
79
|
+
@mutex.synchronize { @metrics.dup }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def metrics_named(name)
|
|
83
|
+
@mutex.synchronize do
|
|
84
|
+
@metrics.select { |m| m[:name] == name.to_s }
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def metric_recorded?(type, name, value: nil, tags: nil)
|
|
89
|
+
@mutex.synchronize do
|
|
90
|
+
@metrics.any? do |metric|
|
|
91
|
+
next false unless metric[:type] == type.to_sym
|
|
92
|
+
next false unless metric[:name] == name.to_s
|
|
93
|
+
next false if value && metric[:value] != value
|
|
94
|
+
next false if tags && !properties_match?(metric[:tags], tags)
|
|
95
|
+
|
|
96
|
+
true
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def clear_metrics!
|
|
102
|
+
@mutex.synchronize { @metrics.clear }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# === Logs (Recall) ===
|
|
106
|
+
|
|
107
|
+
def record_log(level, message, data = {})
|
|
108
|
+
@mutex.synchronize do
|
|
109
|
+
@logs << {
|
|
110
|
+
level: level.to_sym,
|
|
111
|
+
message: message.to_s,
|
|
112
|
+
data: data,
|
|
113
|
+
timestamp: Time.now.utc
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def logs
|
|
119
|
+
@mutex.synchronize { @logs.dup }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def logs_at_level(level)
|
|
123
|
+
@mutex.synchronize do
|
|
124
|
+
@logs.select { |l| l[:level] == level.to_sym }
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def logged?(level, message = nil, data = nil)
|
|
129
|
+
@mutex.synchronize do
|
|
130
|
+
@logs.any? do |log|
|
|
131
|
+
next false unless log[:level] == level.to_sym
|
|
132
|
+
next true if message.nil?
|
|
133
|
+
|
|
134
|
+
message_matches = case message
|
|
135
|
+
when Regexp
|
|
136
|
+
log[:message].match?(message)
|
|
137
|
+
else
|
|
138
|
+
log[:message].include?(message.to_s)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
next false unless message_matches
|
|
142
|
+
next true if data.nil?
|
|
143
|
+
|
|
144
|
+
properties_match?(log[:data], data)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def clear_logs!
|
|
150
|
+
@mutex.synchronize { @logs.clear }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# === Errors (Reflex) ===
|
|
154
|
+
|
|
155
|
+
def record_error(exception, context = {})
|
|
156
|
+
@mutex.synchronize do
|
|
157
|
+
@errors << {
|
|
158
|
+
exception: exception,
|
|
159
|
+
error_class: exception.class.name,
|
|
160
|
+
message: exception.message,
|
|
161
|
+
backtrace: exception.backtrace,
|
|
162
|
+
context: context,
|
|
163
|
+
timestamp: Time.now.utc
|
|
164
|
+
}
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def record_error_message(message, level, context = {})
|
|
169
|
+
@mutex.synchronize do
|
|
170
|
+
@error_messages << {
|
|
171
|
+
message: message.to_s,
|
|
172
|
+
level: level.to_sym,
|
|
173
|
+
context: context,
|
|
174
|
+
timestamp: Time.now.utc
|
|
175
|
+
}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def errors
|
|
180
|
+
@mutex.synchronize { @errors.dup }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def error_messages
|
|
184
|
+
@mutex.synchronize { @error_messages.dup }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def error_captured?(error_class = nil, message: nil, context: nil)
|
|
188
|
+
@mutex.synchronize do
|
|
189
|
+
@errors.any? do |error|
|
|
190
|
+
if error_class
|
|
191
|
+
next false unless error[:error_class] == error_class.to_s ||
|
|
192
|
+
(error_class.is_a?(Class) && error[:exception].is_a?(error_class))
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
if message
|
|
196
|
+
message_matches = case message
|
|
197
|
+
when Regexp
|
|
198
|
+
error[:message].match?(message)
|
|
199
|
+
else
|
|
200
|
+
error[:message].include?(message.to_s)
|
|
201
|
+
end
|
|
202
|
+
next false unless message_matches
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
next false if context && !properties_match?(error[:context], context)
|
|
206
|
+
|
|
207
|
+
true
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def last_error
|
|
213
|
+
@mutex.synchronize { @errors.last }
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def clear_errors!
|
|
217
|
+
@mutex.synchronize do
|
|
218
|
+
@errors.clear
|
|
219
|
+
@error_messages.clear
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# === Traces (Pulse) ===
|
|
224
|
+
|
|
225
|
+
def record_trace(name, opts = {})
|
|
226
|
+
@mutex.synchronize do
|
|
227
|
+
@traces << {
|
|
228
|
+
name: name.to_s,
|
|
229
|
+
options: opts,
|
|
230
|
+
timestamp: Time.now.utc
|
|
231
|
+
}
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def traces
|
|
236
|
+
@mutex.synchronize { @traces.dup }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def trace_recorded?(name, opts = nil)
|
|
240
|
+
@mutex.synchronize do
|
|
241
|
+
@traces.any? do |trace|
|
|
242
|
+
next false unless trace[:name] == name.to_s
|
|
243
|
+
next true if opts.nil?
|
|
244
|
+
|
|
245
|
+
properties_match?(trace[:options], opts)
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def clear_traces!
|
|
251
|
+
@mutex.synchronize { @traces.clear }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# === Alerts (Signal) ===
|
|
255
|
+
|
|
256
|
+
def record_alert(name, message, severity, channels, data)
|
|
257
|
+
@mutex.synchronize do
|
|
258
|
+
@alerts << {
|
|
259
|
+
name: name.to_s,
|
|
260
|
+
message: message.to_s,
|
|
261
|
+
severity: severity.to_sym,
|
|
262
|
+
channels: channels,
|
|
263
|
+
data: data,
|
|
264
|
+
timestamp: Time.now.utc
|
|
265
|
+
}
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def alerts
|
|
270
|
+
@mutex.synchronize { @alerts.dup }
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def alert_sent?(name, message: nil, severity: nil)
|
|
274
|
+
@mutex.synchronize do
|
|
275
|
+
@alerts.any? do |alert|
|
|
276
|
+
next false unless alert[:name] == name.to_s
|
|
277
|
+
next false if message && !alert[:message].include?(message.to_s)
|
|
278
|
+
next false if severity && alert[:severity] != severity.to_sym
|
|
279
|
+
|
|
280
|
+
true
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def clear_alerts!
|
|
286
|
+
@mutex.synchronize { @alerts.clear }
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# === Notifications (Signal) ===
|
|
290
|
+
|
|
291
|
+
def record_notification(channel, message, title, data)
|
|
292
|
+
@mutex.synchronize do
|
|
293
|
+
@notifications << {
|
|
294
|
+
channel: Array(channel).map(&:to_s),
|
|
295
|
+
message: message.to_s,
|
|
296
|
+
title: title,
|
|
297
|
+
data: data,
|
|
298
|
+
timestamp: Time.now.utc
|
|
299
|
+
}
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def notifications
|
|
304
|
+
@mutex.synchronize { @notifications.dup }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def clear_notifications!
|
|
308
|
+
@mutex.synchronize { @notifications.clear }
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# === Triggers (Signal) ===
|
|
312
|
+
|
|
313
|
+
def record_trigger(rule_name, context)
|
|
314
|
+
@mutex.synchronize do
|
|
315
|
+
@triggers << {
|
|
316
|
+
rule_name: rule_name.to_s,
|
|
317
|
+
context: context,
|
|
318
|
+
timestamp: Time.now.utc
|
|
319
|
+
}
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def triggers
|
|
324
|
+
@mutex.synchronize { @triggers.dup }
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def clear_triggers!
|
|
328
|
+
@mutex.synchronize { @triggers.clear }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# === General ===
|
|
332
|
+
|
|
333
|
+
def clear!
|
|
334
|
+
@mutex.synchronize do
|
|
335
|
+
@events.clear
|
|
336
|
+
@metrics.clear
|
|
337
|
+
@logs.clear
|
|
338
|
+
@errors.clear
|
|
339
|
+
@error_messages.clear
|
|
340
|
+
@traces.clear
|
|
341
|
+
@alerts.clear
|
|
342
|
+
@notifications.clear
|
|
343
|
+
@triggers.clear
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def empty?
|
|
348
|
+
@mutex.synchronize do
|
|
349
|
+
@events.empty? &&
|
|
350
|
+
@metrics.empty? &&
|
|
351
|
+
@logs.empty? &&
|
|
352
|
+
@errors.empty? &&
|
|
353
|
+
@error_messages.empty? &&
|
|
354
|
+
@traces.empty? &&
|
|
355
|
+
@alerts.empty? &&
|
|
356
|
+
@notifications.empty? &&
|
|
357
|
+
@triggers.empty?
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
private
|
|
362
|
+
|
|
363
|
+
def properties_match?(actual, expected)
|
|
364
|
+
expected.all? do |key, value|
|
|
365
|
+
actual_value = actual[key] || actual[key.to_s] || actual[key.to_sym]
|
|
366
|
+
|
|
367
|
+
case value
|
|
368
|
+
when Regexp
|
|
369
|
+
actual_value.to_s.match?(value)
|
|
370
|
+
else
|
|
371
|
+
actual_value == value
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
end
|