solid_apm 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/app/assets/javascripts/solid_apm/application.js +0 -1
- data/app/controllers/solid_apm/transactions_controller.rb +42 -31
- data/app/helpers/solid_apm/application_helper.rb +29 -0
- data/app/models/solid_apm/span_subscriber/action_dispatch.rb +3 -2
- data/app/models/solid_apm/span_subscriber/active_record_sql.rb +6 -6
- data/app/views/layouts/solid_apm/application.html.erb +9 -4
- data/app/views/solid_apm/application/_time_range_form.html.erb +34 -0
- data/app/views/solid_apm/transactions/_charts.html.erb +10 -0
- data/app/views/solid_apm/transactions/index.html.erb +42 -23
- data/app/views/solid_apm/transactions/spans.html.erb +34 -0
- data/config/routes.rb +1 -6
- data/lib/solid_apm/middleware.rb +0 -1
- data/lib/solid_apm/version.rb +1 -1
- data/lib/solid_apm.rb +5 -0
- metadata +56 -18
- data/app/assets/javascripts/solid_apm/controllers/spans-chart_controller.js +0 -97
- data/app/assets/javascripts/solid_apm/controllers/transaction-chart_controller.js +0 -76
- data/app/views/solid_apm/transactions/show.html.erb +0 -9
- data/app/views/solid_apm/transactions/show_by_name.html.erb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2980cc185b4e779e37f5a2b1ef323f3bddc9453076725d992a3d7641e54ae370
|
4
|
+
data.tar.gz: 9ef8518d90d7351f17001d428c79dba5c7ebecd890c642f47ae2fd1e744f5591
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9af1a90d9603e05b5436bdcb420c31dfe7156d4768f261772a6e602f160385a6c7af60e8b08bb2050df4151a27703858c184bf271bfeac69d05de5b6efa344f
|
7
|
+
data.tar.gz: 48fb657be11a65ae888235a88ac8794a1ecdf8ea4863d5c7cdb1c3e292cabb04917b1b109d1c157099550a53931d1272fa0362ffbddb433fb8e4f839ce8cf93c
|
data/Rakefile
CHANGED
@@ -3,22 +3,41 @@
|
|
3
3
|
module SolidApm
|
4
4
|
class TransactionsController < ApplicationController
|
5
5
|
TransactionAggregation = Struct.new(:name, :tmp, :latency, :percentile_95, :impact)
|
6
|
+
private_constant :TransactionAggregation
|
6
7
|
|
7
8
|
def index
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if from_to_range.end < from_to_range.begin
|
10
|
+
flash[:error] = 'Invalid time range'
|
11
|
+
redirect_to transactions_path
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
@transactions_scope = Transaction.where(timestamp: from_to_range)
|
16
|
+
if params[:name].present?
|
17
|
+
@transactions_scope = @transactions_scope.where(name: params[:name])
|
18
|
+
end
|
19
|
+
transaction_names = @transactions_scope.distinct.pluck(:name)
|
20
|
+
latency_95p = @transactions_scope.group(:name).percentile(:duration, 0.95)
|
21
|
+
latency_median = @transactions_scope.group(:name).median(:duration)
|
22
|
+
tmp_dict = @transactions_scope.group(:name).group_by_minute(:timestamp, series: false).count.each_with_object({}) do |(k, v), h|
|
23
|
+
current_value = h[k.first] ||= 0
|
24
|
+
h[k.first] = v if v > current_value
|
25
|
+
end
|
26
|
+
|
27
|
+
@aggregated_transactions = transaction_names.each_with_object({}) do |transaction_name, h|
|
28
|
+
latency = latency_median[transaction_name]
|
29
|
+
tmp = tmp_dict[transaction_name]
|
12
30
|
impact = latency * tmp
|
13
|
-
|
14
|
-
|
15
|
-
transactions.first.name,
|
31
|
+
h[transaction_name] = TransactionAggregation.new(
|
32
|
+
transaction_name,
|
16
33
|
tmp,
|
17
34
|
latency,
|
18
|
-
|
35
|
+
latency_95p[transaction_name],
|
19
36
|
impact
|
20
37
|
)
|
21
38
|
end
|
39
|
+
|
40
|
+
return if @aggregated_transactions.empty?
|
22
41
|
# Find the maximum and minimum impact values
|
23
42
|
max_impact = @aggregated_transactions.values.max_by(&:impact).impact
|
24
43
|
min_impact = @aggregated_transactions.values.min_by(&:impact).impact
|
@@ -30,32 +49,15 @@ module SolidApm
|
|
30
49
|
aggregation.impact = normalized_impact.to_i || 0
|
31
50
|
end
|
32
51
|
@aggregated_transactions = @aggregated_transactions.sort_by { |_, v| -v.impact }.to_h
|
33
|
-
end
|
34
52
|
|
35
|
-
|
36
|
-
@
|
37
|
-
|
38
|
-
|
39
|
-
def show
|
40
|
-
@transaction = Transaction.find(params[:id])
|
53
|
+
scope = @transactions_scope.group_by_second(:timestamp, n: n_intervals_seconds(from_to_range))
|
54
|
+
@throughput_data = scope.count
|
55
|
+
@latency_data = scope.median(:duration).transform_values(&:to_i)
|
41
56
|
end
|
42
57
|
|
43
58
|
def spans
|
44
|
-
@transaction = Transaction.
|
45
|
-
@
|
46
|
-
render json: @spans
|
47
|
-
end
|
48
|
-
|
49
|
-
def count_time_aggregations
|
50
|
-
scope = Transaction.all.order(timestamp: :desc)
|
51
|
-
.where(created_at: from_to_range)
|
52
|
-
|
53
|
-
|
54
|
-
if params[:name].present?
|
55
|
-
scope = scope.where(name: params[:name])
|
56
|
-
end
|
57
|
-
|
58
|
-
render json: aggregate(scope.select(:id, :created_at).find_each, from_to_range, intervals_count: 20).transform_values!(&:count)
|
59
|
+
@transaction = Transaction.find_by!(uuid: params[:uuid])
|
60
|
+
@transaction.spans.to_a
|
59
61
|
end
|
60
62
|
|
61
63
|
private
|
@@ -64,10 +66,19 @@ module SolidApm
|
|
64
66
|
params[:from_value] ||= 60
|
65
67
|
params[:from_unit] ||= 'minutes'
|
66
68
|
from = params[:from_value].to_i.public_send(params[:from_unit].to_sym).ago
|
67
|
-
|
69
|
+
params[:to_value] ||= 1
|
70
|
+
params[:to_unit] ||= 'seconds'
|
71
|
+
to = params[:to_value].to_i.public_send(params[:to_unit].to_sym).ago
|
68
72
|
(from..to)
|
69
73
|
end
|
70
74
|
|
75
|
+
def n_intervals_seconds(range, intervals_count: 30)
|
76
|
+
start_time = range.begin
|
77
|
+
end_time = range.end
|
78
|
+
time_range_in_seconds = (end_time - start_time).to_i
|
79
|
+
(time_range_in_seconds / intervals_count.to_f).round
|
80
|
+
end
|
81
|
+
|
71
82
|
def aggregate(items, range, intervals_count: 20)
|
72
83
|
start_time = range.begin
|
73
84
|
end_time = range.end
|
@@ -1,4 +1,33 @@
|
|
1
1
|
module SolidApm
|
2
2
|
module ApplicationHelper
|
3
|
+
def area_chart_options
|
4
|
+
{
|
5
|
+
module: true,
|
6
|
+
chart: {
|
7
|
+
type: 'area', height: '200', background: '0', foreColor: '#ffffff55', zoom: {
|
8
|
+
enabled: false,
|
9
|
+
}, toolbar: {
|
10
|
+
show: false,
|
11
|
+
}
|
12
|
+
},
|
13
|
+
xaxis: {
|
14
|
+
type: 'datetime',
|
15
|
+
tooltip: {
|
16
|
+
enabled: false
|
17
|
+
|
18
|
+
}
|
19
|
+
},
|
20
|
+
stroke: {
|
21
|
+
curve: 'smooth'
|
22
|
+
}, theme: {
|
23
|
+
mode: 'dark',
|
24
|
+
}, grid: {
|
25
|
+
show: true, borderColor: '#ffffff55',
|
26
|
+
}, dataLabels: {
|
27
|
+
enabled: false
|
28
|
+
},
|
29
|
+
tooltip: {x: {formatter: {function: {args: "val", body: "return new Date(val).toLocaleString()"}} }}
|
30
|
+
}
|
31
|
+
end
|
3
32
|
end
|
4
33
|
end
|
@@ -8,13 +8,14 @@ module SolidApm
|
|
8
8
|
super do |name, start, finish, id, payload|
|
9
9
|
transaction = SpanSubscriber::Base.transaction
|
10
10
|
transaction.name = "#{payload[:request].controller_class}##{payload[:request].path_parameters[:action]}"
|
11
|
+
transaction.timestamp = start
|
11
12
|
transaction.end_time = finish
|
12
|
-
transaction.duration = ((
|
13
|
+
transaction.duration = ((finish.to_f - start.to_f) * 1000).round(6)
|
13
14
|
transaction.metadata = {
|
14
15
|
params: payload[:request].params.except(:controller, :action),
|
15
16
|
context: SpanSubscriber::Base.context
|
16
17
|
}
|
17
|
-
SpanSubscriber::Base.context =
|
18
|
+
SpanSubscriber::Base.context = {}
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module SolidApm
|
3
|
-
module SpanSubscriber
|
4
|
-
|
5
|
-
|
3
|
+
module SpanSubscriber
|
4
|
+
class ActiveRecordSql < Base
|
5
|
+
PATTERN = "sql.active_record"
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
def summary(payload)
|
8
|
+
payload[:sql]
|
9
|
+
end
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
12
|
-
end
|
@@ -5,18 +5,23 @@
|
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
|
8
|
-
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
8
|
+
<script src="https://cdn.jsdelivr.net/npm/apexcharts@4.4.0"></script>
|
9
9
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.1/css/bulma.min.css">
|
10
10
|
<%= stylesheet_link_tag "solid_apm/application", media: "all" %>
|
11
11
|
<%= javascript_include_tag "solid_apm/application", "data-turoblinks-track": "reload", type: "module" %>
|
12
|
-
<%= javascript_include_tag "solid_apm/controllers/transaction-chart_controller", "data-turoblinks-track": "reload", type: "module" %>
|
13
|
-
<%= javascript_include_tag "solid_apm/controllers/spans-chart_controller", "data-turoblinks-track": "reload", type: "module" %>
|
14
12
|
</head>
|
15
13
|
|
16
|
-
<body
|
14
|
+
<body>
|
17
15
|
<section class="section">
|
18
16
|
<%= yield %>
|
19
17
|
</section>
|
18
|
+
|
19
|
+
<% if flash[:error] %>
|
20
|
+
<div class="notification is-danger mb-1" style="width: 20em; position: absolute; bottom: 1em; right: 1em;">
|
21
|
+
<button class="delete"></button>
|
22
|
+
<%= flash[:error] %>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
20
25
|
</body>
|
21
26
|
|
22
27
|
</html>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<%= form_with path: transactions_path, method: :get do |f| %>
|
2
|
+
<div class="is-flex is-flex-direction-row is-justify-content-center is-align-items-center" style="gap: 1em">
|
3
|
+
<%= f.number_field :from_value, value: params[:from_value] || 60, min: 1, class: 'input', style: 'width: 6em' %>
|
4
|
+
<div class="select">
|
5
|
+
<%= f.select :from_unit, {
|
6
|
+
"minutes" => "minutes",
|
7
|
+
"hours" => "hours",
|
8
|
+
"days" => "days",
|
9
|
+
"weeks" => "weeks",
|
10
|
+
"months" => "months",
|
11
|
+
"years" => "years"
|
12
|
+
}, {selected: params[:from_unit] || 'minutes' } %>
|
13
|
+
</div>
|
14
|
+
<b>→</b>
|
15
|
+
<%= f.number_field :to_value, value: params[:to_value] || 1, min: 1, class: 'input', style: 'width: 6em' %>
|
16
|
+
<div class="select">
|
17
|
+
<%= f.select :to_unit, {
|
18
|
+
"seconds" => "seconds",
|
19
|
+
"minutes" => "minutes",
|
20
|
+
"hours" => "hours",
|
21
|
+
"days" => "days",
|
22
|
+
"weeks" => "weeks",
|
23
|
+
"months" => "months",
|
24
|
+
"years" => "years"
|
25
|
+
}, {selected: params[:to_unit] || 'seconds' } %>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<% if params[:name] %>
|
29
|
+
<%= f.hidden_field :name, value: params[:name] %>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<%= f.submit 'Apply', class: 'button' %>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="columns pt-2" style="height: 16em">
|
2
|
+
<div class="column">
|
3
|
+
<h2 class="ml-4">Throughput</h2>
|
4
|
+
<%= area_chart({ name: 'tmp', data: @throughput_data }, area_chart_options.merge(colors: ['#43BCCD'])) %>
|
5
|
+
</div>
|
6
|
+
<div class="column">
|
7
|
+
<h2 class="ml-4">Latency</h2>
|
8
|
+
<%= area_chart({ name: 'ms', data: @latency_data }, area_chart_options.merge(colors: ['#13d8aa'])) %>
|
9
|
+
</div>
|
10
|
+
</div>
|
@@ -1,26 +1,15 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
"minutes" => "minutes",
|
10
|
-
"hours" => "hours",
|
11
|
-
"days" => "days",
|
12
|
-
"weeks" => "weeks",
|
13
|
-
"months" => "months",
|
14
|
-
"years" => "years"
|
15
|
-
}, {selected: params[:from_unit] || 'minutes' } %>
|
16
|
-
</div>
|
17
|
-
<span class="has-text-white-ter">ago</span>
|
18
|
-
<b>→</b>
|
19
|
-
<span class="has-text-white-ter">now</span>
|
20
|
-
<%= f.submit 'Apply', class: 'button' %>
|
21
|
-
</div>
|
1
|
+
<% if params[:name] %>
|
2
|
+
<h1 class="title"><%= link_to '⬅ ', transactions_path(
|
3
|
+
from_value: params[:from_value],
|
4
|
+
from_unit: params[:from_unit],
|
5
|
+
to_value: params[:to_value],
|
6
|
+
to_unit: params[:to_unit]) %><%= params[:name] %></h1>
|
7
|
+
<% else %>
|
8
|
+
<h1 class="title">Solid APM</h1>
|
22
9
|
<% end %>
|
23
|
-
|
10
|
+
|
11
|
+
<%= render 'time_range_form' %>
|
12
|
+
<%= render 'charts' %>
|
24
13
|
|
25
14
|
<table class="table is-fullwidth">
|
26
15
|
<thead>
|
@@ -35,7 +24,12 @@
|
|
35
24
|
|
36
25
|
<% @aggregated_transactions.each do |name, aggregation| %>
|
37
26
|
<tr>
|
38
|
-
<td><%= link_to name,
|
27
|
+
<td><%= link_to name, transactions_path(
|
28
|
+
from_value: params[:from_value],
|
29
|
+
from_unit: params[:from_unit],
|
30
|
+
to_value: params[:to_value],
|
31
|
+
to_unit: params[:to_unit],
|
32
|
+
name: name) %>
|
39
33
|
<td><%= aggregation.latency.round(2) %> ms</td>
|
40
34
|
<td><%= aggregation.tmp.round(2) %></td>
|
41
35
|
<td><%= aggregation.percentile_95.round(2) %> ms</td>
|
@@ -45,3 +39,28 @@
|
|
45
39
|
</tr>
|
46
40
|
<% end %>
|
47
41
|
</table>
|
42
|
+
|
43
|
+
<% if params[:name] %>
|
44
|
+
<table class="table is-fullwidth is-hoverable">
|
45
|
+
<thead>
|
46
|
+
<tr>
|
47
|
+
<th scope="col">Name</th>
|
48
|
+
<th scope="col">Timestamp</th>
|
49
|
+
<th scope="col">Type</th>
|
50
|
+
<th scope="col">Duration</th>
|
51
|
+
<th scope="col">Metadata</th>
|
52
|
+
</tr>
|
53
|
+
</thead>
|
54
|
+
<tbody>
|
55
|
+
<% @transactions_scope.order(timestamp: :desc).each do |transaction| %>
|
56
|
+
<tr>
|
57
|
+
<td><%= link_to transaction.name, transaction_spans_path(uuid: transaction.uuid) %></td>
|
58
|
+
<td><%= transaction.timestamp %></td>
|
59
|
+
<td><%= transaction.type %></td>
|
60
|
+
<td><%= transaction.duration.round(2) %></td>
|
61
|
+
<td><%= transaction.metadata %></td>
|
62
|
+
</tr>
|
63
|
+
<% end %>
|
64
|
+
</tbody>
|
65
|
+
</table>
|
66
|
+
<% end %>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<h1 class="title"><%= link_to '⬅ ', request.referer %><%= @transaction.name %></h1>
|
2
|
+
<h2 class="title is-6"><span class="has-text-grey">Trace ID:</span> <%= @transaction.uuid %></h2>
|
3
|
+
<h2 class="title is-6"><span class="has-text-grey">Timestamp:</span> <%= @transaction.timestamp %></h2>
|
4
|
+
<h2 class="title is-6"><span class="has-text-grey">Duration:</span> <%= @transaction.duration.round(2) %> ms</h2>
|
5
|
+
<h2 class="title is-6"><span class="has-text-grey">Metadata:</span> <%= @transaction.metadata %></h2>
|
6
|
+
|
7
|
+
<h2 class="title is-4 mt-6">Spans</h2>
|
8
|
+
|
9
|
+
<% min_start_time = @transaction.spans.minimum(:timestamp) %>
|
10
|
+
<% max_end_time = @transaction.spans.maximum(:end_time) %>
|
11
|
+
<% total_duration = max_end_time - min_start_time %>
|
12
|
+
|
13
|
+
<div class="is-fullwidth">
|
14
|
+
<% @transaction.spans.each do |span| %>
|
15
|
+
<% left_percent = ((span.timestamp - min_start_time).to_f / total_duration * 100) %>
|
16
|
+
<% width_percent = [((span.end_time - span.timestamp).to_f / total_duration * 100), 0.1].max %>
|
17
|
+
<% right_percent = (100 - left_percent - width_percent) %>
|
18
|
+
|
19
|
+
<div style="display: flex; height: 1.5em;">
|
20
|
+
<div style="flex: <%= left_percent %>"></div>
|
21
|
+
<div style="flex: <%= width_percent %>; display: flex; align-items: center; justify-content: center; border-radius: 5px" class="has-background-primary-15">
|
22
|
+
</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
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
data/config/routes.rb
CHANGED
@@ -2,10 +2,5 @@ SolidApm::Engine.routes.draw do
|
|
2
2
|
root 'transactions#index'
|
3
3
|
|
4
4
|
get 'transactions', to: 'transactions#index'
|
5
|
-
get 'transactions/
|
6
|
-
default: { format: 'json' }
|
7
|
-
|
8
|
-
get 'transactions/:id', to: 'transactions#show', as: 'transaction', constraints: { id: /\d+/ }
|
9
|
-
get 'transactions/:name', to: 'transactions#show_by_name', as: 'transaction_by_name'
|
10
|
-
get 'transactions/:id/spans', to: 'transactions#spans', as: 'transaction_spans'
|
5
|
+
get 'transactions/:uuid/spans', to: 'transactions#spans', as: 'transaction_spans'
|
11
6
|
end
|
data/lib/solid_apm/middleware.rb
CHANGED
data/lib/solid_apm/version.rb
CHANGED
data/lib/solid_apm.rb
CHANGED
metadata
CHANGED
@@ -1,71 +1,112 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solid_apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean-Francis Bastien
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-12 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: actionpack
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
|
-
- - "
|
16
|
+
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
18
|
version: '7.1'
|
20
19
|
type: :runtime
|
21
20
|
prerelease: false
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
23
22
|
requirements:
|
24
|
-
- - "
|
23
|
+
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: '7.1'
|
27
26
|
- !ruby/object:Gem::Dependency
|
28
27
|
name: actionview
|
29
28
|
requirement: !ruby/object:Gem::Requirement
|
30
29
|
requirements:
|
31
|
-
- - "
|
30
|
+
- - ">="
|
32
31
|
- !ruby/object:Gem::Version
|
33
32
|
version: '7.1'
|
34
33
|
type: :runtime
|
35
34
|
prerelease: false
|
36
35
|
version_requirements: !ruby/object:Gem::Requirement
|
37
36
|
requirements:
|
38
|
-
- - "
|
37
|
+
- - ">="
|
39
38
|
- !ruby/object:Gem::Version
|
40
39
|
version: '7.1'
|
41
40
|
- !ruby/object:Gem::Dependency
|
42
41
|
name: activerecord
|
43
42
|
requirement: !ruby/object:Gem::Requirement
|
44
43
|
requirements:
|
45
|
-
- - "
|
44
|
+
- - ">="
|
46
45
|
- !ruby/object:Gem::Version
|
47
46
|
version: '7.1'
|
48
47
|
type: :runtime
|
49
48
|
prerelease: false
|
50
49
|
version_requirements: !ruby/object:Gem::Requirement
|
51
50
|
requirements:
|
52
|
-
- - "
|
51
|
+
- - ">="
|
53
52
|
- !ruby/object:Gem::Version
|
54
53
|
version: '7.1'
|
55
54
|
- !ruby/object:Gem::Dependency
|
56
55
|
name: railties
|
57
56
|
requirement: !ruby/object:Gem::Requirement
|
58
57
|
requirements:
|
59
|
-
- - "
|
58
|
+
- - ">="
|
60
59
|
- !ruby/object:Gem::Version
|
61
60
|
version: '7.1'
|
62
61
|
type: :runtime
|
63
62
|
prerelease: false
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
65
64
|
requirements:
|
66
|
-
- - "
|
65
|
+
- - ">="
|
67
66
|
- !ruby/object:Gem::Version
|
68
67
|
version: '7.1'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: apexcharts
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: groupdate
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: active_median
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
69
110
|
description: SolidApm allow you to monitor your application without any external service.
|
70
111
|
email:
|
71
112
|
- bhacaz@gmail.com
|
@@ -78,8 +119,6 @@ files:
|
|
78
119
|
- Rakefile
|
79
120
|
- app/assets/config/solid_apm_manifest.js
|
80
121
|
- app/assets/javascripts/solid_apm/application.js
|
81
|
-
- app/assets/javascripts/solid_apm/controllers/spans-chart_controller.js
|
82
|
-
- app/assets/javascripts/solid_apm/controllers/transaction-chart_controller.js
|
83
122
|
- app/assets/stylesheets/solid_apm/application.css
|
84
123
|
- app/controllers/solid_apm/application_controller.rb
|
85
124
|
- app/controllers/solid_apm/transactions_controller.rb
|
@@ -96,10 +135,11 @@ files:
|
|
96
135
|
- app/models/solid_apm/transaction.rb
|
97
136
|
- app/views/javascripts/_javascripts.html.erb
|
98
137
|
- app/views/layouts/solid_apm/application.html.erb
|
138
|
+
- app/views/solid_apm/application/_time_range_form.html.erb
|
99
139
|
- app/views/solid_apm/spans/index.html.erb
|
140
|
+
- app/views/solid_apm/transactions/_charts.html.erb
|
100
141
|
- app/views/solid_apm/transactions/index.html.erb
|
101
|
-
- app/views/solid_apm/transactions/
|
102
|
-
- app/views/solid_apm/transactions/show_by_name.html.erb
|
142
|
+
- app/views/solid_apm/transactions/spans.html.erb
|
103
143
|
- config/routes.rb
|
104
144
|
- db/migrate/20240608015633_create_solid_apm_transactions.rb
|
105
145
|
- db/migrate/20240608021940_create_solid_apm_spans.rb
|
@@ -114,7 +154,6 @@ metadata:
|
|
114
154
|
homepage_uri: https://github.com/Bhacaz/solid_apm
|
115
155
|
source_code_uri: https://github.com/Bhacaz/solid_apm
|
116
156
|
changelog_uri: https://github.com/Bhacaz/solid_apm/releases
|
117
|
-
post_install_message:
|
118
157
|
rdoc_options: []
|
119
158
|
require_paths:
|
120
159
|
- lib
|
@@ -129,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
129
168
|
- !ruby/object:Gem::Version
|
130
169
|
version: '0'
|
131
170
|
requirements: []
|
132
|
-
rubygems_version: 3.
|
133
|
-
signing_key:
|
171
|
+
rubygems_version: 3.6.2
|
134
172
|
specification_version: 4
|
135
173
|
summary: SolidApm is a DB base engine for Application Performance Monitoring.
|
136
174
|
test_files: []
|
@@ -1,97 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
Controller,
|
3
|
-
} from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js";
|
4
|
-
|
5
|
-
// Connects to data-controller="spans-charts"
|
6
|
-
window.Stimulus.register('spans-chart',
|
7
|
-
class extends Controller {
|
8
|
-
static values = { id: String }
|
9
|
-
|
10
|
-
connect() {
|
11
|
-
console.log("Connected")
|
12
|
-
const options = {
|
13
|
-
series: [
|
14
|
-
{
|
15
|
-
data: []
|
16
|
-
}
|
17
|
-
],
|
18
|
-
chart: {
|
19
|
-
type: "rangeBar",
|
20
|
-
height: "250em",
|
21
|
-
},
|
22
|
-
plotOptions: {
|
23
|
-
bar: {
|
24
|
-
horizontal: true
|
25
|
-
}
|
26
|
-
},
|
27
|
-
xaxis: {
|
28
|
-
type: "datetime",
|
29
|
-
labels: {
|
30
|
-
show: false
|
31
|
-
}
|
32
|
-
},
|
33
|
-
tooltip: {
|
34
|
-
custom: function ({y1, y2,dataPointIndex, seriesIndex, w}) {
|
35
|
-
// custom: function (opts) {
|
36
|
-
// console.log(opts)
|
37
|
-
// console.log(value)
|
38
|
-
return (
|
39
|
-
'<div class="apexcharts-tooltip-title has-text-black" style="max-width: 40em; text-wrap: balance;">' +
|
40
|
-
w.globals.initialSeries[seriesIndex].data[dataPointIndex].duration + "ms" +
|
41
|
-
"<br>" +
|
42
|
-
w.globals.initialSeries[seriesIndex].data[dataPointIndex].name +
|
43
|
-
"<br>" +
|
44
|
-
w.globals.initialSeries[seriesIndex].data[dataPointIndex].summary +
|
45
|
-
'</div>'
|
46
|
-
)
|
47
|
-
}
|
48
|
-
},
|
49
|
-
yaxis: {
|
50
|
-
labels: {
|
51
|
-
formatter: function (value, opts) {
|
52
|
-
if (opts === undefined) {
|
53
|
-
return value[1] - value[0] + "ms"
|
54
|
-
}
|
55
|
-
if (opts.dataPointIndex >= 0) {
|
56
|
-
return opts.w.globals.initialSeries[opts.seriesIndex].data[opts.dataPointIndex].name
|
57
|
-
}
|
58
|
-
return value
|
59
|
-
}
|
60
|
-
}
|
61
|
-
},
|
62
|
-
}
|
63
|
-
|
64
|
-
fetch(this.idValue + "/spans.json")
|
65
|
-
.then(response => response.json())
|
66
|
-
.then(data => {
|
67
|
-
options.series[0].data = data.map(d => {
|
68
|
-
let startTime = new Date(d.timestamp).getTime()
|
69
|
-
let endTime = new Date(d.end_time).getTime()
|
70
|
-
if (endTime - startTime < 1) {
|
71
|
-
endTime = startTime + 1
|
72
|
-
}
|
73
|
-
return {
|
74
|
-
x: d.uuid,
|
75
|
-
y: [startTime, endTime],
|
76
|
-
name: d.name,
|
77
|
-
summary: d.summary || d.name,
|
78
|
-
duration: this.round(d.duration, 2)
|
79
|
-
}
|
80
|
-
})
|
81
|
-
this.chart = new ApexCharts(this.element, options)
|
82
|
-
this.chart.render()
|
83
|
-
})
|
84
|
-
}
|
85
|
-
|
86
|
-
disconnect() {
|
87
|
-
if (this.chart) {
|
88
|
-
this.chart.destroy()
|
89
|
-
this.chart = null
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
round(num, decimalPlaces = 0) {
|
94
|
-
num = Math.round(num + "e" + decimalPlaces);
|
95
|
-
return Number(num + "e" + -decimalPlaces);
|
96
|
-
}
|
97
|
-
})
|
@@ -1,76 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
Controller,
|
3
|
-
} from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js";
|
4
|
-
// unload chart https://github.com/Deanout/weight_tracker/blob/d4123acb952d91fcc9bedb96bbd786088a71482a/app/javascript/controllers/weights_controller.js#L4
|
5
|
-
// tooltip: {
|
6
|
-
// y: {
|
7
|
-
// formatter: function (value, {series, seriesIndex, dataPointIndex, w}) {
|
8
|
-
// return w.globals.initialSeries[seriesIndex].data[dataPointIndex].name + "\n" + value + "ms"
|
9
|
-
// }
|
10
|
-
// }
|
11
|
-
// }
|
12
|
-
|
13
|
-
// Connects to data-controller="transaction-chart"
|
14
|
-
window.Stimulus.register('transaction-chart',
|
15
|
-
class extends Controller {
|
16
|
-
static values = { name: String }
|
17
|
-
|
18
|
-
connect() {
|
19
|
-
var options = {
|
20
|
-
chart: {
|
21
|
-
type: 'bar',
|
22
|
-
height: '200em'
|
23
|
-
},
|
24
|
-
series: [{
|
25
|
-
name: 'tpm',
|
26
|
-
}],
|
27
|
-
xaxis: {
|
28
|
-
type: 'datetime'
|
29
|
-
},
|
30
|
-
dataLabels: {
|
31
|
-
enabled: false
|
32
|
-
},
|
33
|
-
tooltip: {
|
34
|
-
x: {
|
35
|
-
formatter: function (value) {
|
36
|
-
return new Date(value).toLocaleString()
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
|
42
|
-
let path = "count_time_aggregations?";
|
43
|
-
if (this.nameValue) {
|
44
|
-
path = path + "name=" + encodeURIComponent(this.nameValue);
|
45
|
-
}
|
46
|
-
|
47
|
-
const fromValue = document.querySelector('input[name="from_value"]')
|
48
|
-
const fromUnit = document.querySelector('select[name="from_unit"]');
|
49
|
-
if (fromValue && fromUnit) {
|
50
|
-
path = path + "&from_value=" + fromValue.value + "&from_unit=" + fromUnit.value;
|
51
|
-
}
|
52
|
-
|
53
|
-
const base = window.location.pathname.split('/transactions')[0];
|
54
|
-
path = base + "/transactions/" + path;
|
55
|
-
path = path.replace("//", "/");
|
56
|
-
fetch(path)
|
57
|
-
.then(response => response.json())
|
58
|
-
.then(data => {
|
59
|
-
const transformedData = []
|
60
|
-
for (let [key, value] of Object.entries(data)) {
|
61
|
-
transformedData.push({x: key, y: value})
|
62
|
-
}
|
63
|
-
options.series[0].data = transformedData
|
64
|
-
this.chart = new ApexCharts(this.element, options)
|
65
|
-
this.chart.render()
|
66
|
-
})
|
67
|
-
}
|
68
|
-
|
69
|
-
// Unloads the chart before loading new data.
|
70
|
-
disconnect() {
|
71
|
-
if (this.chart) {
|
72
|
-
this.chart.destroy();
|
73
|
-
this.chart = null;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
})
|
@@ -1,9 +0,0 @@
|
|
1
|
-
|
2
|
-
<h1 class="title"><%= @transaction.name %></h1>
|
3
|
-
<h2 class="title is-6"><span class="has-text-grey-dark">Trace ID:</span> <%= @transaction.uuid %></h2>
|
4
|
-
<h2 class="title is-6"><span class="has-text-grey-dark">Timestamp:</span> <%= @transaction.timestamp %></h2>
|
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>
|
7
|
-
<h2 class="title is-6"><span class="has-text-grey-dark">Metadata:</span> <%= @transaction.metadata %></h2>
|
8
|
-
|
9
|
-
<%= render template: 'solid_apm/spans/index', collection: @transaction.spans, locals: { spans: @transaction.spans } %>
|
@@ -1,27 +0,0 @@
|
|
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>
|