solid_apm 0.1.0 → 0.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 +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
|
+
[](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: []
|