rails_server_analytics 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ceecf3d520444f52ee03e83e013c11c051060c8f7aa68a6f51daade9f4ac7d32
4
+ data.tar.gz: aba1fbf7819d6b292a9900ee29cd1d162b96057aa4d88ccf3bbfe4c9aed13252
5
+ SHA512:
6
+ metadata.gz: 0663736f3c8feb23bdae41ec480b2342a521e183deae4da9e4de7d6d8bcea711485cafb5c55e8f48e3d81e04c21f8ebe310fc46625b9341149832dfa05365958
7
+ data.tar.gz: '092920b9405f6d39516a267c6001d33aed1c93b598e3c7908a7d9a41baaa34db0ebc2e2356fbcd1a5365285e0be546c190b4f20f6cdca128f2c679cd83903749'
@@ -0,0 +1,31 @@
1
+ require "rails/generators/base"
2
+
3
+ module Analytics
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+ desc "analyctics controller installation generator"
8
+
9
+ def create_analytics_controller
10
+ template "analytics_controller.rb", "app/controllers/analytics_controller.rb"
11
+ end
12
+
13
+ def create_analytics_view
14
+ template "index.html.erb.tt", "app/views/analytics/index.html.erb"
15
+ end
16
+
17
+ def add_analytics_route
18
+ route "get 'analytics', to: 'analytics#index'"
19
+ end
20
+
21
+ def create_model_file
22
+ template "analytic.rb", "app/models/analytic.rb"
23
+ end
24
+
25
+ def create_migration_file
26
+ timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
27
+ template "migration.rb", "db/migrate/#{timestamp}_create_analytics.rb"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ # analytics_data = {
2
+ # status: ok
3
+ # headers: {
4
+ #
5
+ # },
6
+ # request_uri: "/posts",
7
+ # request_method: "get",
8
+ # path_info "/posts",
9
+ # ip,
10
+ # host: "127.0.0.1",
11
+ # time: Time.now,
12
+ # time_spent: Time.now
13
+ # }
14
+
15
+ class Analytic < ApplicationRecord
16
+ def content
17
+ self.analytics_data = JSON.parse(analytics_data)
18
+ end
19
+
20
+ def self.user
21
+ Rails.root.to_path.split("/")[2]
22
+ end
23
+
24
+ def self.root
25
+ Rails.root.to_path
26
+ end
27
+
28
+ def self.version
29
+ ruby_version = `ruby -v`.split(" ")[1]
30
+ rails_version = `rails -v`.split(" ")[1]
31
+
32
+ {
33
+ ruby_v: ruby_version,
34
+ rails_v: rails_version
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,59 @@
1
+ class AnalyticsController < ApplicationController
2
+ before_action :get_analytics, :process_raw_analytics
3
+
4
+ def index
5
+ respond_to do |format|
6
+ format.html { render :index }
7
+ format.json { render json: @analytics_json }
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def get_analytics
14
+ @analytics = Analytic.all
15
+ @analytics.each(&:content)
16
+ end
17
+
18
+ def process_raw_analytics
19
+ request_count = @analytics.count
20
+ total_request_per_day = @analytics.where(updated_at: (Time.now - 1.day)..Time.now).pluck(:updated_at).count
21
+
22
+ average_response_time = get_average("time_spent")
23
+ average_cpu_usage = get_average("cpu_usage")
24
+
25
+ most_frequent_request = get_most_frequent("request_uri")
26
+ most_frequent_request_method = get_most_frequent("request_method")
27
+
28
+ version = Analytic.version
29
+ user = Analytic.user
30
+ root = Analytic.root
31
+
32
+ @analytics_json = {
33
+ analytics: @analytics,
34
+ request_count: request_count,
35
+ total_request_per_day: total_request_per_day,
36
+ average_response_time: average_response_time,
37
+ most_frequent_request: most_frequent_request,
38
+ most_frequent_request_method: most_frequent_request_method,
39
+ average_cpu_usage: average_cpu_usage,
40
+ ruby_v: version[:ruby_v],
41
+ rails_v: version[:rails_v],
42
+ user: user,
43
+ root: root
44
+ }
45
+ end
46
+
47
+ def get_most_frequent(data)
48
+ all_request = @analytics.map(&:analytics_data).pluck(data)
49
+ all_request.detect { |r| all_request.count(r) > 1 }
50
+
51
+ counts = all_request.group_by { |e| e }.transform_values(&:size)
52
+ counts.max_by { |_, count| count }[0]
53
+ end
54
+
55
+ def get_average(data)
56
+ all_data = @analytics.map(&:analytics_data).pluck(data)
57
+ (all_data.sum / @analytics.count).truncate(3)
58
+ end
59
+ end
@@ -0,0 +1,212 @@
1
+ <!-- analytics_dashboard.html.erb -->
2
+ <title>System Analytics Dashboard</title>
3
+ <style>
4
+ /* Reset and base styles */
5
+ * {
6
+ margin: 0;
7
+ padding: 0;
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ body {
12
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
13
+ line-height: 1.6;
14
+ color: #333;
15
+ background-color: #f5f5f5;
16
+ padding: 20px;
17
+ }
18
+
19
+ /* Container */
20
+ .container {
21
+ max-width: 1200px;
22
+ margin: 0 auto;
23
+ }
24
+
25
+ /* Header styles */
26
+ .header {
27
+ display: flex;
28
+ justify-content: space-between;
29
+ align-items: center;
30
+ margin-bottom: 30px;
31
+ background-color: white;
32
+ padding: 20px;
33
+ border-radius: 8px;
34
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
35
+ }
36
+
37
+ .version-info {
38
+ display: flex;
39
+ gap: 20px;
40
+ font-size: 0.9em;
41
+ color: #666;
42
+ }
43
+
44
+ /* Grid layout */
45
+ .metrics-grid {
46
+ display: grid;
47
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
48
+ gap: 20px;
49
+ margin-bottom: 30px;
50
+ }
51
+
52
+ /* Card styles */
53
+ .card {
54
+ background: white;
55
+ padding: 20px;
56
+ border-radius: 8px;
57
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
58
+ }
59
+
60
+ .metric-card {
61
+ text-align: center;
62
+ }
63
+
64
+ .metric-title {
65
+ font-size: 0.9em;
66
+ color: #666;
67
+ margin-bottom: 10px;
68
+ }
69
+
70
+ .metric-value {
71
+ font-size: 1.8em;
72
+ font-weight: bold;
73
+ color: #2563eb;
74
+ }
75
+
76
+ /* Chart styles */
77
+ .chart-container {
78
+ margin-bottom: 30px;
79
+ }
80
+
81
+ .chart {
82
+ width: 100%;
83
+ height: 300px;
84
+ background: white;
85
+ padding: 20px;
86
+ border-radius: 8px;
87
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
88
+ }
89
+
90
+ /* Request details styles */
91
+ .details-grid {
92
+ display: grid;
93
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
94
+ gap: 20px;
95
+ }
96
+
97
+ .detail-item {
98
+ margin-bottom: 15px;
99
+ }
100
+
101
+ .detail-label {
102
+ font-size: 0.9em;
103
+ color: #666;
104
+ margin-bottom: 5px;
105
+ }
106
+
107
+ .detail-value {
108
+ font-weight: 600;
109
+ }
110
+
111
+ /* Responsive adjustments */
112
+ @media (max-width: 768px) {
113
+ .header {
114
+ flex-direction: column;
115
+ gap: 10px;
116
+ text-align: center;
117
+ }
118
+
119
+ .version-info {
120
+ flex-direction: column;
121
+ gap: 5px;
122
+ }
123
+ }
124
+ </style>
125
+ <div class="container">
126
+ <!-- Header -->
127
+ <div class="header">
128
+ <h1>System Analytics Dashboard</h1>
129
+ <div class="version-info">
130
+ <span>Ruby <%%= @analytics_json[:ruby_v] %></span>
131
+ <span>Rails <%%= @analytics_json[:rails_v] %></span>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Key Metrics -->
136
+ <div class="metrics-grid">
137
+ <div class="card metric-card">
138
+ <div class="metric-title">Total Requests</div>
139
+ <div class="metric-value"><%%= number_with_delimiter(@analytics_json[:request_count]) %></div>
140
+ </div>
141
+
142
+ <div class="card metric-card">
143
+ <div class="metric-title">Avg Response Time</div>
144
+ <div class="metric-value"><%%= @analytics_json[:average_response_time] %>ms</div>
145
+ </div>
146
+
147
+ <div class="card metric-card">
148
+ <div class="metric-title">CPU Usage</div>
149
+ <div class="metric-value"><%%= @analytics_json[:average_cpu_usage] %>%</div>
150
+ </div>
151
+
152
+ <div class="card metric-card">
153
+ <div class="metric-title">System User</div>
154
+ <div class="metric-value"><%%= @analytics_json[:user] %></div>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- Chart Card -->
159
+ <div class="chart-container card">
160
+ <h2>Requests per Day</h2>
161
+ <div class="chart">
162
+ <%%
163
+ # You would need to implement chart rendering here
164
+ # You could use Chart.js or any other JavaScript charting library
165
+ # For now, we'll show the data in a table format
166
+ %>
167
+ <table style="width: 100%; border-collapse: collapse;">
168
+ <thead>
169
+ <tr>
170
+ <th style="text-align: left; padding: 8px; border-bottom: 1px solid #ddd;">Date</th>
171
+ <th style="text-align: right; padding: 8px; border-bottom: 1px solid #ddd;">Requests</th>
172
+ </tr>
173
+ </thead>
174
+ <tbody>
175
+ <tr>
176
+ <td><%% @analytics_json[:total_request_per_day] %></td>
177
+ </tr>
178
+ </tbody>
179
+ </table>
180
+ </div>
181
+ </div>
182
+
183
+ <!-- Request Details -->
184
+ <div class="card">
185
+ <h2 style="margin-bottom: 20px;">Request Details</h2>
186
+ <div class="details-grid">
187
+ <div class="detail-item">
188
+ <div class="detail-label">Most Frequent Request</div>
189
+ <div class="detail-value"><%%= @analytics_json[:most_frequent_request] %></div>
190
+ </div>
191
+
192
+ <div class="detail-item">
193
+ <div class="detail-label">Request Method</div>
194
+ <div class="detail-value"><%%= @analytics_json[:most_frequent_request_method] %></div>
195
+ </div>
196
+
197
+ <div class="detail-item">
198
+ <div class="detail-label">System Root</div>
199
+ <div class="detail-value"><%%= @analytics_json[:root] %></div>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <%% if @analytics_json[:auto_refresh] %>
206
+ <script>
207
+ setTimeout(function() {
208
+ window.location.reload();
209
+ }, 5000);
210
+ </script>
211
+ <%% end %>
212
+
@@ -0,0 +1,9 @@
1
+ class CreateAnalytics < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :analytics do |t|
4
+ t.json :analytics_data
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,67 @@
1
+ require "active_support/log_subscriber"
2
+
3
+ module RailsServerAnalytics
4
+ class AnalyticsTracer < ActiveSupport::LogSubscriber
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ Rails.logger.info(color("Processing request in middleware", YELLOW))
12
+
13
+ time_start = Time.now
14
+ before_cpu = `ps -o %cpu= -p #{Process.pid}`.strip.to_f
15
+
16
+ status, headers, response = @app.call(env)
17
+ # request = ActionDispatch::Request.new env
18
+
19
+ # data = {status:, headers:, response: response.body, request: request.body, time:}
20
+ request_uri = env["REQUEST_URI"]
21
+ request_method = env["REQUEST_METHOD"].downcase
22
+ path_info = env["PATH_INFO"]
23
+ ip = env["action_dispatch.remote_ip"].calculate_ip
24
+ host = env["HTTP_HOST"]
25
+ time_end = Time.now
26
+
27
+ time_spent = time_end - time_start
28
+
29
+ after_cpu = `ps -o %cpu= -p #{Process.pid}`.strip.to_f
30
+ cpu_usage = after_cpu - before_cpu
31
+
32
+ return [status, headers, response] if path_info.eql? "/analytics"
33
+
34
+ analytics_data = JSON.pretty_generate({
35
+ status:,
36
+ headers:,
37
+ request_uri:,
38
+ request_method:,
39
+ path_info:,
40
+ ip:,
41
+ host:,
42
+ time_spent:,
43
+ cpu_usage:,
44
+ })
45
+
46
+ analytics = Analytic.create(analytics_data:)
47
+
48
+ Rails.logger.info(color("response data stored", GREEN))
49
+ # write_data(data)
50
+
51
+ [status, headers, response]
52
+ end
53
+
54
+ def write_data(data)
55
+ json_path =
56
+ if defined?(Rails)
57
+ analytics_dir = File.join(Rails.root, "analytics_data/")
58
+ FileUtils.mkdir_p(analytics_dir)[0] + "trace.json"
59
+ end
60
+
61
+ File.open(json_path, "w") do |f|
62
+ f.write(JSON.pretty_generate(data))
63
+ end
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,13 @@
1
+ require "rails/engine"
2
+
3
+ module RailsServerAnalytics
4
+ class Engine < Rails::Engine
5
+ initializer "server_analytics.configure_middleware" do |app|
6
+ app.middleware.use RailsServerAnalytics::AnalyticsTracer
7
+ end
8
+
9
+ generators do
10
+ require "generators/analytics_generator"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsServerAnalytics
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rails_server_analytics/version"
4
+ require "rails_server_analytics/analytics_tracer"
5
+ require "rails_server_analytics/engine" if defined?(Rails::Engine)
6
+
7
+ module RailsServerAnalytics
8
+ class Error < StandardError; end
9
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_server_analytics
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chong Wei Jie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-11-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: a gem that applies a custom middleware in a rails application for tracing
14
+ requests and generates an analytics page for monitoring.
15
+ email:
16
+ - jackchong398@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/generators/analytics_generator.rb
22
+ - lib/generators/templates/analytic.rb
23
+ - lib/generators/templates/analytics_controller.rb
24
+ - lib/generators/templates/index.html.erb.tt
25
+ - lib/generators/templates/migration.rb
26
+ - lib/rails_server_analytics.rb
27
+ - lib/rails_server_analytics/analytics_tracer.rb
28
+ - lib/rails_server_analytics/engine.rb
29
+ - lib/rails_server_analytics/version.rb
30
+ homepage: https://github.com/Cwjiee/rails-server-analytics
31
+ licenses:
32
+ - MIT
33
+ metadata:
34
+ homepage_uri: https://github.com/Cwjiee/rails-server-analytics
35
+ source_code_uri: https://github.com/Cwjiee/rails-server-analytics
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.0.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.5.11
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: A middleware to trace and montior requests and responses in rails
55
+ test_files: []