catpm 0.9.4 → 0.9.6
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 +2 -21
- data/app/controllers/catpm/system_controller.rb +2 -1
- data/app/helpers/catpm/application_helper.rb +2 -4
- data/app/models/catpm/sample.rb +0 -4
- data/app/views/catpm/samples/show.html.erb +2 -2
- data/app/views/catpm/shared/_segments_waterfall.html.erb +7 -1
- data/app/views/catpm/system/index.html.erb +1 -1
- data/lib/catpm/adapter/base.rb +0 -1
- data/lib/catpm/buffer.rb +7 -5
- data/lib/catpm/collector.rb +83 -70
- data/lib/catpm/configuration.rb +30 -10
- data/lib/catpm/engine.rb +0 -4
- data/lib/catpm/event.rb +2 -3
- data/lib/catpm/fingerprint.rb +1 -1
- data/lib/catpm/flusher.rb +77 -202
- data/lib/catpm/lifecycle.rb +8 -1
- data/lib/catpm/middleware.rb +3 -4
- data/lib/catpm/patches/httpclient.rb +1 -1
- data/lib/catpm/patches/net_http.rb +1 -1
- data/lib/catpm/request_segments.rb +18 -79
- data/lib/catpm/segment_subscribers.rb +5 -5
- data/lib/catpm/trace.rb +3 -16
- data/lib/catpm/version.rb +1 -1
- data/lib/catpm.rb +7 -1
- data/lib/generators/catpm/templates/initializer.rb.tt +4 -6
- metadata +1 -2
- data/lib/catpm/auto_instrument.rb +0 -145
data/lib/catpm.rb
CHANGED
|
@@ -22,7 +22,6 @@ require 'catpm/segment_subscribers'
|
|
|
22
22
|
require 'catpm/lifecycle'
|
|
23
23
|
require 'catpm/trace'
|
|
24
24
|
require 'catpm/span_helpers'
|
|
25
|
-
require 'catpm/auto_instrument'
|
|
26
25
|
require 'catpm/engine'
|
|
27
26
|
|
|
28
27
|
module Catpm
|
|
@@ -40,6 +39,7 @@ module Catpm
|
|
|
40
39
|
@buffer = nil
|
|
41
40
|
@flusher = nil
|
|
42
41
|
Fingerprint.reset_caches!
|
|
42
|
+
Collector.reset_sample_counts!
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def enabled?
|
|
@@ -64,5 +64,11 @@ module Catpm
|
|
|
64
64
|
return unless enabled? && config.events_enabled
|
|
65
65
|
buffer&.push(CustomEvent.new(name: name, payload: payload))
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
# Whether a segment with the given duration should be stored (and have source captured).
|
|
69
|
+
# Segments below min_segment_duration are still counted in summary but not stored.
|
|
70
|
+
def segment_storable?(duration)
|
|
71
|
+
duration >= config.min_segment_duration
|
|
72
|
+
end
|
|
67
73
|
end
|
|
68
74
|
end
|
|
@@ -23,13 +23,9 @@ Catpm.configure do |config|
|
|
|
23
23
|
# config.show_untracked_segments = false
|
|
24
24
|
# config.track_own_requests = false
|
|
25
25
|
|
|
26
|
-
# === Auto-instrumentation ===
|
|
27
|
-
# config.service_base_classes = nil # nil = auto-detect (ApplicationService, BaseService)
|
|
28
|
-
# config.auto_instrument_methods = [] # e.g. ["Worker#process", "Gateway.charge"]
|
|
29
|
-
|
|
30
26
|
# === Segments ===
|
|
31
27
|
# config.max_segments_per_request = 50 # nil = unlimited
|
|
32
|
-
# config.
|
|
28
|
+
# config.min_segment_duration = 5.0 # ms — segments shorter than this are counted but not stored (0 = store all)
|
|
33
29
|
# config.max_sql_length = 200 # nil = no truncation
|
|
34
30
|
# config.slow_threshold = 500 # ms
|
|
35
31
|
# config.slow_threshold_per_kind = {} # { http: 500, job: 5_000, custom: 1_000 }
|
|
@@ -53,8 +49,10 @@ Catpm.configure do |config|
|
|
|
53
49
|
# config.events_enabled = false
|
|
54
50
|
# config.events_max_samples_per_name = 20 # nil = unlimited
|
|
55
51
|
|
|
52
|
+
# === Memory ===
|
|
53
|
+
# config.max_memory = 20 # MB — global memory budget (2% of 1GB server)
|
|
54
|
+
|
|
56
55
|
# === Buffering & Flushing ===
|
|
57
|
-
# config.max_buffer_memory = 8.megabytes
|
|
58
56
|
# config.flush_interval = 30 # seconds
|
|
59
57
|
# config.flush_jitter = 5 # ±seconds
|
|
60
58
|
# config.persistence_batch_size = 100
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: catpm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
@@ -78,7 +78,6 @@ files:
|
|
|
78
78
|
- lib/catpm/adapter/base.rb
|
|
79
79
|
- lib/catpm/adapter/postgresql.rb
|
|
80
80
|
- lib/catpm/adapter/sqlite.rb
|
|
81
|
-
- lib/catpm/auto_instrument.rb
|
|
82
81
|
- lib/catpm/buffer.rb
|
|
83
82
|
- lib/catpm/call_tracer.rb
|
|
84
83
|
- lib/catpm/circuit_breaker.rb
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Catpm
|
|
4
|
-
# Zero-config service auto-instrumentation.
|
|
5
|
-
#
|
|
6
|
-
# Detects common service base classes (ApplicationService, BaseService)
|
|
7
|
-
# and prepends span tracking on their .call class method. Since subclasses
|
|
8
|
-
# inherit .call from the base, ALL service objects get instrumented
|
|
9
|
-
# automatically — no code changes, no configuration lists.
|
|
10
|
-
#
|
|
11
|
-
# The typical Rails service pattern:
|
|
12
|
-
# class ApplicationService
|
|
13
|
-
# def self.call(...) = new(...).call
|
|
14
|
-
# end
|
|
15
|
-
#
|
|
16
|
-
# class Sync::Processor < ApplicationService
|
|
17
|
-
# def call = ...
|
|
18
|
-
# end
|
|
19
|
-
#
|
|
20
|
-
# After auto-instrumentation, Sync::Processor.call creates a span
|
|
21
|
-
# named "Sync::Processor#call" that wraps the entire service execution.
|
|
22
|
-
#
|
|
23
|
-
# Custom base classes:
|
|
24
|
-
# Catpm.configure { |c| c.service_base_classes = ["MyBase"] }
|
|
25
|
-
#
|
|
26
|
-
# Explicit method list for edge cases:
|
|
27
|
-
# Catpm.configure do |c|
|
|
28
|
-
# c.auto_instrument_methods = ["Worker#process", "Gateway.charge"]
|
|
29
|
-
# end
|
|
30
|
-
#
|
|
31
|
-
module AutoInstrument
|
|
32
|
-
DEFAULT_SERVICE_BASES = %w[
|
|
33
|
-
ApplicationService
|
|
34
|
-
BaseService
|
|
35
|
-
].freeze
|
|
36
|
-
|
|
37
|
-
class << self
|
|
38
|
-
def apply!
|
|
39
|
-
instrument_service_bases
|
|
40
|
-
instrument_explicit_methods
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def reset!
|
|
44
|
-
@applied = Set.new
|
|
45
|
-
@bases_applied = Set.new
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
# ─── Auto-detect service base classes ───
|
|
51
|
-
|
|
52
|
-
def instrument_service_bases
|
|
53
|
-
@bases_applied ||= Set.new
|
|
54
|
-
|
|
55
|
-
bases = Catpm.config.service_base_classes
|
|
56
|
-
bases = DEFAULT_SERVICE_BASES if bases.nil?
|
|
57
|
-
|
|
58
|
-
bases.each do |base_name|
|
|
59
|
-
next if @bases_applied.include?(base_name)
|
|
60
|
-
|
|
61
|
-
begin
|
|
62
|
-
klass = Object.const_get(base_name)
|
|
63
|
-
rescue NameError
|
|
64
|
-
next
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
next unless klass.is_a?(Class)
|
|
68
|
-
|
|
69
|
-
# Prepend on the class-level .call so ALL subclasses get instrumented.
|
|
70
|
-
# Since subclasses inherit .call from the base and only override
|
|
71
|
-
# instance #call, this single prepend covers everything.
|
|
72
|
-
if klass.respond_to?(:call)
|
|
73
|
-
# Guard against double-prepend (e.g. code reloading in development)
|
|
74
|
-
already = klass.singleton_class.ancestors.any? do |a|
|
|
75
|
-
a.instance_variable_defined?(:@catpm_service_span)
|
|
76
|
-
end
|
|
77
|
-
next if already
|
|
78
|
-
|
|
79
|
-
mod = Module.new do
|
|
80
|
-
@catpm_service_span = true
|
|
81
|
-
|
|
82
|
-
define_method(:call) do |*args, **kwargs, &block|
|
|
83
|
-
Catpm.span("#{name}#call", type: :code) { super(*args, **kwargs, &block) }
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
klass.singleton_class.prepend(mod)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
@bases_applied << base_name
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# ─── Explicit method list ───
|
|
94
|
-
|
|
95
|
-
def instrument_explicit_methods
|
|
96
|
-
methods = Catpm.config.auto_instrument_methods
|
|
97
|
-
return if methods.nil? || methods.empty?
|
|
98
|
-
|
|
99
|
-
@applied ||= Set.new
|
|
100
|
-
|
|
101
|
-
methods.each do |method_spec|
|
|
102
|
-
next if @applied.include?(method_spec)
|
|
103
|
-
|
|
104
|
-
if method_spec.include?('#')
|
|
105
|
-
class_name, method_name = method_spec.split('#', 2)
|
|
106
|
-
instrument_instance_method(class_name, method_name, method_spec)
|
|
107
|
-
elsif method_spec.include?('.')
|
|
108
|
-
class_name, method_name = method_spec.split('.', 2)
|
|
109
|
-
instrument_class_method(class_name, method_name, method_spec)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def instrument_instance_method(class_name, method_name, spec)
|
|
115
|
-
klass = Object.const_get(class_name)
|
|
116
|
-
span_name = spec
|
|
117
|
-
|
|
118
|
-
mod = Module.new do
|
|
119
|
-
define_method(method_name.to_sym) do |*args, **kwargs, &block|
|
|
120
|
-
Catpm.span(span_name) { super(*args, **kwargs, &block) }
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
klass.prepend(mod)
|
|
124
|
-
@applied << spec
|
|
125
|
-
rescue NameError
|
|
126
|
-
nil
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def instrument_class_method(class_name, method_name, spec)
|
|
130
|
-
klass = Object.const_get(class_name)
|
|
131
|
-
span_name = spec
|
|
132
|
-
|
|
133
|
-
mod = Module.new do
|
|
134
|
-
define_method(method_name.to_sym) do |*args, **kwargs, &block|
|
|
135
|
-
Catpm.span(span_name) { super(*args, **kwargs, &block) }
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
klass.singleton_class.prepend(mod)
|
|
139
|
-
@applied << spec
|
|
140
|
-
rescue NameError
|
|
141
|
-
nil
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|