brainzlab 0.1.21 → 0.1.23

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: 270efe6b3c0c0dbb73b656bf71512df7c076c099bfbbc338998339617041c3a8
4
- data.tar.gz: f38258fa3dbdd3987514ec4cef4585ad3824c30f63ffc276789c156199d8553d
3
+ metadata.gz: bba9f09002a8dcd486cb1b110567274f2d558c471eed12657996fbb0589cbba0
4
+ data.tar.gz: 60a5d0b80300d8ccf3aa6aa48bd615514fd222246be796f3ba7e2c9260578050
5
5
  SHA512:
6
- metadata.gz: 4ae23a5d31515df4c0a21b9b456321286dc0d6d085fa32a2b0da618f64289f6c9bd398849cc9e9461b84cb98f9daad5358f94804e2dffabbe250c7f722269227
7
- data.tar.gz: 9b83420964240f4e97f54c39ff53d9b7a2418b1c507179c64c7212e4f7dba6155547efc9be71288460e6d83bd1710aaeb40eccbb6bc711bbb27b8718aaca7a98
6
+ metadata.gz: 2d4d0694747b4b08413636cb030465ad5babdd79388d8751f6fe6482229eea1dee0ca891711eb82a56641f745018b09c04cdfdcd87843ac1271c40f0b54bd2e8
7
+ data.tar.gz: cfd9ba154ac07f66eca51f22a4666a5c5a97db6c28265b725b7fcb1bbbd90a9064203957b0a26a39164201b963d55da8dbe7f23f45a9357cd89eccbe1cbce202
@@ -158,7 +158,13 @@ module BrainzLab
158
158
  :devtools_expand_by_default,
159
159
  :rails_instrumentation_handled_externally,
160
160
  :development_db_path,
161
- :development_log_output
161
+ :development_log_output,
162
+ :slow_query_threshold,
163
+ :very_slow_query_threshold,
164
+ :slow_query_log_level,
165
+ :very_slow_query_log_level,
166
+ :n_plus_one_threshold,
167
+ :n_plus_one_log_level
162
168
 
163
169
  # Services that should not track themselves to avoid circular dependencies
