rails_server_analytics 0.1.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 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: []