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.
Files changed (121) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +68 -0
  3. data/LICENSE +11 -0
  4. data/README.md +571 -0
  5. data/lib/brainzlab/beacon/client.rb +227 -0
  6. data/lib/brainzlab/beacon/provisioner.rb +44 -0
  7. data/lib/brainzlab/beacon.rb +215 -0
  8. data/lib/brainzlab/configuration.rb +676 -0
  9. data/lib/brainzlab/context.rb +90 -0
  10. data/lib/brainzlab/cortex/cache.rb +59 -0
  11. data/lib/brainzlab/cortex/client.rb +159 -0
  12. data/lib/brainzlab/cortex/provisioner.rb +49 -0
  13. data/lib/brainzlab/cortex.rb +223 -0
  14. data/lib/brainzlab/debug.rb +305 -0
  15. data/lib/brainzlab/dendrite/client.rb +250 -0
  16. data/lib/brainzlab/dendrite/provisioner.rb +44 -0
  17. data/lib/brainzlab/dendrite.rb +195 -0
  18. data/lib/brainzlab/development/logger.rb +150 -0
  19. data/lib/brainzlab/development/store.rb +121 -0
  20. data/lib/brainzlab/development.rb +72 -0
  21. data/lib/brainzlab/devtools/assets/devtools.css +1329 -0
  22. data/lib/brainzlab/devtools/assets/devtools.js +396 -0
  23. data/lib/brainzlab/devtools/assets/logo.svg +6 -0
  24. data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +511 -0
  25. data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
  26. data/lib/brainzlab/devtools/data/collector.rb +248 -0
  27. data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
  28. data/lib/brainzlab/devtools/middleware/database_handler.rb +177 -0
  29. data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
  30. data/lib/brainzlab/devtools/middleware/error_page.rb +377 -0
  31. data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +159 -0
  32. data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +98 -0
  33. data/lib/brainzlab/devtools.rb +75 -0
  34. data/lib/brainzlab/errors.rb +490 -0
  35. data/lib/brainzlab/flux/buffer.rb +96 -0
  36. data/lib/brainzlab/flux/client.rb +68 -0
  37. data/lib/brainzlab/flux/provisioner.rb +124 -0
  38. data/lib/brainzlab/flux.rb +184 -0
  39. data/lib/brainzlab/instrumentation/action_cable.rb +351 -0
  40. data/lib/brainzlab/instrumentation/action_controller.rb +649 -0
  41. data/lib/brainzlab/instrumentation/action_dispatch.rb +259 -0
  42. data/lib/brainzlab/instrumentation/action_mailbox.rb +197 -0
  43. data/lib/brainzlab/instrumentation/action_mailer.rb +182 -0
  44. data/lib/brainzlab/instrumentation/action_view.rb +380 -0
  45. data/lib/brainzlab/instrumentation/active_job.rb +569 -0
  46. data/lib/brainzlab/instrumentation/active_record.rb +559 -0
  47. data/lib/brainzlab/instrumentation/active_storage.rb +541 -0
  48. data/lib/brainzlab/instrumentation/active_support_cache.rb +730 -0
  49. data/lib/brainzlab/instrumentation/aws.rb +183 -0
  50. data/lib/brainzlab/instrumentation/dalli.rb +108 -0
  51. data/lib/brainzlab/instrumentation/delayed_job.rb +234 -0
  52. data/lib/brainzlab/instrumentation/elasticsearch.rb +209 -0
  53. data/lib/brainzlab/instrumentation/excon.rb +152 -0
  54. data/lib/brainzlab/instrumentation/faraday.rb +181 -0
  55. data/lib/brainzlab/instrumentation/good_job.rb +102 -0
  56. data/lib/brainzlab/instrumentation/grape.rb +293 -0
  57. data/lib/brainzlab/instrumentation/graphql.rb +252 -0
  58. data/lib/brainzlab/instrumentation/httparty.rb +193 -0
  59. data/lib/brainzlab/instrumentation/mongodb.rb +187 -0
  60. data/lib/brainzlab/instrumentation/net_http.rb +114 -0
  61. data/lib/brainzlab/instrumentation/rails_deprecation.rb +139 -0
  62. data/lib/brainzlab/instrumentation/railties.rb +134 -0
  63. data/lib/brainzlab/instrumentation/redis.rb +324 -0
  64. data/lib/brainzlab/instrumentation/resque.rb +114 -0
  65. data/lib/brainzlab/instrumentation/sidekiq.rb +265 -0
  66. data/lib/brainzlab/instrumentation/solid_queue.rb +194 -0
  67. data/lib/brainzlab/instrumentation/stripe.rb +163 -0
  68. data/lib/brainzlab/instrumentation/typhoeus.rb +106 -0
  69. data/lib/brainzlab/instrumentation.rb +360 -0
  70. data/lib/brainzlab/nerve/client.rb +235 -0
  71. data/lib/brainzlab/nerve/provisioner.rb +44 -0
  72. data/lib/brainzlab/nerve.rb +219 -0
  73. data/lib/brainzlab/pulse/client.rb +203 -0
  74. data/lib/brainzlab/pulse/instrumentation.rb +401 -0
  75. data/lib/brainzlab/pulse/propagation.rb +241 -0
  76. data/lib/brainzlab/pulse/provisioner.rb +114 -0
  77. data/lib/brainzlab/pulse/tracer.rb +111 -0
  78. data/lib/brainzlab/pulse.rb +294 -0
  79. data/lib/brainzlab/rails/log_formatter.rb +807 -0
  80. data/lib/brainzlab/rails/log_subscriber.rb +334 -0
  81. data/lib/brainzlab/rails/railtie.rb +606 -0
  82. data/lib/brainzlab/recall/buffer.rb +66 -0
  83. data/lib/brainzlab/recall/client.rb +158 -0
  84. data/lib/brainzlab/recall/logger.rb +116 -0
  85. data/lib/brainzlab/recall/provisioner.rb +130 -0
  86. data/lib/brainzlab/recall.rb +175 -0
  87. data/lib/brainzlab/reflex/breadcrumbs.rb +55 -0
  88. data/lib/brainzlab/reflex/client.rb +150 -0
  89. data/lib/brainzlab/reflex/provisioner.rb +116 -0
  90. data/lib/brainzlab/reflex.rb +421 -0
  91. data/lib/brainzlab/sentinel/client.rb +236 -0
  92. data/lib/brainzlab/sentinel/provisioner.rb +44 -0
  93. data/lib/brainzlab/sentinel.rb +165 -0
  94. data/lib/brainzlab/signal/client.rb +60 -0
  95. data/lib/brainzlab/signal/provisioner.rb +115 -0
  96. data/lib/brainzlab/signal.rb +136 -0
  97. data/lib/brainzlab/synapse/client.rb +308 -0
  98. data/lib/brainzlab/synapse/provisioner.rb +44 -0
  99. data/lib/brainzlab/synapse.rb +270 -0
  100. data/lib/brainzlab/testing/event_store.rb +377 -0
  101. data/lib/brainzlab/testing/helpers.rb +650 -0
  102. data/lib/brainzlab/testing/matchers.rb +391 -0
  103. data/lib/brainzlab/testing.rb +327 -0
  104. data/lib/brainzlab/utilities/circuit_breaker.rb +290 -0
  105. data/lib/brainzlab/utilities/health_check.rb +294 -0
  106. data/lib/brainzlab/utilities/log_formatter.rb +254 -0
  107. data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
  108. data/lib/brainzlab/utilities.rb +17 -0
  109. data/lib/brainzlab/vault/cache.rb +80 -0
  110. data/lib/brainzlab/vault/client.rb +216 -0
  111. data/lib/brainzlab/vault/provisioner.rb +49 -0
  112. data/lib/brainzlab/vault.rb +262 -0
  113. data/lib/brainzlab/version.rb +5 -0
  114. data/lib/brainzlab/vision/client.rb +175 -0
  115. data/lib/brainzlab/vision/provisioner.rb +136 -0
  116. data/lib/brainzlab/vision.rb +155 -0
  117. data/lib/brainzlab-sdk.rb +3 -0
  118. data/lib/brainzlab.rb +306 -0
  119. data/lib/generators/brainzlab/install/install_generator.rb +63 -0
  120. data/lib/generators/brainzlab/install/templates/brainzlab.rb.tt +77 -0
  121. metadata +251 -0
