solid_apm 0.8.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6218de46aeec80382ad161cffb631016973a2c5f6f4762095b1e250e3424f010
4
- data.tar.gz: 7ee929e42611dc2861b1954056a3457aab1de4daff968558140b7cdd113268ad
3
+ metadata.gz: e3d5be7324da26b5f4720bc0ba9e61717be304286b9ecad84e09adf503095699
4
+ data.tar.gz: 8969d758c906bb7de44b4024e1f7353bc63f1f4dda0f2ee5631df1e2fa436e2e
5
5
  SHA512:
6
- metadata.gz: c66d5a85d9f39e9c82d916f8c289e41addbfe4d20543ad671e7ce982be49c9954d08e5553c6b68e1a448143fef109d5dc1a16ba2613bafd1921f8c3d3a1ddebe
7
- data.tar.gz: eccd3664d5c31c26408b49db52d6e3c5a377a49861b5500acb18a6f0d56b17422037ae78e90339a6867aaeb7de62f35a040eccb61f4b5118066dda23feeb88af
6
+ metadata.gz: 1ed2237e30cabd350515555a5e6a66006777835d4e07adcc2dc2758f0fe8e474e931e3da204f44309671923ba1eb1fbcbc32203eaf459a6cebffc2eb3030f3fa
7
+ data.tar.gz: fb86bb4e14bf08207255ca5e0121b6e50a5ba43e3a4d394d777362df614d47d25e0d54508a66660c01c7ecccf3c53cc00b395981bee6a32378a5903ca64dd268
data/README.md CHANGED
@@ -5,6 +5,7 @@ Rails engine to manage APM data without using a third party service.
5
5
 
6
6
  <img src="./docs/img.png" width="600px">
7
7
  <img src="./docs/img_1.png" width="600px">
8
+ <img src="./docs/img_2.png" width="600px">
8
9
 
9
10
  ## Installation
10
11
 
@@ -51,6 +52,158 @@ class ApplicationController
51
52
  end
