miniapm 1.0.0 → 1.3.0
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c449015f1ca45ec37a611645ce5e220f33eb59cfcc2e1d5d33a703ee5acd37b7
|
|
4
|
+
data.tar.gz: 415f6d83da9ef99c12b104ac5c23265585c47758176894734622903ed327ad60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6de6e509faec967f0da90e233823b6ecf325a5f4e3b6d3d248569906f9310fcd07718cb3af3795bf9b454315b1f3c131fcf66031d12c6f8135c4a478fa08ae3d
|
|
7
|
+
data.tar.gz: 2a82ad3cab7be447ed2be3531eb862c0803ba1757bd34a7fd0f2d2874fd4d8e23e16a90f8a974460fa79232bde8327e5d91048f0410f8208f3214743dd1f63b5
|
|
@@ -138,7 +138,7 @@ module MiniAPM
|
|
|
138
138
|
class InstrumentationConfig
|
|
139
139
|
DEFAULTS = {
|
|
140
140
|
rails: { enabled: true },
|
|
141
|
-
activerecord: { enabled: true, log_sql:
|
|
141
|
+
activerecord: { enabled: true, log_sql: true },
|
|
142
142
|
activejob: { enabled: true },
|
|
143
143
|
sidekiq: { enabled: true },
|
|
144
144
|
cache: { enabled: true },
|
data/lib/miniapm/error_event.rb
CHANGED
|
@@ -7,7 +7,10 @@ module MiniAPM
|
|
|
7
7
|
class ErrorEvent
|
|
8
8
|
attr_reader :exception_class, :message, :backtrace, :fingerprint
|
|
9
9
|
attr_reader :request_id, :user_id, :params, :timestamp
|
|
10
|
-
attr_reader :context
|
|
10
|
+
attr_reader :context, :source_context
|
|
11
|
+
|
|
12
|
+
# Number of lines to include before and after the error line
|
|
13
|
+
CONTEXT_LINES = 5
|
|
11
14
|
|
|
12
15
|
def initialize(
|
|
13
16
|
exception_class:,
|
|
@@ -29,6 +32,7 @@ module MiniAPM
|
|
|
29
32
|
@params = filter_params(params)
|
|
30
33
|
@timestamp = timestamp || Time.now.utc
|
|
31
34
|
@context = context
|
|
35
|
+
@source_context = extract_source_context
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def self.from_exception(exception, context = {})
|
|
@@ -44,7 +48,7 @@ module MiniAPM
|
|
|
44
48
|
end
|
|
45
49
|
|
|
46
50
|
def to_h
|
|
47
|
-
{
|
|
51
|
+
hash = {
|
|
48
52
|
exception_class: @exception_class,
|
|
49
53
|
message: @message,
|
|
50
54
|
backtrace: @backtrace,
|
|
@@ -53,7 +57,9 @@ module MiniAPM
|
|
|
53
57
|
user_id: @user_id,
|
|
54
58
|
params: @params,
|
|
55
59
|
timestamp: @timestamp.iso8601
|
|
56
|
-
}
|
|
60
|
+
}
|
|
61
|
+
hash[:source_context] = @source_context if @source_context
|
|
62
|
+
hash.compact
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
private
|
|
@@ -126,5 +132,60 @@ module MiniAPM
|
|
|
126
132
|
string = string.to_s
|
|
127
133
|
string.length > max_length ? string[0, max_length] + "..." : string
|
|
128
134
|
end
|
|
135
|
+
|
|
136
|
+
def extract_source_context
|
|
137
|
+
# Find first application backtrace line (not gem/stdlib)
|
|
138
|
+
app_line = @backtrace.find do |line|
|
|
139
|
+
!line.include?("/gems/") &&
|
|
140
|
+
!line.include?("/ruby/") &&
|
|
141
|
+
!line.include?("/vendor/") &&
|
|
142
|
+
!line.include?("/bundle/") &&
|
|
143
|
+
!line.start_with?("<")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
return nil unless app_line
|
|
147
|
+
|
|
148
|
+
# Parse file:line from backtrace line
|
|
149
|
+
# Format: "/path/to/file.rb:123:in `method_name'" or "/path/to/file.rb:123"
|
|
150
|
+
match = app_line.match(/\A(.+?):(\d+)/)
|
|
151
|
+
return nil unless match
|
|
152
|
+
|
|
153
|
+
file_path = match[1]
|
|
154
|
+
lineno = match[2].to_i
|
|
155
|
+
|
|
156
|
+
# Handle relative paths (e.g., "app/controllers/...") by prepending Rails.root
|
|
157
|
+
unless file_path.start_with?("/")
|
|
158
|
+
if defined?(Rails) && Rails.respond_to?(:root) && Rails.root
|
|
159
|
+
file_path = File.join(Rails.root.to_s, file_path)
|
|
160
|
+
else
|
|
161
|
+
return nil
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
return nil unless File.exist?(file_path) && File.readable?(file_path)
|
|
166
|
+
|
|
167
|
+
begin
|
|
168
|
+
lines = File.readlines(file_path)
|
|
169
|
+
return nil if lineno < 1 || lineno > lines.length
|
|
170
|
+
|
|
171
|
+
# Calculate context range (0-indexed)
|
|
172
|
+
start_line = [lineno - CONTEXT_LINES - 1, 0].max
|
|
173
|
+
end_line = [lineno + CONTEXT_LINES - 1, lines.length - 1].min
|
|
174
|
+
|
|
175
|
+
pre_context = lines[start_line...lineno - 1].map(&:chomp)
|
|
176
|
+
context_line = lines[lineno - 1]&.chomp || ""
|
|
177
|
+
post_context = lines[lineno..end_line].map(&:chomp)
|
|
178
|
+
|
|
179
|
+
{
|
|
180
|
+
file: file_path,
|
|
181
|
+
lineno: lineno,
|
|
182
|
+
pre_context: pre_context,
|
|
183
|
+
context_line: context_line,
|
|
184
|
+
post_context: post_context
|
|
185
|
+
}
|
|
186
|
+
rescue StandardError
|
|
187
|
+
nil
|
|
188
|
+
end
|
|
189
|
+
end
|
|
129
190
|
end
|
|
130
191
|
end
|
|
@@ -43,7 +43,7 @@ module MiniAPM
|
|
|
43
43
|
|
|
44
44
|
attributes["db.sql.table"] = table if table
|
|
45
45
|
|
|
46
|
-
#
|
|
46
|
+
# Log SQL (configurable, defaults to on)
|
|
47
47
|
if MiniAPM.configuration.instrumentations.options(:activerecord)[:log_sql]
|
|
48
48
|
attributes["db.statement"] = truncate_sql(sql)
|
|
49
49
|
end
|
|
@@ -4,32 +4,88 @@ module MiniAPM
|
|
|
4
4
|
module Instrumentations
|
|
5
5
|
module Rails
|
|
6
6
|
class Controller < Base
|
|
7
|
+
# Subscriber class for view rendering that tracks span context properly
|
|
8
|
+
class ViewSubscriber
|
|
9
|
+
def initialize(type)
|
|
10
|
+
@type = type
|
|
11
|
+
@spans = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def start(name, id, payload)
|
|
15
|
+
return unless MiniAPM.enabled?
|
|
16
|
+
return unless Context.current_trace
|
|
17
|
+
|
|
18
|
+
template = payload[:identifier] || payload[:virtual_path] || "unknown"
|
|
19
|
+
|
|
20
|
+
# Clean up template path
|
|
21
|
+
if defined?(::Rails.root) && ::Rails.root
|
|
22
|
+
template = template.sub(::Rails.root.to_s + "/", "")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
template_name = File.basename(template)
|
|
26
|
+
|
|
27
|
+
attributes = {
|
|
28
|
+
"rails.template" => template,
|
|
29
|
+
"rails.template.type" => @type
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
attributes["rails.layout"] = payload[:layout] if payload[:layout]
|
|
33
|
+
attributes["rails.collection.count"] = payload[:count] if payload[:count]
|
|
34
|
+
|
|
35
|
+
span = Span.new(
|
|
36
|
+
name: "#{@type} #{template_name}",
|
|
37
|
+
category: :view,
|
|
38
|
+
trace_id: Context.current_trace_id,
|
|
39
|
+
parent_span_id: Context.current_span&.span_id,
|
|
40
|
+
attributes: attributes
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Store span by event ID and push to context
|
|
44
|
+
@spans[id] = span
|
|
45
|
+
Context.push_span(span)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def finish(name, id, payload)
|
|
49
|
+
span = @spans.delete(id)
|
|
50
|
+
return unless span
|
|
51
|
+
|
|
52
|
+
# Pop from context and finish
|
|
53
|
+
Context.pop_span
|
|
54
|
+
span.finish
|
|
55
|
+
MiniAPM.record_span(span)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
7
59
|
class << self
|
|
8
60
|
def install!
|
|
9
61
|
return if installed?
|
|
10
62
|
mark_installed!
|
|
11
63
|
|
|
12
|
-
# Subscribe to controller processing
|
|
64
|
+
# Subscribe to controller processing (still uses standard subscribe)
|
|
13
65
|
subscribe("process_action.action_controller") do |event|
|
|
14
66
|
handle_process_action(event)
|
|
15
67
|
end
|
|
16
68
|
|
|
17
|
-
# Subscribe to view rendering
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
69
|
+
# Subscribe to view rendering with monotonic subscribers for proper nesting
|
|
70
|
+
ActiveSupport::Notifications.monotonic_subscribe(
|
|
71
|
+
"render_template.action_view",
|
|
72
|
+
ViewSubscriber.new("render_template")
|
|
73
|
+
)
|
|
21
74
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
75
|
+
ActiveSupport::Notifications.monotonic_subscribe(
|
|
76
|
+
"render_partial.action_view",
|
|
77
|
+
ViewSubscriber.new("render_partial")
|
|
78
|
+
)
|
|
25
79
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
80
|
+
ActiveSupport::Notifications.monotonic_subscribe(
|
|
81
|
+
"render_collection.action_view",
|
|
82
|
+
ViewSubscriber.new("render_collection")
|
|
83
|
+
)
|
|
29
84
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
85
|
+
ActiveSupport::Notifications.monotonic_subscribe(
|
|
86
|
+
"render_layout.action_view",
|
|
87
|
+
ViewSubscriber.new("render_layout")
|
|
88
|
+
)
|
|
33
89
|
end
|
|
34
90
|
|
|
35
91
|
private
|
|
@@ -64,60 +120,16 @@ module MiniAPM
|
|
|
64
120
|
# Record exception if present
|
|
65
121
|
if payload[:exception_object]
|
|
66
122
|
span.record_exception(payload[:exception_object])
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def handle_render_template(event)
|
|
71
|
-
record_view_span("render_template", event)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def handle_render_partial(event)
|
|
75
|
-
record_view_span("render_partial", event)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def handle_render_collection(event)
|
|
79
|
-
record_view_span("render_collection", event)
|
|
80
|
-
end
|
|
81
123
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
payload = event.payload
|
|
91
|
-
template = payload[:identifier] || payload[:virtual_path] || "unknown"
|
|
92
|
-
|
|
93
|
-
# Clean up template path
|
|
94
|
-
if defined?(::Rails.root) && ::Rails.root
|
|
95
|
-
template = template.sub(::Rails.root.to_s + "/", "")
|
|
124
|
+
# Also send to dedicated errors endpoint (with source context)
|
|
125
|
+
MiniAPM.record_error(
|
|
126
|
+
payload[:exception_object],
|
|
127
|
+
context: {
|
|
128
|
+
request_id: payload[:request]&.request_id,
|
|
129
|
+
params: payload[:params]
|
|
130
|
+
}
|
|
131
|
+
)
|
|
96
132
|
end
|
|
97
|
-
|
|
98
|
-
template_name = File.basename(template)
|
|
99
|
-
|
|
100
|
-
attributes = {
|
|
101
|
-
"rails.template" => template,
|
|
102
|
-
"rails.template.type" => type
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if payload[:layout]
|
|
106
|
-
attributes["rails.layout"] = payload[:layout]
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
if payload[:count]
|
|
110
|
-
attributes["rails.collection.count"] = payload[:count]
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
span = create_span_from_event(
|
|
114
|
-
event,
|
|
115
|
-
name: "#{type} #{template_name}",
|
|
116
|
-
category: :view,
|
|
117
|
-
attributes: attributes
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
record_span(span)
|
|
121
133
|
end
|
|
122
134
|
end
|
|
123
135
|
end
|
data/lib/miniapm/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: miniapm
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Hasinski
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|