@@ -0,0 +1,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/log_subscriber'
4
+
5
+ module BrainzLab
6
+ module Rails
7
+ class LogSubscriber < ActiveSupport::LogSubscriber
8
+ INTERNAL_PARAMS = %w[controller action format _method authenticity_token].freeze
9
+
10
+ class << self
11
+ attr_accessor :formatter
12
+ end
13
+
14
+ def start_processing(event)
15
+ return unless formatter
16
+
17
+ request_id = event.payload[:request]&.request_id || Thread.current[:brainzlab_request_id]
18
+ return unless request_id
19
+
20
+ payload = event.payload
21
+ params = payload[:params]&.except(*INTERNAL_PARAMS) || {}
22
+
23
+ formatter.start_request(request_id,
24
+ method: payload[:method],
25
+ path: payload[:path],
26
+ params: filter_params(params),
27
+ controller: payload[:controller],
28
+ action: payload[:action])
29
+ end
30
+
31
+ def process_action(event)
32
+ return unless formatter
33
+
34
+ request_id = event.payload[:request]&.request_id || Thread.current[:brainzlab_request_id]
35
+ return unless request_id
36
+
37
+ payload = event.payload
38
+
39
+ formatter.process_action(request_id,
40
+ controller: payload[:controller],
41
+ action: payload[:action],
42
+ status: payload[:status],
43
+ duration: event.duration,
44
+ view_runtime: payload[:view_runtime],
45
+ db_runtime: payload[:db_runtime])
46
+
47
+ # Handle exception if present
48
+ formatter.error(request_id, payload[:exception_object]) if payload[:exception_object]
49
+
50
+ # Output the formatted log
51
+ output = formatter.end_request(request_id)
52
+ log_output(output) if output
53
+ end
54
+
55
+ def halted_callback(event)
56
+ # Request was halted by a before_action
57
+ end
58
+
59
+ def redirect_to(event)
60
+ # Redirect happened
61
+ end
62
+
63
+ private
64
+
65
+ def formatter
66
+ self.class.formatter
67
+ end
68
+
69
+ def filter_params(params)
70
+ return {} unless params.is_a?(Hash)
71
+
72
+ filter_keys = BrainzLab.configuration.scrub_fields.map(&:to_s)
73
+ deep_filter(params, filter_keys)
74
+ end
75
+
76
+ def deep_filter(obj, filter_keys)
77
+ case obj
78
+ when Hash
79
+ obj.each_with_object({}) do |(k, v), h|
80
+ h[k] = if filter_keys.include?(k.to_s.downcase)
81
+ '[FILTERED]'
82
+ else
83
+ deep_filter(v, filter_keys)
84
+ end
85
+ end
86
+ when Array
87
+ obj.map { |v| deep_filter(v, filter_keys) }
88
+ else
89
+ obj
90
+ end
91
+ end
92
+
93
+ def log_output(output)
94
+ # Output directly to stdout for clean formatting
95
+ # This bypasses the Rails logger which would add timestamps/prefixes
96
+ $stdout.write(output)
97
+ end
98
+ end
99
+
100
+ # SQL query subscriber to track query details
101
+ class SqlLogSubscriber < ActiveSupport::LogSubscriber
102
+ IGNORED_PAYLOADS = %w[SCHEMA].freeze
103
+
104
+ def sql(event)
105
+ return unless LogSubscriber.formatter
106
+
107
+ payload = event.payload
108
+ return if IGNORED_PAYLOADS.include?(payload[:name])
109
+
110
+ request_id = Thread.current[:brainzlab_request_id]
111
+ return unless request_id
112
+
113
+ # Extract source location from the backtrace
114
+ source = extract_source_location(caller)
115
+
116
+ # Normalize SQL for pattern detection (remove specific values)
117
+ sql_pattern = normalize_sql(payload[:sql])
118
+
119
+ LogSubscriber.formatter.sql_query(request_id,
120
+ name: payload[:name],
121
+ duration: event.duration,
122
+ sql: payload[:sql],
123
+ sql_pattern: sql_pattern,
124
+ cached: payload[:cached] || payload[:name] == 'CACHE',
125
+ source: source)
126
+ end
127
+
128
+ private
129
+
130
+ def extract_source_location(backtrace)
131
+ # Find the first line that's in app/ directory
132
+ backtrace.each do |line|
133
+ next unless line.include?('/app/') && !line.include?('/brainzlab')
134
+
135
+ # Extract just the relevant part: app/models/user.rb:42
136
+ match = line.match(%r{(app/[^:]+:\d+)})
137
+ return match[1] if match
138
+ end
139
+ nil
140
+ end
141
+
142
+ def normalize_sql(sql)
143
+ return nil unless sql
144
+
145
+ sql
146
+ .gsub(/\b\d+\b/, '?') # Replace numbers
147
+ .gsub(/'[^']*'/, '?') # Replace single-quoted strings
148
+ .gsub(/\$\d+/, '?') # Replace positional params
149
+ .gsub(%r{/\*.*?\*/}, '') # Remove comments
150
+ .gsub(/\s+/, ' ') # Normalize whitespace
151
+ .strip
152
+ end
153
+ end
154
+
155
+ # ActionCable subscriber for broadcast formatting
156
+ class CableLogSubscriber < ActiveSupport::LogSubscriber
157
+ COLORS = LogFormatter::COLORS
158
+
159
+ def broadcast(event)
160
+ payload = event.payload
161
+ broadcasting = payload[:broadcasting]
162
+ message = payload[:message]
163
+
164
+ # Decode the channel name from the gid
165
+ channel_name = decode_broadcasting(broadcasting)
166
+
167
+ # Format the message summary
168
+ message_summary = format_message(message)
169
+
170
+ output = build_output(channel_name, message_summary, event.duration)
171
+ $stdout.write(output)
172
+ end
173
+
174
+ private
175
+
176
+ def decode_broadcasting(broadcasting)
177
+ return broadcasting unless broadcasting
178
+
179
+ # Extract channel name from gid format: logs:Z2lkOi8vcmVjYWxsL1Byb2plY3QvNDhi...
180
+ if broadcasting.start_with?('logs:')
181
+ 'LogsChannel'
182
+ elsif broadcasting.include?(':')
183
+ # Generic channel:id format
184
+ "#{broadcasting.split(':').first.capitalize}Channel"
185
+ else
186
+ broadcasting
187
+ end
188
+ end
189
+
190
+ def format_message(message)
191
+ return '{}' unless message
192
+
193
+ case message
194
+ when Hash
195
+ format_hash(message)
196
+ when String
197
+ begin
198
+ parsed = JSON.parse(message)
199
+ format_hash(parsed)
200
+ rescue JSON::ParserError
201
+ truncate(message, 80)
202
+ end
203
+ else
204
+ message.to_s[0..80]
205
+ end
206
+ end
207
+
208
+ def format_hash(hash)
209
+ parts = []
210
+
211
+ # Show key fields for log entries
212
+ if hash['level'] || hash[:level]
213
+ level = hash['level'] || hash[:level]
214
+ parts << colorize_level(level)
215
+ end
216
+
217
+ if hash['message'] || hash[:message]
218
+ msg = hash['message'] || hash[:message]
219
+ parts << truncate(msg.to_s, 50)
220
+ end
221
+
222
+ if hash['id'] || hash[:id]
223
+ id = hash['id'] || hash[:id]
224
+ parts << colorize(id.to_s[0..7], :gray)
225
+ end
226
+
227
+ parts.any? ? parts.join(' ') : hash.keys.first(3).join(', ')
228
+ end
229
+
230
+ def build_output(channel, message, duration)
231
+ time = Time.current.strftime('%H:%M:%S')
232
+ duration_str = duration ? "#{duration.round(1)}ms" : ''
233
+
234
+ parts = [
235
+ colorize(time, :gray),
236
+ colorize('⚡', :magenta),
237
+ colorize(channel, :magenta),
238
+ colorize('→', :gray),
239
+ message,
240
+ colorize(duration_str, :gray)
241
+ ]
242
+
243
+ " #{parts.join(' ')}\n"
244
+ end
245
+
246
+ def truncate(text, length)
247
+ return text if text.nil? || text.length <= length
248
+
249
+ "#{text[0..(length - 4)]}..."
250
+ end
251
+
252
+ def colorize(text, color)
253
+ return text unless $stdout.tty?
254
+ return text unless COLORS[color]
255
+
256
+ "#{COLORS[color]}#{text}#{COLORS[:reset]}"
257
+ end
258
+
259
+ def colorize_level(level)
260
+ color = case level.to_s.downcase
261
+ when 'debug' then :gray
262
+ when 'info' then :green
263
+ when 'warn', 'warning' then :yellow
264
+ when 'error' then :red
265
+ when 'fatal' then :red
266
+ else :white
267
+ end
268
+ colorize(level.to_s.upcase.ljust(5), color)
269
+ end
270
+ end
271
+
272
+ # View rendering subscriber
273
+ class ViewLogSubscriber < ActiveSupport::LogSubscriber
274
+ def render_template(event)
275
+ return unless LogSubscriber.formatter
276
+
277
+ request_id = Thread.current[:brainzlab_request_id]
278
+ return unless request_id
279
+
280
+ payload = event.payload
281
+ template = template_name(payload[:identifier])
282
+
283
+ LogSubscriber.formatter.render_template(request_id,
284
+ template: template,
285
+ duration: event.duration,
286
+ layout: payload[:layout])
287
+ end
288
+
289
+ def render_partial(event)
290
+ return unless LogSubscriber.formatter
291
+
292
+ request_id = Thread.current[:brainzlab_request_id]
293
+ return unless request_id
294
+
295
+ payload = event.payload
296
+ template = template_name(payload[:identifier])
297
+
298
+ LogSubscriber.formatter.render_partial(request_id,
299
+ template: template,
300
+ duration: event.duration,
301
+ count: payload[:count])
302
+ end
303
+
304
+ def render_layout(event)
305
+ return unless LogSubscriber.formatter
306
+
307
+ request_id = Thread.current[:brainzlab_request_id]
308
+ return unless request_id
309
+
310
+ payload = event.payload
311
+ layout = template_name(payload[:identifier])
312
+
313
+ LogSubscriber.formatter.render_layout(request_id,
314
+ layout: layout,
315
+ duration: event.duration)
316
+ end
317
+
318
+ private
319
+
320
+ def template_name(identifier)
321
+ return nil unless identifier
322
+
323
+ # Extract relative path from full identifier
324
+ if identifier.include?('/app/views/')
325
+ identifier.split('/app/views/').last
326
+ elsif identifier.include?('/views/')
327
+ identifier.split('/views/').last
328
+ else
329
+ File.basename(identifier)
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end