solid_apm 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +21 -0
- data/README.md +27 -6
- data/app/assets/javascripts/solid_apm/controllers/transaction-chart_controller.js +10 -2
- data/app/controllers/solid_apm/transactions_controller.rb +37 -20
- data/app/models/solid_apm/span_subscriber/action_dispatch.rb +26 -0
- data/app/models/solid_apm/span_subscriber/action_view_render.rb +23 -23
- data/app/models/solid_apm/span_subscriber/base.rb +46 -42
- data/app/views/solid_apm/transactions/index.html.erb +13 -16
- data/app/views/solid_apm/transactions/show.html.erb +1 -0
- data/app/views/solid_apm/transactions/show_by_name.html.erb +27 -0
- data/config/routes.rb +7 -1
- data/lib/solid_apm/engine.rb +0 -18
- data/lib/solid_apm/middleware.rb +28 -11
- data/lib/solid_apm/version.rb +1 -1
- data/lib/solid_apm.rb +4 -0
- metadata +61 -15
- data/app/models/solid_apm/span_subscriber/action_controller.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6233330338e9044097e1315840f1bbc7f2cdeb1677df3dac3261c2a50666046
|
4
|
+
data.tar.gz: 37f306cf60295e74485092216c4bdbe2ba2c824ce08d279424e15c631543b33f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18d46f142b8bc401acc9dcd3417c9782d04a6e2beabda8d3c7dbafd62429f8bcf13e69ecf2e7fe51049f17da8b35411c3b1239c3f9476950590917990d0c7aba
|
7
|
+
data.tar.gz: a0140e3fdb51254f8c615ed7cd4002f851d0c1ef11b71d408c4966581b1638f475ada593977a53761631493201e53641df382b9c8d9c10598935fe7ae8b50da3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Jean-Francis Bastien
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/solid_apm.svg)](https://badge.fury.io/rb/solid_apm)
|
2
|
+
|
1
3
|
# SolidApm
|
2
4
|
Rails engine to manage APM data without using a third party service.
|
3
5
|
|
4
|
-
<img src="
|
5
|
-
<img src="
|
6
|
-
|
6
|
+
<img src="./docs/img.png" width="400px">
|
7
|
+
<img src="./docs/img_1.png" width="400px">
|
7
8
|
|
8
9
|
## Installation
|
9
10
|
|
@@ -21,6 +22,10 @@ Rails.application.routes.draw do
|
|
21
22
|
end
|
22
23
|
```
|
23
24
|
|
25
|
+
Routing constraint can be use to authorize access.
|
26
|
+
See [Routing constraint](https://guides.rubyonrails.org/routing.html#advanced-constraints)
|
27
|
+
for more information.
|
28
|
+
|
24
29
|
Configure the database connection:
|
25
30
|
```ruby
|
26
31
|
# config/initializers/solid_apm.rb
|
@@ -36,22 +41,38 @@ DATABASE=solid_apm bin/rails solid_apm:install:migrations
|
|
36
41
|
|
37
42
|
Go to `http://localhost:3000/solid_apm` and start monitoring your application.
|
38
43
|
|
44
|
+
Add context
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class ApplicationController
|
48
|
+
before_action do
|
49
|
+
SolidApm.set_context(user_id: current_user&.id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
39
54
|
## TODOs
|
40
55
|
|
41
56
|
### Features
|
42
57
|
|
43
|
-
- [ ] Ignore `/solid_apm` requests
|
44
58
|
- [ ] Better handle subscribing to ActiveSupport notifications
|
45
|
-
- [ ]
|
59
|
+
- [ ] Custom events
|
46
60
|
|
47
61
|
### Interface
|
48
62
|
|
49
63
|
- [ ] Paginate transactions list
|
50
64
|
- [ ] Allow date range transactions index
|
51
|
-
- [ ] Display transaction as aggregated data with avg latency, tpm and impact (Relative Avg. duration * transactions per minute)
|
52
65
|
|
53
66
|
## Contributing
|
54
67
|
Contribution directions go here.
|
55
68
|
|
69
|
+
## Release
|
70
|
+
|
71
|
+
```shell
|
72
|
+
gem bump -v minor
|
73
|
+
bundle install && git add . && git commit --amend --no-edit
|
74
|
+
gem tag -p
|
75
|
+
```
|
76
|
+
|
56
77
|
## License
|
57
78
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -13,7 +13,9 @@ import {
|
|
13
13
|
// Connects to data-controller="transaction-chart"
|
14
14
|
window.Stimulus.register('transaction-chart',
|
15
15
|
class extends Controller {
|
16
|
-
|
16
|
+
static values = { name: String }
|
17
|
+
|
18
|
+
connect() {
|
17
19
|
console.log('Connected')
|
18
20
|
var options = {
|
19
21
|
chart: {
|
@@ -34,7 +36,13 @@ window.Stimulus.register('transaction-chart',
|
|
34
36
|
}
|
35
37
|
}
|
36
38
|
}
|
37
|
-
|
39
|
+
|
40
|
+
let path = 'transactions/count_by_minutes'
|
41
|
+
if (this.nameValue) {
|
42
|
+
path = path + "?name=" + encodeURIComponent(this.nameValue);
|
43
|
+
}
|
44
|
+
|
45
|
+
fetch(path)
|
38
46
|
.then(response => response.json())
|
39
47
|
.then(data => {
|
40
48
|
const transformedData = []
|
@@ -2,21 +2,35 @@
|
|
2
2
|
|
3
3
|
module SolidApm
|
4
4
|
class TransactionsController < ApplicationController
|
5
|
+
TransactionAggregation = Struct.new(:name, :tmp, :latency, :impact)
|
6
|
+
|
5
7
|
def index
|
6
|
-
@
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
8
|
+
@aggregated_transactions = Transaction.where(created_at: 1.hour.ago..).group_by(&:name)
|
9
|
+
@aggregated_transactions.transform_values! do |transactions|
|
10
|
+
latency = transactions.map(&:duration).sum / transactions.size
|
11
|
+
tmp = transactions.size.to_f / 60
|
12
|
+
impact = latency * tmp
|
13
|
+
TransactionAggregation.new(
|
14
|
+
transactions.first.name,
|
15
|
+
tmp,
|
16
|
+
latency,
|
17
|
+
impact
|
18
|
+
)
|
19
|
+
end
|
20
|
+
# Find the maximum and minimum impact values
|
21
|
+
max_impact = @aggregated_transactions.values.max_by(&:impact).impact
|
22
|
+
min_impact = @aggregated_transactions.values.min_by(&:impact).impact
|
23
|
+
|
24
|
+
# Normalize impact 0-100
|
25
|
+
@aggregated_transactions.each do |_, aggregation|
|
26
|
+
normalized_impact = ((aggregation.impact - min_impact) / (max_impact - min_impact)) * 100
|
27
|
+
aggregation.impact = normalized_impact.to_i
|
19
28
|
end
|
29
|
+
@aggregated_transactions = @aggregated_transactions.sort_by { |_, v| -v.impact }.to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def show_by_name
|
33
|
+
@transactions = Transaction.where(name: params[:name]).order(timestamp: :desc).limit(20)
|
20
34
|
end
|
21
35
|
|
22
36
|
def show
|
@@ -29,13 +43,16 @@ module SolidApm
|
|
29
43
|
render json: @spans
|
30
44
|
end
|
31
45
|
|
32
|
-
|
33
|
-
|
34
|
-
def transactions_count_by_minutes
|
35
|
-
Transaction.all.order(timestamp: :desc)
|
46
|
+
def count_by_minutes
|
47
|
+
scope = Transaction.all.order(timestamp: :desc)
|
36
48
|
.where(created_at: 1.hour.ago..)
|
37
|
-
|
38
|
-
|
49
|
+
|
50
|
+
if params[:name].present?
|
51
|
+
scope = scope.where(name: params[:name])
|
52
|
+
end
|
53
|
+
|
54
|
+
render json: scope.group_by { |t| t.created_at.beginning_of_minute }
|
55
|
+
.transform_values!(&:count)
|
39
56
|
end
|
40
57
|
end
|
41
|
-
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidApm
|
4
|
+
module SpanSubscriber
|
5
|
+
class ActionDispatch < Base
|
6
|
+
PATTERN = /\w+\.action_dispatch/.freeze
|
7
|
+
def self.subscribe
|
8
|
+
super do |name, start, finish, id, payload|
|
9
|
+
transaction = SpanSubscriber::Base.transaction
|
10
|
+
transaction.name = "#{payload[:request].controller_class}##{payload[:request].path_parameters[:action]}"
|
11
|
+
transaction.end_time = finish
|
12
|
+
transaction.duration = ((transaction.end_time.to_f - transaction.timestamp.to_f) * 1000).round(6)
|
13
|
+
transaction.metadata = {
|
14
|
+
params: payload[:request].params.except(:controller, :action),
|
15
|
+
context: SpanSubscriber::Base.context
|
16
|
+
}
|
17
|
+
SpanSubscriber::Base.context = nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def summary(payload)
|
22
|
+
"#{payload[:request].controller_class}##{payload[:request].path_parameters[:action]}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,37 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SolidApm
|
4
|
-
module SpanSubscriber
|
5
|
-
|
6
|
-
|
4
|
+
module SpanSubscriber
|
5
|
+
class ActionViewRender < Base
|
6
|
+
PATTERN = /^render_.+\.action_view/
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def summary(payload)
|
9
|
+
identifier = payload[:identifier]
|
10
|
+
sanitize_path(identifier)
|
11
|
+
end
|
12
12
|
|
13
|
-
|
13
|
+
private
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def sanitize_path(path)
|
16
|
+
if path.start_with? Rails.root.to_s
|
17
|
+
app_path(path)
|
18
|
+
else
|
19
|
+
gem_path(path)
|
20
|
+
end
|
20
21
|
end
|
21
|
-
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
def app_path(path)
|
24
|
+
return unless path.start_with? Rails.root.to_s
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
format '$APP_PATH%s', path[Rails.root.to_s.length, path.length]
|
27
|
+
end
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
def gem_path(path)
|
30
|
+
root = Gem.path.find { |gp| path.start_with? gp }
|
31
|
+
return unless root
|
32
32
|
|
33
|
-
|
33
|
+
format '$GEM_PATH%s', path[root.length, path.length]
|
34
|
+
end
|
34
35
|
end
|
35
36
|
end
|
36
|
-
end
|
37
37
|
end
|
@@ -1,55 +1,59 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module SolidApm
|
3
|
-
module SpanSubscriber
|
4
|
-
|
5
|
-
|
3
|
+
module SpanSubscriber
|
4
|
+
class Base
|
5
|
+
# PATTERN = /.*/
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
class_attribute :subscribers, default: Set.new
|
8
|
+
thread_cattr_accessor :transaction
|
9
|
+
thread_cattr_accessor :spans
|
10
|
+
thread_cattr_accessor :context
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def self.inherited(subclass)
|
13
|
+
subscribers << subclass
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
def self.subscribe!
|
17
|
+
subscribers.each(&:subscribe)
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
20
|
+
def self.subscribe
|
21
|
+
ActiveSupport::Notifications.subscribe(self::PATTERN) do |name, start, finish, id, payload|
|
22
|
+
next unless SpanSubscriber::Base.transaction
|
23
|
+
|
24
|
+
subtype, type = name.split('.')
|
25
|
+
duration = ((finish.to_f - start.to_f) * 1000).round(6)
|
26
|
+
|
27
|
+
span = {
|
28
|
+
uuid: SecureRandom.uuid,
|
29
|
+
sequence: SpanSubscriber::Base.spans.size + 1,
|
30
|
+
timestamp: start,
|
31
|
+
end_time: finish,
|
32
|
+
duration: duration,
|
33
|
+
name: name,
|
34
|
+
type: type,
|
35
|
+
subtype: subtype,
|
36
|
+
summary: self.new.summary(payload),
|
37
|
+
}
|
38
|
+
|
39
|
+
SpanSubscriber::Base.spans << span
|
40
|
+
|
41
|
+
# Allow the subscriber to yield additional spans, like ending the transaction
|
42
|
+
yield(name, start, finish, id, payload) if block_given?
|
43
|
+
end
|
39
44
|
end
|
40
|
-
end
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
# def summary(payload)
|
47
|
+
# if payload.is_a?(Hash)
|
48
|
+
# payload.first.last.inspect
|
49
|
+
# else
|
50
|
+
# payload.inspect
|
51
|
+
# end
|
52
|
+
# end
|
49
53
|
|
50
|
-
|
54
|
+
# private_class_method :subscribe
|
55
|
+
end
|
51
56
|
end
|
52
57
|
end
|
53
|
-
end
|
54
58
|
|
55
59
|
Dir[File.join(__dir__, '*.rb')].sort.each { |file| require file }
|
@@ -3,27 +3,24 @@
|
|
3
3
|
<h2 class="title is-4 has-text-grey">Last hour</h2>
|
4
4
|
<div data-controller="transaction-chart"></div>
|
5
5
|
|
6
|
-
<table class="table">
|
6
|
+
<table class="table is-fullwidth">
|
7
7
|
<thead>
|
8
8
|
<tr>
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
<th>Name</th>
|
10
|
+
<th>Latency</th>
|
11
|
+
<th>tmp</th>
|
12
|
+
<th>Impact</th>
|
13
13
|
</tr>
|
14
14
|
</thead>
|
15
|
-
|
16
|
-
<% @
|
15
|
+
|
16
|
+
<% @aggregated_transactions.each do |name, aggregation| %>
|
17
17
|
<tr>
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<% end %>
|
25
|
-
<% end %>
|
18
|
+
<td><%= link_to name, transaction_by_name_path(name) %></td>
|
19
|
+
<td><%= aggregation.latency.round(2) %> ms</td>
|
20
|
+
<td><%= aggregation.tmp.round(2) %></td>
|
21
|
+
<td>
|
22
|
+
<progress class="progress is-warning" value="<%= aggregation.impact %>" max="100"></progress>
|
23
|
+
</td>
|
26
24
|
</tr>
|
27
25
|
<% end %>
|
28
|
-
</tbody>
|
29
26
|
</table>
|
@@ -3,6 +3,7 @@
|
|
3
3
|
<h2 class="title is-6"><span class="has-text-grey-dark">Trace ID:</span> <%= @transaction.uuid %></h2>
|
4
4
|
<h2 class="title is-6"><span class="has-text-grey-dark">Timestamp:</span> <%= @transaction.timestamp %></h2>
|
5
5
|
<h2 class="title is-6"><span class="has-text-grey-dark">Duration:</span> <%= @transaction.duration %> ms</h2>
|
6
|
+
<h2 class="title is-6"><span class="has-text-grey-dark">Unix minute:</span> <%= @transaction.unix_minute %></h2>
|
6
7
|
<h2 class="title is-6"><span class="has-text-grey-dark">Metadata:</span> <%= @transaction.metadata %></h2>
|
7
8
|
|
8
9
|
<%= render template: 'solid_apm/spans/index', collection: @transaction.spans, locals: { spans: @transaction.spans } %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h1 class="title is-3"><%= params[:name] %></h1>
|
2
|
+
<div data-controller="transaction-chart" data-transaction-chart-name-value="<%= params[:name] %>"></div>
|
3
|
+
|
4
|
+
<table class="table">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<% SolidApm::Transaction.attribute_names.each do |attribute| %>
|
8
|
+
<% next if attribute.to_s.end_with?('_at') %>
|
9
|
+
<th scope="col"><%= attribute.humanize %></th>
|
10
|
+
<% end %>
|
11
|
+
</tr>
|
12
|
+
</thead>
|
13
|
+
<tbody>
|
14
|
+
<% @transactions.each do |transaction| %>
|
15
|
+
<tr>
|
16
|
+
<% transaction.attributes.each do |attribute| %>
|
17
|
+
<% next if attribute[0].to_s.end_with?('_at') %>
|
18
|
+
<% if attribute[0] == 'uuid' %>
|
19
|
+
<td><%= link_to attribute[1], transaction_path(transaction) %></td>
|
20
|
+
<% else %>
|
21
|
+
<td><%= attribute[1] %></td>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</tr>
|
25
|
+
<% end %>
|
26
|
+
</tbody>
|
27
|
+
</table>
|
data/config/routes.rb
CHANGED
@@ -2,6 +2,12 @@ SolidApm::Engine.routes.draw do
|
|
2
2
|
root 'transactions#index'
|
3
3
|
|
4
4
|
get 'transactions', to: 'transactions#index'
|
5
|
-
get 'transactions
|
5
|
+
get 'transactions/count_by_minutes',
|
6
|
+
to: 'transactions#count_by_minutes',
|
7
|
+
as: 'transactions_count_by_minutes',
|
8
|
+
default: { format: 'json' }
|
9
|
+
|
10
|
+
get 'transactions/:id', to: 'transactions#show', as: 'transaction', constraints: { id: /\d+/ }
|
11
|
+
get 'transactions/:name', to: 'transactions#show_by_name', as: 'transaction_by_name'
|
6
12
|
get 'transactions/:id/spans', to: 'transactions#spans', as: 'transaction_spans'
|
7
13
|
end
|
data/lib/solid_apm/engine.rb
CHANGED
@@ -11,24 +11,6 @@ module SolidApm
|
|
11
11
|
end
|
12
12
|
|
13
13
|
config.after_initialize do
|
14
|
-
ActiveSupport::Notifications.subscribe("start_processing.action_controller") do |name, start, finish, id, payload|
|
15
|
-
SpanSubscriber::Base.transaction = Transaction.new(
|
16
|
-
uuid: SecureRandom.uuid,
|
17
|
-
timestamp: start,
|
18
|
-
type: 'request',
|
19
|
-
name: "#{payload[:controller]}##{payload[:action]}",
|
20
|
-
metadata: { params: payload[:request].params.except(:controller, :action) }
|
21
|
-
)
|
22
|
-
SpanSubscriber::Base.spans = []
|
23
|
-
end
|
24
|
-
|
25
|
-
ActiveSupport::Notifications.subscribe("process_action.action_controller") do |name, start, finish, id, payload|
|
26
|
-
# Set the end time and duration of the transaction with the process_action event
|
27
|
-
transaction = SpanSubscriber::Base.transaction
|
28
|
-
transaction.end_time = finish
|
29
|
-
transaction.duration = ((transaction.end_time.to_f - transaction.timestamp.to_f) * 1000).round(6)
|
30
|
-
end
|
31
|
-
|
32
14
|
SpanSubscriber::Base.subscribe!
|
33
15
|
end
|
34
16
|
end
|
data/lib/solid_apm/middleware.rb
CHANGED
@@ -1,28 +1,33 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SolidApm
|
4
|
-
class Middleware
|
4
|
+
class Middleware
|
5
5
|
def initialize(app)
|
6
6
|
@app = app
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
|
11
|
-
|
12
|
-
self.class.call
|
13
|
-
rescue StandardError => e
|
14
|
-
Rails.logger.error e
|
15
|
-
Rails.logger.error e.backtrace&.join("\n")
|
16
|
-
end
|
10
|
+
self.class.init_transaction
|
11
|
+
status, headers, body = @app.call(env)
|
17
12
|
|
18
|
-
|
13
|
+
env['rack.after_reply'] ||= []
|
14
|
+
env['rack.after_reply'] << ->() do
|
15
|
+
self.class.call
|
16
|
+
rescue StandardError => e
|
17
|
+
Rails.logger.error e
|
18
|
+
Rails.logger.error e.backtrace&.join("\n")
|
19
|
+
end
|
20
|
+
[status, headers, body]
|
19
21
|
end
|
20
22
|
|
21
23
|
def self.call
|
22
24
|
transaction = SpanSubscriber::Base.transaction
|
23
|
-
return unless transaction
|
24
|
-
|
25
25
|
SpanSubscriber::Base.transaction = nil
|
26
|
+
if transaction.nil? || transaction.name.start_with?('SolidApm::')
|
27
|
+
SpanSubscriber::Base.spans = nil
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
26
31
|
ApplicationRecord.transaction do
|
27
32
|
transaction.save!
|
28
33
|
|
@@ -33,5 +38,17 @@ class Middleware
|
|
33
38
|
end
|
34
39
|
SpanSubscriber::Base.spans = nil
|
35
40
|
end
|
41
|
+
|
42
|
+
def self.init_transaction
|
43
|
+
now = Time.zone.now
|
44
|
+
SpanSubscriber::Base.transaction = Transaction.new(
|
45
|
+
uuid: SecureRandom.uuid,
|
46
|
+
timestamp: now,
|
47
|
+
unix_minute: (now.to_f / 60).to_i,
|
48
|
+
type: 'request'
|
49
|
+
)
|
50
|
+
SpanSubscriber::Base.spans = []
|
51
|
+
SpanSubscriber::Base.transaction
|
52
|
+
end
|
36
53
|
end
|
37
54
|
end
|
data/lib/solid_apm/version.rb
CHANGED
data/lib/solid_apm.rb
CHANGED
metadata
CHANGED
@@ -1,36 +1,79 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Francis Bastien
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: actionpack
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.1
|
19
|
+
version: '7.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.1
|
27
|
-
|
26
|
+
version: '7.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: actionview
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '7.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '7.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activerecord
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '7.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '7.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: railties
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '7.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '7.1'
|
69
|
+
description: SolidApm allow you to monitor your application without any external service.
|
28
70
|
email:
|
29
71
|
- bhacaz@gmail.com
|
30
72
|
executables: []
|
31
73
|
extensions: []
|
32
74
|
extra_rdoc_files: []
|
33
75
|
files:
|
76
|
+
- LICENSE
|
34
77
|
- README.md
|
35
78
|
- Rakefile
|
36
79
|
- app/assets/config/solid_apm_manifest.js
|
@@ -44,7 +87,7 @@ files:
|
|
44
87
|
- app/jobs/solid_apm/application_job.rb
|
45
88
|
- app/models/solid_apm/application_record.rb
|
46
89
|
- app/models/solid_apm/span.rb
|
47
|
-
- app/models/solid_apm/span_subscriber/
|
90
|
+
- app/models/solid_apm/span_subscriber/action_dispatch.rb
|
48
91
|
- app/models/solid_apm/span_subscriber/action_view_render.rb
|
49
92
|
- app/models/solid_apm/span_subscriber/active_record_sql.rb
|
50
93
|
- app/models/solid_apm/span_subscriber/active_support_cache.rb
|
@@ -56,6 +99,7 @@ files:
|
|
56
99
|
- app/views/solid_apm/spans/index.html.erb
|
57
100
|
- app/views/solid_apm/transactions/index.html.erb
|
58
101
|
- app/views/solid_apm/transactions/show.html.erb
|
102
|
+
- app/views/solid_apm/transactions/show_by_name.html.erb
|
59
103
|
- config/routes.rb
|
60
104
|
- db/migrate/20240608015633_create_solid_apm_transactions.rb
|
61
105
|
- db/migrate/20240608021940_create_solid_apm_spans.rb
|
@@ -68,7 +112,9 @@ homepage: https://github.com/Bhacaz/solid_apm
|
|
68
112
|
licenses: []
|
69
113
|
metadata:
|
70
114
|
homepage_uri: https://github.com/Bhacaz/solid_apm
|
71
|
-
|
115
|
+
source_code_uri: https://github.com/Bhacaz/solid_apm
|
116
|
+
changelog_uri: https://github.com/Bhacaz/solid_apm/releases
|
117
|
+
post_install_message:
|
72
118
|
rdoc_options: []
|
73
119
|
require_paths:
|
74
120
|
- lib
|
@@ -76,15 +122,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
122
|
requirements:
|
77
123
|
- - ">="
|
78
124
|
- !ruby/object:Gem::Version
|
79
|
-
version: '
|
125
|
+
version: '3.2'
|
80
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
127
|
requirements:
|
82
128
|
- - ">="
|
83
129
|
- !ruby/object:Gem::Version
|
84
130
|
version: '0'
|
85
131
|
requirements: []
|
86
|
-
rubygems_version: 3.5.
|
87
|
-
signing_key:
|
132
|
+
rubygems_version: 3.5.11
|
133
|
+
signing_key:
|
88
134
|
specification_version: 4
|
89
|
-
summary:
|
135
|
+
summary: SolidApm is a DB base engine for Application Performance Monitoring.
|
90
136
|
test_files: []
|