catpm 0.9.0 → 0.9.1
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 +4 -4
- data/README.md +1 -1
- data/lib/catpm/collector.rb +73 -11
- data/lib/catpm/middleware.rb +1 -0
- data/lib/catpm/trace.rb +5 -0
- data/lib/catpm/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bb890d1a121d0351a7153a0575608c76c9b003b2d5798931fa4eaaca0067b81
|
|
4
|
+
data.tar.gz: bb4b7b4978e199de34306e18aafcdcac0ed7cd1fcc7a784d685ebe1ba6e9793e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1b377c63b639edf420597d451c2ef2349e73598ca7118bdda9a05c5a455ebad517c821f0cd2a8d8e9a6fde4b1dd71f15482442f9192158b08a6a5564996d3a6c
|
|
7
|
+
data.tar.gz: ef93cd60ad7840b0a90fd5389ef3ec8f35016fd01eb5d19032fc7e73822c7218d1259a7c326ccfc80109f944ccddddb2eeb9e8e638dae8fe725d7741974704ac
|
data/README.md
CHANGED
data/lib/catpm/collector.rb
CHANGED
|
@@ -4,6 +4,10 @@ module Catpm
|
|
|
4
4
|
module Collector
|
|
5
5
|
SYNTHETIC_MIDDLEWARE_OFFSET_MS = 0.5
|
|
6
6
|
MIN_GAP_MS = 1.0
|
|
7
|
+
# Cap global force-instrument counter to avoid cascade when many requests
|
|
8
|
+
# are slow. Without this cap, apps with 30% slow requests would see ~23%
|
|
9
|
+
# instrumentation instead of the configured 1/random_sample_rate.
|
|
10
|
+
MAX_FORCE_INSTRUMENT_COUNT = 3
|
|
7
11
|
|
|
8
12
|
class << self
|
|
9
13
|
def process_action_controller(event)
|
|
@@ -19,7 +23,11 @@ module Catpm
|
|
|
19
23
|
metadata = build_http_metadata(payload)
|
|
20
24
|
|
|
21
25
|
req_segments = Thread.current[:catpm_request_segments]
|
|
22
|
-
|
|
26
|
+
# track_request clears req_segments after processing but leaves a marker
|
|
27
|
+
# so we know the request was already fully instrumented.
|
|
28
|
+
tracked_instrumented = Thread.current[:catpm_tracked_instrumented]
|
|
29
|
+
Thread.current[:catpm_tracked_instrumented] = nil
|
|
30
|
+
instrumented = !req_segments.nil? || tracked_instrumented
|
|
23
31
|
|
|
24
32
|
if req_segments
|
|
25
33
|
segment_data = req_segments.to_h
|
|
@@ -52,9 +60,11 @@ module Catpm
|
|
|
52
60
|
instrumented: instrumented
|
|
53
61
|
)
|
|
54
62
|
|
|
55
|
-
# Slow spike detection: force
|
|
63
|
+
# Slow spike detection: force the NEXT HTTP request through middleware
|
|
64
|
+
# to be fully instrumented (uses global counter for should_instrument_request?).
|
|
65
|
+
# Skip if already handled by track_request (tracked_instrumented).
|
|
56
66
|
if !instrumented && (payload[:exception] || duration >= Catpm.config.slow_threshold_for(:http))
|
|
57
|
-
trigger_force_instrument
|
|
67
|
+
trigger_force_instrument
|
|
58
68
|
end
|
|
59
69
|
|
|
60
70
|
if sample_type
|
|
@@ -470,28 +480,68 @@ module Catpm
|
|
|
470
480
|
|
|
471
481
|
# Called when a slow/error request had no instrumentation —
|
|
472
482
|
# forces the NEXT request(s) to be fully instrumented.
|
|
483
|
+
#
|
|
484
|
+
# Two modes (mutually exclusive to avoid double-instrumentation):
|
|
485
|
+
# - With endpoint: sets per-endpoint flag consumed by should_instrument?
|
|
486
|
+
# (for track_request paths where endpoint is known)
|
|
487
|
+
# - Without endpoint: increments global counter consumed by
|
|
488
|
+
# should_instrument_request? (for middleware path where endpoint is unknown)
|
|
473
489
|
def trigger_force_instrument(kind: nil, target: nil, operation: nil)
|
|
474
490
|
if kind && target
|
|
475
491
|
endpoint_key = [kind.to_s, target.to_s, (operation || '').to_s]
|
|
476
492
|
force_instrument_endpoints[endpoint_key] = true
|
|
493
|
+
else
|
|
494
|
+
@force_instrument_count = [(@force_instrument_count || 0) + 1, MAX_FORCE_INSTRUMENT_COUNT].min
|
|
477
495
|
end
|
|
478
|
-
@force_instrument_count = (@force_instrument_count || 0) + 1
|
|
479
496
|
end
|
|
480
497
|
|
|
481
498
|
def reset_sample_counts!
|
|
482
499
|
@instrumented_sample_counts = nil
|
|
500
|
+
@instrumented_sample_counts_loaded = false
|
|
483
501
|
@force_instrument_endpoints = nil
|
|
484
502
|
@force_instrument_count = nil
|
|
485
503
|
end
|
|
486
504
|
|
|
487
|
-
|
|
505
|
+
private
|
|
488
506
|
|
|
489
507
|
def force_instrument_endpoints
|
|
490
508
|
@force_instrument_endpoints ||= {}
|
|
491
509
|
end
|
|
492
510
|
|
|
493
511
|
def instrumented_sample_counts
|
|
494
|
-
@instrumented_sample_counts
|
|
512
|
+
return @instrumented_sample_counts if @instrumented_sample_counts_loaded
|
|
513
|
+
|
|
514
|
+
@instrumented_sample_counts = load_sample_counts_from_db
|
|
515
|
+
@instrumented_sample_counts_loaded = true
|
|
516
|
+
@instrumented_sample_counts
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Pre-populate filling counters from DB so old endpoints don't
|
|
520
|
+
# re-enter filling phase on every process restart.
|
|
521
|
+
# Temporarily clears thread-local to prevent our query from being
|
|
522
|
+
# captured as a segment in any active request.
|
|
523
|
+
def load_sample_counts_from_db
|
|
524
|
+
counts = Hash.new(0)
|
|
525
|
+
return counts unless defined?(Catpm::Sample) && Catpm::Bucket.table_exists?
|
|
526
|
+
|
|
527
|
+
saved_rs = Thread.current[:catpm_request_segments]
|
|
528
|
+
Thread.current[:catpm_request_segments] = nil
|
|
529
|
+
begin
|
|
530
|
+
Catpm::Sample.joins(:bucket)
|
|
531
|
+
.where(sample_type: 'random')
|
|
532
|
+
.group('catpm_buckets.kind', 'catpm_buckets.target', 'catpm_buckets.operation')
|
|
533
|
+
.count
|
|
534
|
+
.each do |(kind, target, operation), count|
|
|
535
|
+
counts[[kind.to_s, target.to_s, operation.to_s]] = count
|
|
536
|
+
end
|
|
537
|
+
ensure
|
|
538
|
+
Thread.current[:catpm_request_segments] = saved_rs
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
counts
|
|
542
|
+
rescue => e
|
|
543
|
+
Catpm.config.error_handler&.call(e)
|
|
544
|
+
Hash.new(0)
|
|
495
545
|
end
|
|
496
546
|
|
|
497
547
|
# Remove near-zero-duration "code" spans that merely wrap a "controller" span.
|
|
@@ -555,22 +605,34 @@ module Catpm
|
|
|
555
605
|
# Filling counter only increments for instrumented requests so
|
|
556
606
|
# non-instrumented requests don't waste filling slots.
|
|
557
607
|
def early_sample_type(error:, duration:, kind:, target:, operation:, instrumented: true)
|
|
558
|
-
|
|
559
|
-
|
|
608
|
+
# Errors: only create sample for instrumented requests (with segments).
|
|
609
|
+
# Non-instrumented errors are still tracked in error_groups via
|
|
610
|
+
# event.error? — occurrence counts, contexts, and backtrace are preserved.
|
|
611
|
+
# trigger_force_instrument ensures the next occurrence gets full segments.
|
|
612
|
+
return 'error' if error && instrumented
|
|
613
|
+
|
|
614
|
+
is_slow = duration >= Catpm.config.slow_threshold_for(kind.to_sym)
|
|
615
|
+
|
|
616
|
+
# Non-instrumented slow requests still get a sample (for dashboard) but
|
|
617
|
+
# don't count towards filling phase (they have no segments).
|
|
618
|
+
return 'slow' if is_slow && !instrumented
|
|
560
619
|
|
|
561
620
|
# Non-instrumented requests have no segments — skip sample creation
|
|
562
621
|
return nil unless instrumented
|
|
563
622
|
|
|
564
|
-
#
|
|
623
|
+
# Count this instrumented request towards filling phase completion.
|
|
624
|
+
# Both slow and random requests count — without this, endpoints where
|
|
625
|
+
# most requests exceed slow_threshold would never exit the filling phase,
|
|
626
|
+
# causing 100% instrumentation regardless of random_sample_rate.
|
|
565
627
|
endpoint_key = [kind.to_s, target, operation.to_s]
|
|
566
628
|
count = instrumented_sample_counts[endpoint_key]
|
|
567
629
|
max_random = Catpm.config.max_random_samples_per_endpoint
|
|
568
630
|
if max_random.nil? || count < max_random
|
|
569
631
|
instrumented_sample_counts[endpoint_key] = count + 1
|
|
570
|
-
return 'random'
|
|
571
632
|
end
|
|
572
633
|
|
|
573
|
-
|
|
634
|
+
return 'slow' if is_slow
|
|
635
|
+
|
|
574
636
|
'random'
|
|
575
637
|
end
|
|
576
638
|
|
data/lib/catpm/middleware.rb
CHANGED
data/lib/catpm/trace.rb
CHANGED
|
@@ -129,6 +129,11 @@ module Catpm
|
|
|
129
129
|
if owns_segments
|
|
130
130
|
req_segments&.release!
|
|
131
131
|
Thread.current[:catpm_request_segments] = nil
|
|
132
|
+
# Mark that this request was already instrumented and processed by
|
|
133
|
+
# track_request. Without this, process_action_controller would see
|
|
134
|
+
# nil req_segments and falsely trigger force_instrument for slow
|
|
135
|
+
# requests — even though they were fully instrumented here.
|
|
136
|
+
Thread.current[:catpm_tracked_instrumented] = true
|
|
132
137
|
end
|
|
133
138
|
end
|
|
134
139
|
end
|
data/lib/catpm/version.rb
CHANGED