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.
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.segment_source_threshold = 0.0 # ms — capture caller_locations above this
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
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