solid_apm 0.7.1 → 0.8.1
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/app/controllers/solid_apm/spans_controller.rb +9 -0
- data/app/helpers/solid_apm/application_helper.rb +19 -0
- data/app/models/solid_apm/span_subscriber/action_view_render.rb +2 -25
- data/app/models/solid_apm/span_subscriber/active_record_sql.rb +1 -1
- data/app/models/solid_apm/span_subscriber/base.rb +25 -0
- data/app/models/solid_apm/span_subscriber/net_http.rb +29 -30
- data/app/views/solid_apm/spans/show.html.erb +46 -0
- data/app/views/solid_apm/transactions/spans.html.erb +51 -13
- data/config/routes.rb +1 -0
- data/lib/solid_apm/version.rb +1 -1
- metadata +5 -4
- data/app/views/solid_apm/spans/index.html.erb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 548f55d6caa0c8616d90f4ad6412cc4cab61f321497748300a66b5fa685ce6c5
|
4
|
+
data.tar.gz: 22c809b06a11521169d94077cc9fbc02e29eb724fc558e9afc399cf767f506e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3bd60eb649a8534929ff31438b97e0685010a7aaae7cbeed837e7ab808993f477290a80bc0b7ee9acb650f8bf6105e7c0c821dd3f14c0c200b9f43d52581d5e
|
7
|
+
data.tar.gz: dc50593ebd0efa6e94384a664e5b80994faaf9d326f7405f3b0d0d7b57e57049f2b644a6b40217c6f58bc1e587d563eda3aa42e5d95dd867efed8c6ea74e66b6
|
@@ -1,5 +1,24 @@
|
|
1
1
|
module SolidApm
|
2
2
|
module ApplicationHelper
|
3
|
+
def span_type_color(span)
|
4
|
+
case span.type
|
5
|
+
when 'action_view'
|
6
|
+
'#00bcd4cc' # cyan
|
7
|
+
when 'action_dispatch'
|
8
|
+
'#4caf50cc' # green
|
9
|
+
when 'active_record'
|
10
|
+
'#ff9800cc' # orange
|
11
|
+
when 'active_support'
|
12
|
+
'#f44336cc' # red
|
13
|
+
when 'net_http'
|
14
|
+
'#9c27b0cc' # purple
|
15
|
+
when 'custom'
|
16
|
+
'#3f51b5cc' # indigo
|
17
|
+
else
|
18
|
+
'#9e9e9ecc' # grey
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
3
22
|
def area_chart_options
|
4
23
|
{
|
5
24
|
module: true,
|
@@ -7,31 +7,8 @@ module SolidApm
|
|
7
7
|
|
8
8
|
def summary(payload)
|
9
9
|
identifier = payload[:identifier]
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
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
|
21
|
-
end
|
22
|
-
|
23
|
-
def app_path(path)
|
24
|
-
return unless path.start_with? Rails.root.to_s
|
25
|
-
|
26
|
-
format '$APP_PATH%s', path[Rails.root.to_s.length, path.length]
|
27
|
-
end
|
28
|
-
|
29
|
-
def gem_path(path)
|
30
|
-
root = Gem.path.find { |gp| path.start_with? gp }
|
31
|
-
return unless root
|
32
|
-
|
33
|
-
format '$GEM_PATH%s', path[root.length, path.length]
|
10
|
+
clean_trace(identifier)
|
34
11
|
end
|
35
12
|
end
|
36
13
|
end
|
37
|
-
end
|
14
|
+
end
|
@@ -34,6 +34,7 @@ module SolidApm
|
|
34
34
|
type: type,
|
35
35
|
subtype: subtype,
|
36
36
|
summary: self.new.summary(payload),
|
37
|
+
stacktrace: clean_trace(caller_locations)
|
37
38
|
}
|
38
39
|
|
39
40
|
SpanSubscriber::Base.spans << span
|
@@ -43,6 +44,30 @@ module SolidApm
|
|
43
44
|
end
|
44
45
|
end
|
45
46
|
|
47
|
+
def self.backtrace_cleaner
|
48
|
+
@backtrace_cleaner ||= begin
|
49
|
+
bc = ActiveSupport::BacktraceCleaner.new
|
50
|
+
bc.remove_filters!
|
51
|
+
gem_roots = Gem.path
|
52
|
+
[Rails.root, *gem_roots].each do |path|
|
53
|
+
bc.add_filter { |line| line.delete_prefix("#{path}/") }
|
54
|
+
end
|
55
|
+
bc
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.clean_trace(backtrace)
|
60
|
+
if backtrace.is_a?(Array)
|
61
|
+
backtrace_cleaner.clean(backtrace)
|
62
|
+
else
|
63
|
+
backtrace_cleaner.clean([backtrace]).first
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def clean_trace(backtrace)
|
68
|
+
self.class.clean_trace(backtrace)
|
69
|
+
end
|
70
|
+
|
46
71
|
# def summary(payload)
|
47
72
|
# if payload.is_a?(Hash)
|
48
73
|
# payload.first.last.inspect
|
@@ -1,45 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'net/http'
|
4
3
|
module SolidApm
|
5
|
-
module SpanSubscriber
|
6
|
-
|
7
|
-
|
4
|
+
module SpanSubscriber
|
5
|
+
class NetHttp < Base
|
6
|
+
PATTERN = 'request.net_http'
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.subscribe
|
14
|
-
if defined?(::Net::HTTP)
|
15
|
-
::Net::HTTP.prepend(NetHttpInstrumentationPrepend)
|
16
|
-
super
|
8
|
+
def summary(payload)
|
9
|
+
payload
|
17
10
|
end
|
18
|
-
end
|
19
11
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
super(request, *args, &block)
|
12
|
+
def self.subscribe
|
13
|
+
if defined?(::Net::HTTP)
|
14
|
+
::Net::HTTP.prepend(NetHttpInstrumentationPrepend)
|
15
|
+
super
|
25
16
|
end
|
26
17
|
end
|
27
18
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
return "No Address Found"
|
19
|
+
# https://github.com/scoutapp/scout_apm_ruby/blob/3838109214503755c5cbd4caf78f6446adbe222f/lib/scout_apm/instruments/net_http.rb#L61
|
20
|
+
module NetHttpInstrumentationPrepend
|
21
|
+
def request(request, *args, &block)
|
22
|
+
ActiveSupport::Notifications.instrument PATTERN, request_solid_apm_description(request) do
|
23
|
+
super(request, *args, &block)
|
24
|
+
end
|
35
25
|
end
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
27
|
+
def request_solid_apm_description(req)
|
28
|
+
path = req.path
|
29
|
+
path = path.path if path.respond_to?(:path)
|
30
|
+
|
31
|
+
# Protect against a nil address value
|
32
|
+
if @address.nil?
|
33
|
+
return "No Address Found"
|
34
|
+
end
|
35
|
+
|
36
|
+
max_length = 500
|
37
|
+
req.method.upcase + " " + (@address + path.split('?').first)[0..(max_length - 1)]
|
38
|
+
rescue
|
39
|
+
""
|
40
|
+
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
-
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<h1 class="title">Span Details</h1>
|
2
|
+
|
3
|
+
<h2 class="title is-6"><span class="has-text-grey">UUID:</span> <%= @span.uuid %></h2>
|
4
|
+
<h2 class="title is-6"><span class="has-text-grey">Timestamp:</span> <%= @span.timestamp %></h2>
|
5
|
+
<h2 class="title is-6"><span class="has-text-grey">Name:</span> <%= @span.name %></h2>
|
6
|
+
<h2 class="title is-6"><span class="has-text-grey">Type:</span> <%= @span.type %></h2>
|
7
|
+
<h2 class="title is-6"><span class="has-text-grey">Subtype:</span> <%= @span.subtype %></h2>
|
8
|
+
<h2 class="title is-6"><span class="has-text-grey">Summary:</span> <%= @span.summary %></h2>
|
9
|
+
<h2 class="title is-6"><span class="has-text-grey">Duration:</span> <%= @span.duration.round(2) %>ms</h2>
|
10
|
+
|
11
|
+
<h2 class="title is-4 pt-4">Stacktrace</h2>
|
12
|
+
<% if @span.stacktrace.present? %>
|
13
|
+
<button id="toggle-stacktrace" data-state="app-only">Show All</button>
|
14
|
+
<pre id="stacktrace" style="display: grid">
|
15
|
+
<% @span.stacktrace.each do |line| %>
|
16
|
+
<% source = line.start_with?('gems') ? 'other' : 'app' %>
|
17
|
+
<span class="stack-line" data-source="<%= source %>" style="<%= 'display:none;' if source == 'other' %>"><%= line %></span>
|
18
|
+
<% end %>
|
19
|
+
</pre>
|
20
|
+
<script>
|
21
|
+
document.addEventListener('DOMContentLoaded', function() {
|
22
|
+
var btn = document.getElementById('toggle-stacktrace');
|
23
|
+
btn.addEventListener('click', function() {
|
24
|
+
var state = btn.getAttribute('data-state');
|
25
|
+
var lines = document.querySelectorAll('#stacktrace .stack-line');
|
26
|
+
if (state === 'app-only') {
|
27
|
+
lines.forEach(function(line) {
|
28
|
+
line.style.display = 'inline-block';
|
29
|
+
});
|
30
|
+
btn.textContent = 'Show App Only';
|
31
|
+
btn.setAttribute('data-state', 'all');
|
32
|
+
} else {
|
33
|
+
lines.forEach(function(line) {
|
34
|
+
if (line.dataset.source === 'other') {
|
35
|
+
line.style.display = 'none';
|
36
|
+
}
|
37
|
+
});
|
38
|
+
btn.textContent = 'Show All';
|
39
|
+
btn.setAttribute('data-state', 'app-only');
|
40
|
+
}
|
41
|
+
});
|
42
|
+
});
|
43
|
+
</script>
|
44
|
+
<% else %>
|
45
|
+
<p>No stacktrace available.</p>
|
46
|
+
<% end %>
|
@@ -10,25 +10,63 @@
|
|
10
10
|
<% max_end_time = @transaction.spans.maximum(:end_time) %>
|
11
11
|
<% total_duration = max_end_time - min_start_time %>
|
12
12
|
|
13
|
+
<style>
|
14
|
+
.span-details {
|
15
|
+
position: absolute;
|
16
|
+
right: 0;
|
17
|
+
display: flex;
|
18
|
+
flex-direction: column;
|
19
|
+
justify-content: center;
|
20
|
+
padding-left: .5rem;
|
21
|
+
height: 100%;
|
22
|
+
max-width: 25em;
|
23
|
+
}
|
24
|
+
|
25
|
+
.span-bar {
|
26
|
+
position: relative;
|
27
|
+
border-radius: 5px;
|
28
|
+
}
|
29
|
+
|
30
|
+
.span-details .truncate {
|
31
|
+
text-overflow: ellipsis;
|
32
|
+
white-space: nowrap;
|
33
|
+
overflow: hidden;
|
34
|
+
}
|
35
|
+
|
36
|
+
.span-details .summary {
|
37
|
+
white-space: nowrap;
|
38
|
+
margin-top: .25rem;
|
39
|
+
}
|
40
|
+
|
41
|
+
.span-row {
|
42
|
+
border-bottom: 1px solid #333333;
|
43
|
+
padding: 0 1rem;
|
44
|
+
}
|
45
|
+
|
46
|
+
.span-row:last-child {
|
47
|
+
border-bottom: none;
|
48
|
+
}
|
49
|
+
</style>
|
50
|
+
|
13
51
|
<div class="is-fullwidth">
|
14
52
|
<% @transaction.spans.each do |span| %>
|
15
53
|
<% left_percent = ((span.timestamp - min_start_time).to_f / total_duration * 100) %>
|
16
54
|
<% width_percent = [((span.end_time - span.timestamp).to_f / total_duration * 100), 0.1].max %>
|
17
55
|
<% right_percent = (100 - left_percent - width_percent) %>
|
18
56
|
|
19
|
-
<div style="
|
20
|
-
<div style="
|
21
|
-
|
57
|
+
<div class="span-row" style="width: 100%; height: 6.5em;">
|
58
|
+
<div style="left: 0; width: 100%">
|
59
|
+
<div class="py-3" style="display: block; position: relative;">
|
60
|
+
<div class="span-bar" style="left: <%= left_percent %>%; width: <%= width_percent %>%; min-width: 0.5em; height: 24px; border-radius: 5px; background-color: <%= span_type_color(span) %>"></div>
|
61
|
+
<div class="span-details mt-1 mb-2" style="min-width: <%= width_percent + right_percent %>%;">
|
62
|
+
<div class="is-flex is-align-items-center">
|
63
|
+
<span class="has-text-grey-lighter truncate"><%= link_to span.name, span_path(span.uuid) %></span>
|
64
|
+
<span class="has-text-grey ml-2" style="white-space: nowrap;"><%= span.duration.round(2) %> ms</span>
|
65
|
+
</div>
|
66
|
+
<div class="summary has-text-grey"><%= span.summary %></div>
|
67
|
+
</div>
|
68
|
+
</div>
|
22
69
|
</div>
|
23
|
-
<div style="flex: <%= right_percent %>;"></div>
|
24
|
-
</div>
|
25
|
-
<div style="margin-left: <%= left_percent %>%" class="mt-1 mb-2">
|
26
|
-
<p>
|
27
|
-
<span class="has-text-grey-lighter "><%= span.name %></span><span class="has-text-grey pl-2"><%= span.duration.round(2) %>ms</span>
|
28
|
-
</p>
|
29
|
-
<p>
|
30
|
-
<%= span.summary %>
|
31
|
-
</p>
|
32
70
|
</div>
|
33
71
|
<% end %>
|
34
|
-
|
72
|
+
</div>
|
data/config/routes.rb
CHANGED
data/lib/solid_apm/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Francis Bastien
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: actionpack
|
@@ -121,6 +121,7 @@ files:
|
|
121
121
|
- app/assets/javascripts/solid_apm/application.js
|
122
122
|
- app/assets/stylesheets/solid_apm/application.css
|
123
123
|
- app/controllers/solid_apm/application_controller.rb
|
124
|
+
- app/controllers/solid_apm/spans_controller.rb
|
124
125
|
- app/controllers/solid_apm/transactions_controller.rb
|
125
126
|
- app/helpers/solid_apm/application_helper.rb
|
126
127
|
- app/jobs/solid_apm/application_job.rb
|
@@ -136,7 +137,7 @@ files:
|
|
136
137
|
- app/views/javascripts/_javascripts.html.erb
|
137
138
|
- app/views/layouts/solid_apm/application.html.erb
|
138
139
|
- app/views/solid_apm/application/_time_range_form.html.erb
|
139
|
-
- app/views/solid_apm/spans/
|
140
|
+
- app/views/solid_apm/spans/show.html.erb
|
140
141
|
- app/views/solid_apm/transactions/_charts.html.erb
|
141
142
|
- app/views/solid_apm/transactions/index.html.erb
|
142
143
|
- app/views/solid_apm/transactions/spans.html.erb
|
@@ -169,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
170
|
- !ruby/object:Gem::Version
|
170
171
|
version: '0'
|
171
172
|
requirements: []
|
172
|
-
rubygems_version: 3.6.
|
173
|
+
rubygems_version: 3.6.7
|
173
174
|
specification_version: 4
|
174
175
|
summary: SolidApm is a DB base engine for Application Performance Monitoring.
|
175
176
|
test_files: []
|
@@ -1,22 +0,0 @@
|
|
1
|
-
<div data-controller="spans-chart" data-spans-chart-id-value="<%= params[:id] %>"></div>
|
2
|
-
|
3
|
-
<table class="table">
|
4
|
-
<thead>
|
5
|
-
<tr>
|
6
|
-
<% SolidApm::Span.attribute_names.each do |attribute| %>
|
7
|
-
<% next if attribute.to_s.end_with?('_at') %>
|
8
|
-
<th scope="col"><%= attribute.humanize %></th>
|
9
|
-
<% end %>
|
10
|
-
</tr>
|
11
|
-
</thead>
|
12
|
-
<tbody>
|
13
|
-
<% spans.each do |span| %>
|
14
|
-
<tr>
|
15
|
-
<% span.attributes.each do |attribute| %>
|
16
|
-
<% next if attribute[0].to_s.end_with?('_at') %>
|
17
|
-
<td><%= attribute[1] %></td>
|
18
|
-
<% end %>
|
19
|
-
</tr>
|
20
|
-
<% end %>
|
21
|
-
</tbody>
|
22
|
-
</table>
|