solid_apm 0.9.0 → 0.10.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 +4 -4
- data/README.md +107 -0
- data/app/jobs/solid_apm/cleanup_job.rb +13 -0
- data/app/models/solid_apm/span_subscriber/base.rb +1 -0
- data/lib/solid_apm/cleanup_service.rb +51 -0
- data/lib/solid_apm/middleware.rb +19 -2
- data/lib/solid_apm/sampler.rb +12 -0
- data/lib/solid_apm/version.rb +1 -1
- data/lib/solid_apm.rb +13 -2
- data/lib/tasks/solid_apm_tasks.rake +18 -4
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3d5be7324da26b5f4720bc0ba9e61717be304286b9ecad84e09adf503095699
|
4
|
+
data.tar.gz: 8969d758c906bb7de44b4024e1f7353bc63f1f4dda0f2ee5631df1e2fa436e2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ed2237e30cabd350515555a5e6a66006777835d4e07adcc2dc2758f0fe8e474e931e3da204f44309671923ba1eb1fbcbc32203eaf459a6cebffc2eb3030f3fa
|
7
|
+
data.tar.gz: fb86bb4e14bf08207255ca5e0121b6e50a5ba43e3a4d394d777362df614d47d25e0d54508a66660c01c7ecccf3c53cc00b395981bee6a32378a5903ca64dd268
|
data/README.md
CHANGED
@@ -52,6 +52,113 @@ class ApplicationController
|
|
52
52
|
end
|
53
53
|
```
|
54
54
|
|
55
|
+
## Configuration
|
56
|
+
|
57
|
+
SolidAPM can be configured using the following options in your `config/initializers/solid_apm.rb` file:
|
58
|
+
|
59
|
+
### Database Connection
|
60
|
+
|
61
|
+
Configure the database connection for SolidAPM:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
SolidApm.connects_to = { database: { writing: :solid_apm } }
|
65
|
+
```
|
66
|
+
|
67
|
+
### ActiveRecord Logger Silencing
|
68
|
+
|
69
|
+
Control whether ActiveRecord logger is silenced during SolidAPM operations (default: `true`):
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
# Disable ActiveRecord logger silencing to see SQL queries in logs
|
73
|
+
SolidApm.silence_active_record_logger = false
|
74
|
+
```
|
75
|
+
|
76
|
+
### Transaction Sampling
|
77
|
+
|
78
|
+
Control the sampling rate for transactions using a "1 out of N" approach (default: `1`):
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
# Sample every transaction (default behavior)
|
82
|
+
SolidApm.transaction_sampling = 1
|
83
|
+
|
84
|
+
# Sample 1 out of every 2 transactions (50% sampling)
|
85
|
+
SolidApm.transaction_sampling = 2
|
86
|
+
|
87
|
+
# Sample 1 out of every 5 transactions (20% sampling)
|
88
|
+
SolidApm.transaction_sampling = 5
|
89
|
+
|
90
|
+
# Sample 1 out of every 10 transactions (10% sampling)
|
91
|
+
SolidApm.transaction_sampling = 10
|
92
|
+
```
|
93
|
+
|
94
|
+
The sampling is done per-thread using a round-robin counter, ensuring even distribution across requests.
|
95
|
+
This is useful for high-traffic applications where you want to reduce the volume of
|
96
|
+
APM data while still maintaining representative performance insights.
|
97
|
+
|
98
|
+
### Transaction Name Filtering
|
99
|
+
|
100
|
+
Filter specific transactions by name using exact string matches or regular expressions:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
# Filter specific transactions by exact name
|
104
|
+
SolidApm.transaction_filters += ['HomeController#index', /^Rails::HealthController/]
|
105
|
+
```
|
106
|
+
|
107
|
+
## Data Cleanup
|
108
|
+
|
109
|
+
SolidAPM provides a rake task to clean up old transaction data to manage database size over time.
|
110
|
+
|
111
|
+
### Manual Cleanup
|
112
|
+
|
113
|
+
Clean up transactions older than 1 month (default):
|
114
|
+
|
115
|
+
```shell
|
116
|
+
bin/rails solid_apm:cleanup
|
117
|
+
```
|
118
|
+
|
119
|
+
Clean up transactions with custom time periods:
|
120
|
+
|
121
|
+
```shell
|
122
|
+
# Delete transactions older than 1 week
|
123
|
+
bin/rails solid_apm:cleanup[1.week.ago]
|
124
|
+
```
|
125
|
+
|
126
|
+
### Automated Cleanup with ActiveJob
|
127
|
+
|
128
|
+
For production applications, it's recommended to set up automated cleanup.
|
129
|
+
|
130
|
+
Example with SolidQueue. Configure recurring cleanup in your `config/recurring.yml`:
|
131
|
+
|
132
|
+
```yaml
|
133
|
+
solid_apm_cleanup_weekly:
|
134
|
+
class: SolidApm::CleanupJob
|
135
|
+
cron: "0 3 * * *" # Every day at 3 AM
|
136
|
+
args: ["1.week.ago"]
|
137
|
+
```
|
138
|
+
|
139
|
+
## How it works
|
140
|
+
|
141
|
+
SolidAPM stores information in the form of transactions, representing incoming HTTP requests which
|
142
|
+
listen to a variety of spans (events) from `ActiveSupport::Instrument`. Each span
|
143
|
+
saves backtrace information to easily find the source of issues.
|
144
|
+
|
145
|
+
### Request transaction
|
146
|
+
|
147
|
+
It is based on [ActionDispatch](https://guides.rubyonrails.org/active_support_instrumentation.html#action-dispatch)
|
148
|
+
events to start and end a transaction.
|
149
|
+
|
150
|
+
A Rack middleware uses [`rack.after_reply`](https://github.blog/engineering/architecture-optimization/performance-at-github-deferring-stats-with-rack-after_reply/)
|
151
|
+
to bulk insert transactions and spans after delivering the response, so tracking your application
|
152
|
+
doesn't add delay to the client.
|
153
|
+
|
154
|
+
### Spans saved
|
155
|
+
|
156
|
+
* Request
|
157
|
+
* Rendering
|
158
|
+
* SQL requests and transactions
|
159
|
+
* Rails cache
|
160
|
+
* Net/HTTP
|
161
|
+
|
55
162
|
## MCP Server
|
56
163
|
|
57
164
|
SolidAPM offers an optional MCP server to allow an AI agent to interact with SolidAPM
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidApm
|
4
|
+
class CleanupJob < ApplicationJob
|
5
|
+
def perform(older_than = '1.month.ago')
|
6
|
+
result = CleanupService.new(older_than: older_than).call
|
7
|
+
|
8
|
+
Rails.logger.info "SolidApm::CleanupJob completed: deleted #{result[:deleted_count]} transactions older than #{result[:cutoff_time]} (#{result[:older_than]})"
|
9
|
+
|
10
|
+
result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidApm
|
4
|
+
class CleanupService
|
5
|
+
# Regex to match safe time expressions like "1.week.ago", "2.months.ago", etc.
|
6
|
+
DURATION_PATTERN = /\A(\d+)\.(second|minute|hour|day|week|month|year)s?\.ago\z/.freeze
|
7
|
+
def initialize(older_than: '1.month.ago')
|
8
|
+
@older_than = older_than
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
cutoff_time = parse_time_expression(@older_than)
|
13
|
+
deleted_count = Transaction.where(timestamp: ...cutoff_time).destroy_all.size
|
14
|
+
|
15
|
+
{
|
16
|
+
cutoff_time: cutoff_time,
|
17
|
+
deleted_count: deleted_count,
|
18
|
+
older_than: @older_than
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse_time_expression(expression)
|
25
|
+
match = expression.match(DURATION_PATTERN)
|
26
|
+
raise ArgumentError, 'Invalid time expression format' unless match
|
27
|
+
|
28
|
+
number = match[1].to_i
|
29
|
+
unit = match[2]
|
30
|
+
|
31
|
+
case unit
|
32
|
+
when 'second'
|
33
|
+
number.seconds.ago
|
34
|
+
when 'minute'
|
35
|
+
number.minutes.ago
|
36
|
+
when 'hour'
|
37
|
+
number.hours.ago
|
38
|
+
when 'day'
|
39
|
+
number.days.ago
|
40
|
+
when 'week'
|
41
|
+
number.weeks.ago
|
42
|
+
when 'month'
|
43
|
+
number.months.ago
|
44
|
+
when 'year'
|
45
|
+
number.years.ago
|
46
|
+
else
|
47
|
+
raise ArgumentError, "Unsupported time unit: #{unit}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/solid_apm/middleware.rb
CHANGED
@@ -23,7 +23,11 @@ module SolidApm
|
|
23
23
|
def self.call
|
24
24
|
transaction = SpanSubscriber::Base.transaction
|
25
25
|
SpanSubscriber::Base.transaction = nil
|
26
|
-
|
26
|
+
|
27
|
+
if transaction.nil? ||
|
28
|
+
transaction_filtered?(transaction.name) ||
|
29
|
+
!Sampler.should_sample?
|
30
|
+
|
27
31
|
SpanSubscriber::Base.spans = nil
|
28
32
|
return
|
29
33
|
end
|
@@ -41,8 +45,21 @@ module SolidApm
|
|
41
45
|
SpanSubscriber::Base.spans = nil
|
42
46
|
end
|
43
47
|
|
48
|
+
def self.transaction_filtered?(transaction_name)
|
49
|
+
SolidApm.transaction_filters.any? do |filter|
|
50
|
+
case filter
|
51
|
+
when String
|
52
|
+
transaction_name == filter
|
53
|
+
when Regexp
|
54
|
+
filter.match?(transaction_name)
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
44
61
|
def self.with_silence_logger
|
45
|
-
if ActiveRecord::Base.logger
|
62
|
+
if SolidApm.silence_active_record_logger && ActiveRecord::Base.logger
|
46
63
|
ActiveRecord::Base.logger.silence { yield }
|
47
64
|
else
|
48
65
|
yield
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module SolidApm
|
2
|
+
class Sampler
|
3
|
+
def self.should_sample?
|
4
|
+
return true if SolidApm.transaction_sampling <= 1
|
5
|
+
|
6
|
+
thread_counter = Thread.current[:solid_apm_counter] ||= 0
|
7
|
+
Thread.current[:solid_apm_counter] = (thread_counter + 1) % SolidApm.transaction_sampling
|
8
|
+
|
9
|
+
Thread.current[:solid_apm_counter] == 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/solid_apm/version.rb
CHANGED
data/lib/solid_apm.rb
CHANGED
@@ -3,12 +3,23 @@ require 'groupdate'
|
|
3
3
|
require 'active_median'
|
4
4
|
require 'apexcharts'
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
6
|
+
require 'solid_apm/version'
|
7
|
+
require 'solid_apm/engine'
|
8
|
+
require 'solid_apm/sampler'
|
9
|
+
require 'solid_apm/cleanup_service'
|
8
10
|
|
9
11
|
module SolidApm
|
10
12
|
mattr_accessor :connects_to
|
11
13
|
mattr_accessor :mcp_server_config, default: {}
|
14
|
+
mattr_accessor :silence_active_record_logger, default: true
|
15
|
+
mattr_accessor :transaction_sampling, default: 1
|
16
|
+
mattr_accessor(
|
17
|
+
:transaction_filters, default: [
|
18
|
+
/^SolidApm::/,
|
19
|
+
/^ActionDispatch::Request::PASS_NOT_FOUND/,
|
20
|
+
'Rails::HealthController#show'
|
21
|
+
]
|
22
|
+
)
|
12
23
|
|
13
24
|
def self.set_context(context)
|
14
25
|
SpanSubscriber::Base.context = context
|
@@ -1,4 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
namespace :solid_apm do
|
2
|
+
desc 'Delete old transactions (default: older than 1 month). Usage: rake solid_apm:cleanup[1.week.ago]'
|
3
|
+
task :cleanup, [:older_than] => :environment do |_task, args|
|
4
|
+
older_than = args[:older_than] || '1.month.ago'
|
5
|
+
|
6
|
+
begin
|
7
|
+
result = SolidApm::CleanupService.new(older_than: older_than).call
|
8
|
+
|
9
|
+
puts "Deleting transactions older than #{result[:cutoff_time]}..."
|
10
|
+
puts "Deleted #{result[:deleted_count]} transactions"
|
11
|
+
rescue StandardError => e
|
12
|
+
puts "Error: #{e.message}"
|
13
|
+
puts "Please provide a valid time expression like '1.week.ago', '2.months.ago', etc."
|
14
|
+
puts 'Supported formats: [number].[unit].ago where unit is: second(s), minute(s), hour(s), day(s), week(s), month(s), year(s)'
|
15
|
+
exit 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Francis Bastien
|
@@ -125,6 +125,7 @@ files:
|
|
125
125
|
- app/controllers/solid_apm/transactions_controller.rb
|
126
126
|
- app/helpers/solid_apm/application_helper.rb
|
127
127
|
- app/jobs/solid_apm/application_job.rb
|
128
|
+
- app/jobs/solid_apm/cleanup_job.rb
|
128
129
|
- app/models/solid_apm/application_record.rb
|
129
130
|
- app/models/solid_apm/span.rb
|
130
131
|
- app/models/solid_apm/span_subscriber/action_dispatch.rb
|
@@ -145,10 +146,12 @@ files:
|
|
145
146
|
- db/migrate/20240608015633_create_solid_apm_transactions.rb
|
146
147
|
- db/migrate/20240608021940_create_solid_apm_spans.rb
|
147
148
|
- lib/solid_apm.rb
|
149
|
+
- lib/solid_apm/cleanup_service.rb
|
148
150
|
- lib/solid_apm/engine.rb
|
149
151
|
- lib/solid_apm/mcp/impactful_transactions_resource.rb
|
150
152
|
- lib/solid_apm/mcp/spans_for_transaction_tool.rb
|
151
153
|
- lib/solid_apm/middleware.rb
|
154
|
+
- lib/solid_apm/sampler.rb
|
152
155
|
- lib/solid_apm/version.rb
|
153
156
|
- lib/tasks/solid_apm_tasks.rake
|
154
157
|
homepage: https://github.com/Bhacaz/solid_apm
|
@@ -172,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
175
|
- !ruby/object:Gem::Version
|
173
176
|
version: '0'
|
174
177
|
requirements: []
|
175
|
-
rubygems_version: 3.6.
|
178
|
+
rubygems_version: 3.6.9
|
176
179
|
specification_version: 4
|
177
180
|
summary: SolidApm is a DB base engine for Application Performance Monitoring.
|
178
181
|
test_files: []
|