52
53
  ```
53
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
+
162
+ ## MCP Server
163
+
164
+ SolidAPM offers an optional MCP server to allow an AI agent to interact with SolidAPM
165
+ and help identify issues in your application, such as
166
+ N+1 queries, slow queries and more. The AI agent can analyze and suggest fixes for these issues.
167
+
168
+ ### MCP Server Configuration
169
+
170
+ The MCP server is only mounted if the [fast-mcp](https://github.com/yjacquin/fast-mcp) gem is installed by your application.
171
+
172
+ 1. Add to your Gemfile:
173
+
174
+ ```ruby
175
+ # Work in progress, plus patch for MCP 2025-06-18 Protocol Revision
176
+ # with StreamableHTTP support
177
+ # https://github.com/yjacquin/fast-mcp/issues/109
178
+ gem 'fast-mcp', branch: 'transport', github: 'Bhacaz/fast-mcp'
179
+ ```
180
+
181
+ 2. Configure the MCP server in your `config/initializers/solid_apm.rb`:
182
+
183
+ ```ruby
184
+ SolidApm.mcp_server_config = {
185
+ name: 'my-app-solid-apm',
186
+ path: '/solid_apm/mcp',
187
+ auth_token: Rails.application.credentials.solid_apm[:mcp_auth_token]
188
+ }
189
+ ```
190
+
191
+ 3. Test the MCP server by running:
192
+
193
+ ```shell
194
+ curl -X POST http://localhost:3000/solid_apm/mcp \
195
+ -H "Content-Type: application/json" \
196
+ -H "Accept: application/json" \
197
+ -H "Authorization: Bearer <AUTH_TOKEN>" \
198
+ -d '{"jsonrpc":"2.0","method":"tools/list","id":1}
199
+ ```
200
+
201
+ ### MCP usage
202
+
203
+ 1. Add the MCP resource `impactful-transactions` to the context of your prompt.
204
+ 2. Prompt example: "Analyze the impactful transactions of my application and suggest improvements, base on the spans details."
205
+ 3. Allow the AI agent to use the MCP tool `spans-for-transaction` to retrieve the longest spans for a specific transaction.
206
+
54
207
  ## TODOs
55
208
 
56
209
  ### Features
@@ -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
@@ -13,7 +13,7 @@ module SolidApm
13
13
  transaction.duration = ((finish.to_f - start.to_f) * 1000).round(6)
14
14
  transaction.metadata = {
15
15
  params: payload[:request].params.except(:controller, :action),
16
- context: SpanSubscriber::Base.context
16
+ context: SpanSubscriber::Base.context || {}
17
17
  }
18
18
  SpanSubscriber::Base.context = {}
19
19
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module SolidApm
3
4
  module SpanSubscriber
4
5
  class Base
@@ -4,5 +4,14 @@ module SolidApm
4
4
  has_many :spans, -> { order(:timestamp, :sequence) }, foreign_key: 'transaction_id', dependent: :delete_all
5
5
 
6
6
  attribute :uuid, :string, default: -> { SecureRandom.uuid }
7
+
8
+ def self.metadata_filter
9
+ @metadata_filter ||= ActiveSupport::ParameterFilter
10
+ .new(Rails.application.config.filter_parameters)
11
+ end
12
+
13
+ def metadata=(value = {})
14
+ super(self.class.metadata_filter.filter(value))
15
+ end
7
16
  end
8
17
  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
@@ -10,6 +10,32 @@ module SolidApm
10
10
  app.config.assets.precompile += %w( application.css application.js )
11
11
  end
12
12
 
13
+ begin
14
+ # Mount the MCP server only if the main app added the fast_mcp in is Gemfile.
15
+ require 'fast_mcp'
16
+ initializer "solid_apm.mount_mcp_server" do |app|
17
+ mcp_server_config = SolidApm.mcp_server_config.reverse_merge(
18
+ name: 'solid-apm-mcp',
19
+ version: '1.0.0',
20
+ path: '/solid_apm/mcp'
21
+ )
22
+
23
+ FastMcp.mount_in_rails(
24
+ app,
25
+ **mcp_server_config
26
+ ) do |server|
27
+ app.config.after_initialize do
28
+ require_relative 'mcp/spans_for_transaction_tool'
29
+ require_relative 'mcp/impactful_transactions_resource'
30
+ server.register_resources(SolidApm::Mcp::ImpactfulTransactionsResource)
31
+ server.register_tools(SolidApm::Mcp::SpansForTransactionTool)
32
+ end
33
+ end
34
+ end
35
+ rescue LoadError
36
+ # Ignored
37
+ end
38
+
13
39
  config.after_initialize do
14
40
  SpanSubscriber::Base.subscribe!
15
41
  end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidApm
4
+ module Mcp
5
+ class ImpactfulTransactionsResource < FastMcp::Resource
6
+ uri "solid-apm://impactful-transactions"
7
+ resource_name "impactful_transactions"
8
+ mime_type "application/json"
9
+ description "Returns the most impactful transactions with comprehensive performance metrics"
10
+
11
+ def content
12
+ transactions_with_impact = calculate_impactful_transactions
13
+
14
+ result = {
15
+ metadata: {
16
+ total_transactions_analyzed: SolidApm::Transaction.where(timestamp: 24.hours.ago..).count,
17
+ analysis_period: "last 24 hours",
18
+ ordered_by: "More impactful first: based on P95 latency, transaction frequency, span complexity."
19
+ },
20
+ transactions: transactions_with_impact.map do |transaction_data|
21
+ {
22
+ id: transaction_data[:transaction].id,
23
+ uuid: transaction_data[:transaction].uuid,
24
+ name: transaction_data[:transaction].name,
25
+ type: transaction_data[:transaction].type,
26
+ metrics: {
27
+ p95_latency_ms: transaction_data[:p95_latency],
28
+ avg_duration_ms: transaction_data[:avg_duration],
29
+ max_duration_ms: transaction_data[:max_duration],
30
+ transactions_per_minute: transaction_data[:tpm],
31
+ max_transactions_per_minute: transaction_data[:max_tpm],
32
+ avg_spans_per_transaction: transaction_data[:avg_spans],
33
+ max_spans_per_transaction: transaction_data[:max_spans],
34
+ total_occurrences: transaction_data[:total_count],
35
+ },
36
+ sample_transaction: {
37
+ uuid: transaction_data[:sample_transaction]&.uuid,
38
+ duration_ms: transaction_data[:sample_transaction]&.duration,
39
+ span_count: transaction_data[:sample_span_count],
40
+ timestamp: transaction_data[:sample_transaction]&.timestamp
41
+ }
42
+ }
43
+ end
44
+ }
45
+
46
+ JSON.generate(result)
47
+ rescue StandardError => e
48
+ JSON.generate({ error: e.message, backtrace: e.backtrace.first(5) })
49
+ end
50
+
51
+ private
52
+
53
+ def calculate_impactful_transactions
54
+ # Get transactions from last 24 hours for more relevant data
55
+ cutoff_time = 24.hours.ago
56
+
57
+ # Group transactions by name and type to aggregate metrics
58
+ transaction_groups = SolidApm::Transaction.includes(:spans).where(timestamp: cutoff_time..).group_by { |t| [t.name, t.type] }
59
+
60
+ impact_data = transaction_groups.map do |group_key, transactions| name, type = group_key
61
+ durations = transactions.map(&:duration).compact
62
+ span_counts = transactions.map { |t| t.spans.size }
63
+
64
+ next if durations.empty?
65
+
66
+ # Calculate P95 latency
67
+ p95_latency = calculate_percentile(durations, 95)
68
+ avg_duration = durations.sum / durations.size.to_f
69
+ max_duration = durations.max
70
+
71
+ # Calculate transaction frequency metrics
72
+ total_count = transactions.size
73
+ time_span_hours = [(Time.current - transactions.map(&:timestamp).min) / 1.hour, 1].max
74
+ tpm = (total_count / (time_span_hours * 60)).round(2)
75
+
76
+ # Calculate max TPM by looking at busiest minute
77
+ max_tpm = calculate_max_tpm(transactions)
78
+
79
+ # Span complexity metrics
80
+ avg_spans = span_counts.sum / span_counts.size.to_f
81
+ max_spans = span_counts.max || 0
82
+
83
+ # Get a representative sample transaction
84
+ sample_transaction = transactions.max_by(&:duration)
85
+ sample_span_count = sample_transaction&.spans&.size || 0
86
+
87
+ {
88
+ transaction: transactions.first, # Representative transaction for metadata
89
+ p95_latency: p95_latency.round(2),
90
+ avg_duration: avg_duration.round(2),
91
+ max_duration: max_duration.round(2),
92
+ tpm: tpm,
93
+ max_tpm: max_tpm,
94
+ avg_spans: avg_spans.round(1),
95
+ max_spans: max_spans,
96
+ total_count: total_count,
97
+ sample_transaction: sample_transaction,
98
+ sample_span_count: sample_span_count
99
+ }
100
+ end.compact
101
+
102
+ # Sort by impact score and return top 10
103
+ impact_data.sort_by do |data|
104
+ -calculate_impact_score(p95_latency: data[:p95_latency],
105
+ tpm: data[:tpm],
106
+ avg_spans: data[:avg_spans],
107
+ total_count: data[:total_count])
108
+ end.first(10)
109
+ end
110
+
111
+ def calculate_percentile(array, percentile)
112
+ return 0 if array.empty?
113
+
114
+ sorted = array.sort
115
+ index = (percentile / 100.0) * (sorted.length - 1)
116
+
117
+ if index == index.to_i
118
+ sorted[index.to_i]
119
+ else lower = sorted[index.floor]
120
+ upper = sorted[index.ceil]
121
+ lower + (upper - lower) * (index - index.floor)
122
+ end
123
+ end
124
+
125
+ def calculate_max_tpm(transactions)
126
+ return 0 if transactions.empty?
127
+
128
+ # Group by minute and find the busiest minute
129
+ minute_counts = transactions.group_by { |t| t.timestamp.beginning_of_minute }.values.map(&:size)
130
+ minute_counts.max || 0
131
+ end
132
+
133
+ def calculate_impact_score(p95_latency:, tpm:, avg_spans:, total_count:)
134
+ # Weighted impact score calculation
135
+ # Higher scores indicate more impactful transactions
136
+
137
+ latency_score = Math.log([p95_latency, 1].max) * 20 # Log scale for latency
138
+ frequency_score = Math.log([tpm, 1].max) * 30 # Log scale for frequency
139
+ complexity_score = Math.log([avg_spans, 1].max) * 15 # Span complexity
140
+ volume_score = Math.log([total_count, 1].max) * 10 # Total volume
141
+
142
+ (latency_score + frequency_score + complexity_score + volume_score).round(2)
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidApm
4
+ module Mcp
5
+ class SpansForTransactionTool < FastMcp::Tool
6
+ tool_name "spans-for-transaction"
7
+ description "Returns spans for a specific transaction uuid in the APM system, with backtrace information and metadata"
8
+
9
+ arguments do
10
+ required(:transaction_uuid)
11
+ .filled(:string)
12
+ .description("The UUID of the transaction to retrieve spans for")
13
+ end
14
+
15
+ def call(transaction_uuid:)
16
+ transaction = SolidApm::Transaction.find_by!(uuid: transaction_uuid)
17
+ JSON.generate({
18
+ transaction: transaction,
19
+ spans: transaction.spans
20
+ }.as_json
21
+ )
22
+ rescue StandardError => e
23
+ JSON.generate({
24
+ error: e.message,
25
+ backtrace: e.backtrace.first(5)
26
+ }.as_json)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -23,22 +23,51 @@ module SolidApm
23
23
  def self.call
24
24
  transaction = SpanSubscriber::Base.transaction
25
25
  SpanSubscriber::Base.transaction = nil
26
- if transaction.nil? || transaction.name.start_with?('SolidApm::')
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
30
34
 
31
- ApplicationRecord.transaction do
32
- transaction.save!
35
+ with_silence_logger do
36
+ ApplicationRecord.transaction do
37
+ transaction.save!
33
38
 
34
- SpanSubscriber::Base.spans.each do |span|
35
- span[:transaction_id] = transaction.id
39
+ SpanSubscriber::Base.spans.each do |span|
40
+ span[:transaction_id] = transaction.id
41
+ end
42
+ SolidApm::Span.insert_all SpanSubscriber::Base.spans
36
43
  end
37
- SolidApm::Span.insert_all SpanSubscriber::Base.spans
38
44
  end
39
45
  SpanSubscriber::Base.spans = nil
40
46
  end
41
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
+
61
+ def self.with_silence_logger
62
+ if SolidApm.silence_active_record_logger && ActiveRecord::Base.logger
63
+ ActiveRecord::Base.logger.silence { yield }
64
+ else
65
+ yield
66
+ end
67
+ end
68
+
69
+ # Initialize a new transaction and reset spans
70
+
42
71
  def self.init_transaction
43
72
  now = Time.zone.now
44
73
  SpanSubscriber::Base.transaction = Transaction.new(
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SolidApm
2
- VERSION = "0.8.2"
2
+ VERSION = "0.10.0"
3
3
  end
data/lib/solid_apm.rb CHANGED
@@ -3,11 +3,23 @@ require 'groupdate'
3
3
  require 'active_median'
4
4
  require 'apexcharts'
5
5
 
6
- require "solid_apm/version"
7
- require "solid_apm/engine"
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
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
+ )
11
23
 
12
24
  def self.set_context(context)
13
25
  SpanSubscriber::Base.context = context
@@ -1,4 +1,18 @@
1
- # desc "Explaining what the task does"
2
- # task :solid_apm do
3
- # # Task goes here
4
- # end
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.8.2
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,8 +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
151
+ - lib/solid_apm/mcp/impactful_transactions_resource.rb
152
+ - lib/solid_apm/mcp/spans_for_transaction_tool.rb
149
153
  - lib/solid_apm/middleware.rb
154
+ - lib/solid_apm/sampler.rb
150
155
  - lib/solid_apm/version.rb
151
156
  - lib/tasks/solid_apm_tasks.rake
152
157
  homepage: https://github.com/Bhacaz/solid_apm
@@ -170,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
175
  - !ruby/object:Gem::Version
171
176
  version: '0'
172
177
  requirements: []
173
- rubygems_version: 3.6.7
178
+ rubygems_version: 3.6.9
174
179
  specification_version: 4
175
180
  summary: SolidApm is a DB base engine for Application Performance Monitoring.
176
181
  test_files: []