164
170
  SELF_TRACKING_SERVICES = {
@@ -362,6 +368,16 @@ module BrainzLab
362
368
  @http_ignore_hosts = %w[localhost 127.0.0.1]
363
369
  @redis_ignore_commands = %w[ping info] # Commands to skip tracking
364
370
 
371
+ # ActiveRecord slow-query / N+1 detection — all tunable so a single chatty
372
+ # default can't flood Recall. Slow-query duration is APM data (Pulse already
373
+ # records it), so it defaults to a quiet level (:debug) and a higher threshold.
374
+ @slow_query_threshold = (ENV['BRAINZLAB_SLOW_QUERY_THRESHOLD'] || 500).to_i # ms (was hardcoded 100)
375
+ @very_slow_query_threshold = (ENV['BRAINZLAB_VERY_SLOW_QUERY_THRESHOLD'] || 2000).to_i # ms (was hardcoded 1000)
376
+ @slow_query_log_level = (ENV['BRAINZLAB_SLOW_QUERY_LOG_LEVEL'] || 'debug').to_sym # was :warn → floods
377
+ @very_slow_query_log_level = (ENV['BRAINZLAB_VERY_SLOW_QUERY_LOG_LEVEL'] || 'warn').to_sym
378
+ @n_plus_one_threshold = (ENV['BRAINZLAB_N_PLUS_ONE_THRESHOLD'] || 10).to_i # was hardcoded 5
379
+ @n_plus_one_log_level = (ENV['BRAINZLAB_N_PLUS_ONE_LOG_LEVEL'] || 'debug').to_sym # was :warning
380
+
365
381
  # Log formatter settings
366
382
  @log_formatter_enabled = true
367
383
  @log_formatter_colors = nil # auto-detect TTY
@@ -6,11 +6,13 @@ module BrainzLab
6
6
  SCHEMA_QUERIES = %w[SCHEMA EXPLAIN].freeze
7
7
  INTERNAL_TABLES = %w[pg_ information_schema sqlite_ mysql.].freeze
8
8
 
9
- # Thresholds for slow query detection (in milliseconds)
9
+ # Slow-query / N+1 thresholds + log levels are now configurable — see
10
+ # BrainzLab::Configuration (slow_query_threshold, very_slow_query_threshold,
11
+ # slow_query_log_level, very_slow_query_log_level, n_plus_one_threshold,
12
+ # n_plus_one_log_level) + their BRAINZLAB_* env vars. These constants are
13
+ # kept only as legacy fallbacks and are no longer read by the instrumentation.
10
14
  SLOW_QUERY_THRESHOLD = 100
11
15
  VERY_SLOW_QUERY_THRESHOLD = 1000
12
-
13
- # N+1 detection settings
14
16
  N_PLUS_ONE_THRESHOLD = 5 # queries to same table in single request
15
17
  N_PLUS_ONE_WINDOW = 50 # max queries to track per request
16
18
 
@@ -58,7 +60,7 @@ module BrainzLab
58
60
  record_sql_span(event, duration)
59
61
 
60
62
  # Log slow queries to Recall
61
- log_slow_query(event, duration) if duration >= SLOW_QUERY_THRESHOLD
63
+ log_slow_query(event, duration) if duration >= BrainzLab.configuration.slow_query_threshold
62
64
 
63
65
  # Track for N+1 detection
64
66
  track_query_for_n_plus_one(event)
@@ -79,9 +81,10 @@ module BrainzLab
79
81
  end
80
82
 
81
83
  # Determine level based on duration
84
+ cfg = BrainzLab.configuration
82
85
  level = case duration
83
- when 0...SLOW_QUERY_THRESHOLD then :info
84
- when SLOW_QUERY_THRESHOLD...VERY_SLOW_QUERY_THRESHOLD then :warning
86
+ when 0...cfg.slow_query_threshold then :info
87
+ when cfg.slow_query_threshold...cfg.very_slow_query_threshold then :warning
85
88
  else :error
86
89
  end
87
90
 
@@ -146,7 +149,8 @@ module BrainzLab
146
149
  operation = extract_operation(sql)
147
150
  name = payload[:name] || 'SQL'
148
151
 
149
- level = duration >= VERY_SLOW_QUERY_THRESHOLD ? :error : :warn
152
+ cfg = BrainzLab.configuration
153
+ level = duration >= cfg.very_slow_query_threshold ? cfg.very_slow_query_log_level : cfg.slow_query_log_level
150
154
 
151
155
  BrainzLab::Recall.send(
152
156
  level,
@@ -158,7 +162,7 @@ module BrainzLab
158
162
  row_count: payload[:row_count],
159
163
  affected_rows: payload[:affected_rows],
160
164
  connection_name: extract_connection_name(payload[:connection]),
161
- threshold_exceeded: duration >= VERY_SLOW_QUERY_THRESHOLD ? 'critical' : 'warning'
165
+ threshold_exceeded: duration >= cfg.very_slow_query_threshold ? 'critical' : 'warning'
162
166
  )
163
167
  end
164
168
 
@@ -188,7 +192,7 @@ module BrainzLab
188
192
  end
189
193
 
190
194
  # Detect N+1 pattern
191
- if tracker[:count] == N_PLUS_ONE_THRESHOLD
195
+ if tracker[:count] == BrainzLab.configuration.n_plus_one_threshold
192
196
  report_n_plus_one(table, tracker)
193
197
  end
194
198
  end
@@ -197,7 +201,7 @@ module BrainzLab
197
201
  BrainzLab::Reflex.add_breadcrumb(
198
202
  "Potential N+1 detected: #{tracker[:count]}+ queries to '#{table}'",
199
203
  category: 'db.n_plus_one',
200
- level: :warning,
204
+ level: BrainzLab.configuration.n_plus_one_log_level,
201
205
  data: {
202
206
  table: table,
203
207
  query_count: tracker[:count],
@@ -644,6 +644,17 @@ module BrainzLab
644
644
  obj.is_a?(Hash) || (!obj.is_a?(Array) && obj.respond_to?(:to_h) && obj.respond_to?(:each))
645
645
  end
646
646
 
647
+ # Convert hash-like values for display without raising. ActionController::Parameters
648
+ # raises UnfilteredParameters on #to_h when not permitted — use #to_unsafe_h (this
649
+ # is a log formatter, not a security boundary; param filtering happens elsewhere).
650
+ def safe_to_h(obj)
651
+ return obj.to_unsafe_h if obj.respond_to?(:to_unsafe_h)
652
+
653
+ obj.to_h
654
+ rescue StandardError
655
+ obj
656
+ end
657
+
647
658
  def format_params_toml(params, prefix = '', depth = 0)
648
659
  lines = []
649
660
  line_prefix = colorize("#{BOX[:vertical]} ", :cyan)
@@ -654,11 +665,7 @@ module BrainzLab
654
665
 
655
666
  case value
656
667
  when Hash, ActionController::Parameters
657
- value_hash = begin
658
- value.to_h
659
- rescue StandardError
660
- value
661
- end
668
+ value_hash = safe_to_h(value)
662
669
  if value_hash.keys.length <= 3 && value_hash.values.all? { |v| !hash_like?(v) && !v.is_a?(Array) }
663
670
  # Compact inline hash for simple cases
664
671
  inline = value_hash.map { |k, v| "#{k} = #{format_value(v)}" }.join(', ')
@@ -669,16 +676,12 @@ module BrainzLab
669
676
  value_hash.each do |k, v|
670
677
  if hash_like?(v)
671
678
  # Recursively format nested hashes
672
- lines << format_hash_nested(v.to_h, "#{full_key}.#{k}", depth + 1)
679
+ lines << format_hash_nested(safe_to_h(v), "#{full_key}.#{k}", depth + 1)
673
680
  elsif v.is_a?(Array) && hash_like?(v.first)
674
681
  lines << "#{line_prefix}#{indent} #{colorize("[[#{full_key}.#{k}]]",
675
682
  :gray)} #{colorize("# #{v.length} items", :gray)}"
676
683
  if v.first
677
- first_hash = begin
678
- v.first.to_h
679
- rescue StandardError
680
- v.first
681
- end
684
+ first_hash = safe_to_h(v.first)
682
685
  first_hash.each do |nested_k, nested_v|
683
686
  lines << "#{line_prefix}#{indent} #{colorize(nested_k.to_s, :white)} = #{format_value(nested_v)}"
684
687
  end
@@ -699,19 +702,11 @@ module BrainzLab
699
702
  :gray)} #{colorize("# #{value.length} items", :gray)}"
700
703
  # Show first item fully expanded
701
704
  if value.first
702
- first_item = begin
703
- value.first.to_h
704
- rescue StandardError
705
- value.first
706
- end
705
+ first_item = safe_to_h(value.first)
707
706
  first_item.each do |k, v|
708
707
  if hash_like?(v)
709
708
  # Expand nested hash fully
710
- nested_hash = begin
711
- v.to_h
712
- rescue StandardError
713
- v
714
- end
709
+ nested_hash = safe_to_h(v)
715
710
  lines << "#{line_prefix}#{indent} #{colorize("[#{k}]", :gray)}"
716
711
  nested_hash.each do |nested_k, nested_v|
717
712
  lines << "#{line_prefix}#{indent} #{colorize(nested_k.to_s,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BrainzLab
4
- VERSION = '0.1.21'
4
+ VERSION = '0.1.23'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainzlab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.21
4
+ version: 0.1.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - BrainzLab