rails_pulse 0.1.1 → 0.1.3
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/README.md +79 -177
- data/Rakefile +77 -2
- data/app/assets/images/rails_pulse/dashboard.png +0 -0
- data/app/assets/images/rails_pulse/request.png +0 -0
- data/app/assets/stylesheets/rails_pulse/application.css +28 -17
- data/app/assets/stylesheets/rails_pulse/components/badge.css +13 -0
- data/app/assets/stylesheets/rails_pulse/components/base.css +12 -2
- data/app/assets/stylesheets/rails_pulse/components/collapsible.css +30 -0
- data/app/assets/stylesheets/rails_pulse/components/popover.css +0 -1
- data/app/assets/stylesheets/rails_pulse/components/row.css +55 -3
- data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +23 -0
- data/app/controllers/concerns/chart_table_concern.rb +21 -4
- data/app/controllers/concerns/response_range_concern.rb +6 -3
- data/app/controllers/concerns/time_range_concern.rb +5 -10
- data/app/controllers/concerns/zoom_range_concern.rb +32 -1
- data/app/controllers/rails_pulse/application_controller.rb +13 -5
- data/app/controllers/rails_pulse/dashboard_controller.rb +12 -0
- data/app/controllers/rails_pulse/queries_controller.rb +111 -51
- data/app/controllers/rails_pulse/requests_controller.rb +37 -12
- data/app/controllers/rails_pulse/routes_controller.rb +98 -24
- data/app/helpers/rails_pulse/application_helper.rb +0 -1
- data/app/helpers/rails_pulse/chart_formatters.rb +3 -3
- data/app/helpers/rails_pulse/chart_helper.rb +21 -9
- data/app/helpers/rails_pulse/status_helper.rb +10 -4
- data/app/javascript/rails_pulse/application.js +34 -3
- data/app/javascript/rails_pulse/controllers/collapsible_controller.js +32 -0
- data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +2 -1
- data/app/javascript/rails_pulse/controllers/expandable_rows_controller.js +58 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +353 -39
- data/app/javascript/rails_pulse/controllers/pagination_controller.js +17 -27
- data/app/javascript/rails_pulse/controllers/popover_controller.js +28 -4
- data/app/javascript/rails_pulse/controllers/table_sort_controller.js +14 -0
- data/app/jobs/rails_pulse/backfill_summaries_job.rb +41 -0
- data/app/jobs/rails_pulse/summary_job.rb +53 -0
- data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +28 -7
- data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +18 -22
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +18 -7
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +34 -41
- data/app/models/rails_pulse/operation.rb +1 -1
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +49 -25
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +40 -28
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +37 -43
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +23 -97
- data/app/models/rails_pulse/queries/tables/index.rb +74 -0
- data/app/models/rails_pulse/query.rb +47 -0
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +23 -84
- data/app/models/rails_pulse/route.rb +1 -6
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +45 -25
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +43 -45
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +36 -44
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +37 -27
- data/app/models/rails_pulse/routes/charts/average_response_times.rb +23 -100
- data/app/models/rails_pulse/routes/tables/index.rb +57 -40
- data/app/models/rails_pulse/summary.rb +143 -0
- data/app/services/rails_pulse/analysis/backtrace_analyzer.rb +256 -0
- data/app/services/rails_pulse/analysis/base_analyzer.rb +67 -0
- data/app/services/rails_pulse/analysis/explain_plan_analyzer.rb +206 -0
- data/app/services/rails_pulse/analysis/index_recommendation_engine.rb +326 -0
- data/app/services/rails_pulse/analysis/n_plus_one_detector.rb +241 -0
- data/app/services/rails_pulse/analysis/query_characteristics_analyzer.rb +146 -0
- data/app/services/rails_pulse/analysis/suggestion_generator.rb +217 -0
- data/app/services/rails_pulse/query_analysis_service.rb +125 -0
- data/app/services/rails_pulse/summary_service.rb +199 -0
- data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +0 -1
- data/app/views/layouts/rails_pulse/application.html.erb +4 -6
- data/app/views/rails_pulse/components/_breadcrumbs.html.erb +1 -1
- data/app/views/rails_pulse/components/_code_panel.html.erb +17 -3
- data/app/views/rails_pulse/components/_empty_state.html.erb +11 -0
- data/app/views/rails_pulse/components/_metric_card.html.erb +37 -28
- data/app/views/rails_pulse/components/_panel.html.erb +1 -1
- data/app/views/rails_pulse/components/_sparkline_stats.html.erb +5 -7
- data/app/views/rails_pulse/components/_table_head.html.erb +6 -1
- data/app/views/rails_pulse/dashboard/index.html.erb +55 -37
- data/app/views/rails_pulse/operations/show.html.erb +17 -15
- data/app/views/rails_pulse/queries/_analysis_error.html.erb +15 -0
- data/app/views/rails_pulse/queries/_analysis_prompt.html.erb +27 -0
- data/app/views/rails_pulse/queries/_analysis_results.html.erb +87 -0
- data/app/views/rails_pulse/queries/_analysis_section.html.erb +39 -0
- data/app/views/rails_pulse/queries/_show_table.html.erb +2 -2
- data/app/views/rails_pulse/queries/_table.html.erb +11 -13
- data/app/views/rails_pulse/queries/index.html.erb +32 -28
- data/app/views/rails_pulse/queries/show.html.erb +45 -34
- data/app/views/rails_pulse/requests/_operations.html.erb +38 -45
- data/app/views/rails_pulse/requests/_table.html.erb +3 -3
- data/app/views/rails_pulse/requests/index.html.erb +33 -28
- data/app/views/rails_pulse/routes/_table.html.erb +14 -14
- data/app/views/rails_pulse/routes/index.html.erb +34 -29
- data/app/views/rails_pulse/routes/show.html.erb +43 -36
- data/config/initializers/rails_pulse.rb +0 -12
- data/config/routes.rb +5 -1
- data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +54 -0
- data/db/migrate/20250916031656_add_analysis_to_rails_pulse_queries.rb +13 -0
- data/db/rails_pulse_schema.rb +130 -0
- data/lib/generators/rails_pulse/convert_to_migrations_generator.rb +65 -0
- data/lib/generators/rails_pulse/install_generator.rb +94 -4
- data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +60 -0
- data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb +22 -0
- data/lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb +19 -0
- data/lib/generators/rails_pulse/templates/rails_pulse.rb +0 -12
- data/lib/generators/rails_pulse/upgrade_generator.rb +225 -0
- data/lib/rails_pulse/configuration.rb +0 -11
- data/lib/rails_pulse/engine.rb +0 -1
- data/lib/rails_pulse/version.rb +1 -1
- data/lib/tasks/rails_pulse.rake +77 -0
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +53 -53
- data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
- data/public/rails-pulse-assets/search.svg +43 -0
- metadata +48 -14
- data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
- data/app/assets/images/rails_pulse/routes.png +0 -0
- data/app/controllers/rails_pulse/caches_controller.rb +0 -115
- data/app/helpers/rails_pulse/cached_component_helper.rb +0 -73
- data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +0 -67
- data/app/models/rails_pulse/component_cache_key.rb +0 -33
- data/app/views/rails_pulse/caches/show.html.erb +0 -9
- data/db/migrate/20250227235904_create_routes.rb +0 -12
- data/db/migrate/20250227235915_create_requests.rb +0 -19
- data/db/migrate/20250228000000_create_queries.rb +0 -14
- data/db/migrate/20250228000056_create_operations.rb +0 -24
- data/lib/rails_pulse/migration.rb +0 -29
@@ -0,0 +1,43 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="748.974" height="457.275" viewBox="0 0 748.974 457.275" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" artist="Katerina Limpitsouni" source="https://undraw.co/">
|
2
|
+
<g id="Group_201" data-name="Group 201" transform="translate(-382.003 -195.455)">
|
3
|
+
<g id="Group_200" data-name="Group 200" transform="translate(382.003 195.455)">
|
4
|
+
<path id="Path_3120-1325" data-name="Path 3120" d="M695.225,508.82,433.394,576.244a34.622,34.622,0,0,1-42.114-24.866L312.1,243.879a34.622,34.622,0,0,1,24.866-42.114l243.591-62.727L642.9,166.948l77.191,299.757A34.622,34.622,0,0,1,695.225,508.82Z" transform="translate(-311.003 -139.037)" fill="#f2f2f2"/>
|
5
|
+
<path id="Path_3121-1326" data-name="Path 3121" d="M338.989,210.925a24.655,24.655,0,0,0-17.708,29.99l79.185,307.5a24.655,24.655,0,0,0,29.99,17.708L692.287,498.7a24.655,24.655,0,0,0,17.708-29.99L634,173.595l-54.792-24.529Z" transform="translate(-310.548 -138.556)" fill="#fff"/>
|
6
|
+
<path id="Path_3122-1327" data-name="Path 3122" d="M629.927,168.5l-40.522,10.435a11.518,11.518,0,0,1-14.026-8.282l-7.707-29.929a.72.72,0,0,1,.989-.837l61.379,27.258a.72.72,0,0,1-.113,1.355Z" transform="translate(-298.695 -139)" fill="#f2f2f2"/>
|
7
|
+
<path id="Path_3123-1328" data-name="Path 3123" d="M612.519,418.284l-119.208,30.7a5.759,5.759,0,0,1-2.872-11.154l119.208-30.7a5.759,5.759,0,1,1,2.872,11.154Z" transform="translate(-302.605 -126.189)" fill="#ccc"/>
|
8
|
+
<path id="Path_3124-1329" data-name="Path 3124" d="M640.149,430.592,497.936,467.214a5.759,5.759,0,1,1-2.872-11.154l142.213-36.622a5.759,5.759,0,0,1,2.872,11.154Z" transform="translate(-302.384 -125.599)" fill="#ccc"/>
|
9
|
+
<circle id="Ellipse_44" data-name="Ellipse 44" cx="20.355" cy="20.355" r="20.355" transform="translate(121.697 319.055)" fill="#FFC91F"/>
|
10
|
+
<path id="Path_3125-1330" data-name="Path 3125" d="M604.421,374.437,446.1,415.191a17.835,17.835,0,0,1-21.694-12.812L391.229,273.49A17.835,17.835,0,0,1,404.041,251.8l158.32-40.754a17.835,17.835,0,0,1,21.694,12.812l33.178,128.889A17.835,17.835,0,0,1,604.421,374.437Z" transform="translate(-307.183 -135.611)" fill="#fff"/>
|
11
|
+
<path id="Path_3126-1331" data-name="Path 3126" d="M604.421,374.437,446.1,415.191a17.835,17.835,0,0,1-21.694-12.812L391.229,273.49A17.835,17.835,0,0,1,404.041,251.8l158.32-40.754a17.835,17.835,0,0,1,21.694,12.812l33.178,128.889A17.835,17.835,0,0,1,604.421,374.437ZM404.563,253.826a15.737,15.737,0,0,0-11.3,19.142l33.178,128.889a15.737,15.737,0,0,0,19.142,11.3L603.9,372.407a15.737,15.737,0,0,0,11.3-19.142L582.025,224.376a15.737,15.737,0,0,0-19.142-11.3Z" transform="translate(-307.183 -135.611)" fill="#e6e6e6"/>
|
12
|
+
<path id="Path_411-1332" data-name="Path 411" d="M550.66,252.63l-79.9,20.568a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l81.335-20.937c3.286,1.665,2.421,5.07.091,5.67Z" transform="translate(-303.514 -133.861)" fill="#f2f2f2"/>
|
13
|
+
<path id="Path_412-1333" data-name="Path 412" d="M554.1,266l-79.9,20.568a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l81.335-20.937c3.286,1.665,2.421,5.07.091,5.67Z" transform="translate(-303.349 -133.22)" fill="#f2f2f2"/>
|
14
|
+
<path id="Path_413-1334" data-name="Path 413" d="M461.146,298.825,436.761,305.1a3.1,3.1,0,0,1-3.776-2.23L425.577,274.1a3.1,3.1,0,0,1,2.23-3.776l24.385-6.277a3.105,3.105,0,0,1,3.776,2.23l7.408,28.777a3.1,3.1,0,0,1-2.23,3.776Z" transform="translate(-305.513 -133.047)" fill="#FFC91F"/>
|
15
|
+
<path id="Path_414-1335" data-name="Path 414" d="M562.854,293.445,440.909,324.835a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l123.38-31.76c3.286,1.665,2.421,5.07.091,5.67Z" transform="translate(-304.946 -131.904)" fill="#f2f2f2"/>
|
16
|
+
<path id="Path_415-1336" data-name="Path 415" d="M566.3,306.822,444.353,338.213a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l123.38-31.76c3.286,1.665,2.421,5.07.091,5.67Z" transform="translate(-304.781 -131.263)" fill="#f2f2f2"/>
|
17
|
+
<path id="Path_416-1337" data-name="Path 416" d="M569.739,320.192,447.794,351.582a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l123.379-31.76c3.286,1.665,2.421,5.07.091,5.67Z" transform="translate(-304.616 -130.621)" fill="#f2f2f2"/>
|
18
|
+
<path id="Path_417-1338" data-name="Path 417" d="M573.183,333.569,451.237,364.959a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l123.38-31.76C576.377,329.564,575.513,332.969,573.183,333.569Z" transform="translate(-304.45 -129.98)" fill="#f2f2f2"/>
|
19
|
+
<path id="Path_418-1339" data-name="Path 418" d="M576.624,346.939,454.679,378.329a2.862,2.862,0,0,1-3.467-1.8,2.757,2.757,0,0,1,1.942-3.5l123.38-31.76C579.819,342.934,578.954,346.339,576.624,346.939Z" transform="translate(-304.285 -129.339)" fill="#f2f2f2"/>
|
20
|
+
<path id="Path_395-1340" data-name="Path 395" d="M448.363,470.511a2.111,2.111,0,0,1-1.335-.092l-.026-.011-5.545-2.351a2.126,2.126,0,1,1,1.664-3.913l3.593,1.528,4.708-11.076a2.125,2.125,0,0,1,2.787-1.124h0l-.028.072.029-.073a2.127,2.127,0,0,1,1.124,2.788l-5.539,13.023a2.126,2.126,0,0,1-1.431,1.224Z" transform="translate(-304.809 -123.966)" fill="#fff"/>
|
21
|
+
</g>
|
22
|
+
<g id="Group_199" data-name="Group 199" transform="translate(673.007 225.872) rotate(-8)">
|
23
|
+
<g id="Group_198" data-name="Group 198" transform="translate(125.896 0) rotate(19)">
|
24
|
+
<path id="Path_3127-1341" data-name="Path 3127" d="M304.956,386.7H34.583A34.622,34.622,0,0,1,0,352.114V34.583A34.622,34.622,0,0,1,34.583,0H286.121l53.418,42.577V352.114A34.622,34.622,0,0,1,304.956,386.7Z" transform="translate(0 0)" fill="#e6e6e6"/>
|
25
|
+
<path id="Path_3128-1342" data-name="Path 3128" d="M24.627,0A24.655,24.655,0,0,0,0,24.627V342.158a24.655,24.655,0,0,0,24.627,24.627H295a24.655,24.655,0,0,0,24.627-24.627V37.418L272.683,0Z" transform="translate(9.956 9.956)" fill="#fff"/>
|
26
|
+
<path id="Path_3129-1343" data-name="Path 3129" d="M128.856,11.518H5.759A5.759,5.759,0,0,1,5.759,0h123.1a5.759,5.759,0,0,1,0,11.518Z" transform="translate(123.512 90.767)" fill="#FFC91F"/>
|
27
|
+
<path id="Path_3130-1344" data-name="Path 3130" d="M152.612,11.518H5.759A5.759,5.759,0,0,1,5.759,0H152.612a5.759,5.759,0,1,1,0,11.518Z" transform="translate(123.512 110.204)" fill="#FFC91F"/>
|
28
|
+
<path id="Path_3131-1345" data-name="Path 3131" d="M128.852,0H5.758a5.758,5.758,0,1,0,0,11.517H128.852a5.759,5.759,0,0,0,0-11.517Z" transform="translate(123.517 177.868)" fill="#ccc"/>
|
29
|
+
<path id="Path_3132-1346" data-name="Path 3132" d="M152.609,0H5.758a5.759,5.759,0,1,0,0,11.517h146.85a5.759,5.759,0,1,0,0-11.517Z" transform="translate(123.517 197.307)" fill="#ccc"/>
|
30
|
+
<path id="Path_3133-1347" data-name="Path 3133" d="M128.856,11.518H5.759A5.759,5.759,0,0,1,5.759,0h123.1a5.759,5.759,0,0,1,0,11.518Z" transform="translate(123.512 264.975)" fill="#ccc"/>
|
31
|
+
<path id="Path_3134-1348" data-name="Path 3134" d="M152.612,11.518H5.759A5.759,5.759,0,0,1,5.759,0H152.612a5.759,5.759,0,1,1,0,11.518Z" transform="translate(123.512 284.411)" fill="#ccc"/>
|
32
|
+
<circle id="Ellipse_44-2" data-name="Ellipse 44" cx="20.355" cy="20.355" r="20.355" transform="translate(57.655 85.89)" fill="#FFC91F"/>
|
33
|
+
<path id="Path_395-2-1349" data-name="Path 395" d="M6.909,15.481a2.111,2.111,0,0,1-1.27-.422l-.023-.017L.832,11.382A2.126,2.126,0,0,1,3.419,8.008l3.1,2.376L13.839.832A2.125,2.125,0,0,1,16.819.439h0L16.774.5l.047-.063a2.127,2.127,0,0,1,.393,2.98L8.6,14.649a2.126,2.126,0,0,1-1.691.829Z" transform="translate(69.085 98.528)" fill="#fff"/>
|
34
|
+
<path id="Path_3135-1350" data-name="Path 3135" d="M40.707,20.359A20.354,20.354,0,0,1,20.356,40.721a4.372,4.372,0,0,1-.524-.021A20.353,20.353,0,1,1,40.707,20.359Z" transform="translate(59.75 172.987)" fill="#FFC91F"/>
|
35
|
+
<circle id="Ellipse_44-3" data-name="Ellipse 44" cx="20.355" cy="20.355" r="20.355" transform="translate(57.655 260.097)" fill="#FFC91F"/>
|
36
|
+
<path id="Path_3136-1351" data-name="Path 3136" d="M53.362,43.143H11.518A11.518,11.518,0,0,1,0,31.625V.72A.72.72,0,0,1,1.167.156l52.642,41.7a.72.72,0,0,1-.447,1.284Z" transform="translate(285.137 0.805)" fill="#ccc"/>
|
37
|
+
</g>
|
38
|
+
</g>
|
39
|
+
<path id="Path_3140-1352" data-name="Path 3140" d="M754.518,518.049a9.158,9.158,0,0,1-12.587,3.05L635.078,455.923a9.158,9.158,0,0,1,9.538-15.637l106.852,65.176a9.158,9.158,0,0,1,3.049,12.587Z" transform="translate(123.58 101.359)" fill="#3f3d56"/>
|
40
|
+
<path id="Path_3141-1353" data-name="Path 3141" d="M688.648,486.5a73.265,73.265,0,1,1-24.4-100.7A73.265,73.265,0,0,1,688.648,486.5ZM579.19,419.73a54.949,54.949,0,1,0,75.524-18.3,54.949,54.949,0,0,0-75.524,18.3Z" transform="translate(82.597 67.737)" fill="#3f3d56"/>
|
41
|
+
<circle id="Ellipse_44-4" data-name="Ellipse 44" cx="57.007" cy="57.007" r="57.007" transform="translate(672.542 442.858) rotate(19)" fill="#FFC91F"/>
|
42
|
+
</g>
|
43
|
+
</svg>
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_pulse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rails Pulse
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-09-24 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -145,6 +145,20 @@ dependencies:
|
|
145
145
|
- - "~>"
|
146
146
|
- !ruby/object:Gem::Version
|
147
147
|
version: '6.0'
|
148
|
+
- !ruby/object:Gem::Dependency
|
149
|
+
name: rails-controller-testing
|
150
|
+
requirement: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
type: :development
|
156
|
+
prerelease: false
|
157
|
+
version_requirements: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
148
162
|
description: Ruby on Rails performance monitoring tool that provides insights into
|
149
163
|
your application's performance, helping you identify bottlenecks and optimize your
|
150
164
|
code for better efficiency.
|
@@ -159,9 +173,7 @@ files:
|
|
159
173
|
- Rakefile
|
160
174
|
- app/assets/images/rails_pulse/dashboard.png
|
161
175
|
- app/assets/images/rails_pulse/menu.svg
|
162
|
-
- app/assets/images/rails_pulse/rails-pulse-logo.png
|
163
176
|
- app/assets/images/rails_pulse/request.png
|
164
|
-
- app/assets/images/rails_pulse/routes.png
|
165
177
|
- app/assets/stylesheets/rails_pulse/application.css
|
166
178
|
- app/assets/stylesheets/rails_pulse/components/alert.css
|
167
179
|
- app/assets/stylesheets/rails_pulse/components/badge.css
|
@@ -170,6 +182,7 @@ files:
|
|
170
182
|
- app/assets/stylesheets/rails_pulse/components/button.css
|
171
183
|
- app/assets/stylesheets/rails_pulse/components/card.css
|
172
184
|
- app/assets/stylesheets/rails_pulse/components/chart.css
|
185
|
+
- app/assets/stylesheets/rails_pulse/components/collapsible.css
|
173
186
|
- app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css
|
174
187
|
- app/assets/stylesheets/rails_pulse/components/descriptive_list.css
|
175
188
|
- app/assets/stylesheets/rails_pulse/components/dialog.css
|
@@ -190,7 +203,6 @@ files:
|
|
190
203
|
- app/controllers/concerns/zoom_range_concern.rb
|
191
204
|
- app/controllers/rails_pulse/application_controller.rb
|
192
205
|
- app/controllers/rails_pulse/assets_controller.rb
|
193
|
-
- app/controllers/rails_pulse/caches_controller.rb
|
194
206
|
- app/controllers/rails_pulse/csp_test_controller.rb
|
195
207
|
- app/controllers/rails_pulse/dashboard_controller.rb
|
196
208
|
- app/controllers/rails_pulse/operations_controller.rb
|
@@ -199,30 +211,32 @@ files:
|
|
199
211
|
- app/controllers/rails_pulse/routes_controller.rb
|
200
212
|
- app/helpers/rails_pulse/application_helper.rb
|
201
213
|
- app/helpers/rails_pulse/breadcrumbs_helper.rb
|
202
|
-
- app/helpers/rails_pulse/cached_component_helper.rb
|
203
214
|
- app/helpers/rails_pulse/chart_formatters.rb
|
204
215
|
- app/helpers/rails_pulse/chart_helper.rb
|
205
216
|
- app/helpers/rails_pulse/formatting_helper.rb
|
206
217
|
- app/helpers/rails_pulse/status_helper.rb
|
207
218
|
- app/helpers/rails_pulse/table_helper.rb
|
208
219
|
- app/javascript/rails_pulse/application.js
|
220
|
+
- app/javascript/rails_pulse/controllers/collapsible_controller.js
|
209
221
|
- app/javascript/rails_pulse/controllers/color_scheme_controller.js
|
210
222
|
- app/javascript/rails_pulse/controllers/context_menu_controller.js
|
211
223
|
- app/javascript/rails_pulse/controllers/dialog_controller.js
|
212
|
-
- app/javascript/rails_pulse/controllers/
|
224
|
+
- app/javascript/rails_pulse/controllers/expandable_rows_controller.js
|
213
225
|
- app/javascript/rails_pulse/controllers/form_controller.js
|
214
226
|
- app/javascript/rails_pulse/controllers/icon_controller.js
|
215
227
|
- app/javascript/rails_pulse/controllers/index_controller.js
|
216
228
|
- app/javascript/rails_pulse/controllers/menu_controller.js
|
217
229
|
- app/javascript/rails_pulse/controllers/pagination_controller.js
|
218
230
|
- app/javascript/rails_pulse/controllers/popover_controller.js
|
231
|
+
- app/javascript/rails_pulse/controllers/table_sort_controller.js
|
219
232
|
- app/javascript/rails_pulse/controllers/timezone_controller.js
|
220
233
|
- app/javascript/rails_pulse/theme.js
|
221
234
|
- app/jobs/rails_pulse/application_job.rb
|
235
|
+
- app/jobs/rails_pulse/backfill_summaries_job.rb
|
222
236
|
- app/jobs/rails_pulse/cleanup_job.rb
|
237
|
+
- app/jobs/rails_pulse/summary_job.rb
|
223
238
|
- app/mailers/rails_pulse/application_mailer.rb
|
224
239
|
- app/models/rails_pulse/application_record.rb
|
225
|
-
- app/models/rails_pulse/component_cache_key.rb
|
226
240
|
- app/models/rails_pulse/dashboard/charts/average_response_time.rb
|
227
241
|
- app/models/rails_pulse/dashboard/charts/p95_response_time.rb
|
228
242
|
- app/models/rails_pulse/dashboard/tables/slow_queries.rb
|
@@ -232,6 +246,7 @@ files:
|
|
232
246
|
- app/models/rails_pulse/queries/cards/execution_rate.rb
|
233
247
|
- app/models/rails_pulse/queries/cards/percentile_query_times.rb
|
234
248
|
- app/models/rails_pulse/queries/charts/average_query_times.rb
|
249
|
+
- app/models/rails_pulse/queries/tables/index.rb
|
235
250
|
- app/models/rails_pulse/query.rb
|
236
251
|
- app/models/rails_pulse/request.rb
|
237
252
|
- app/models/rails_pulse/requests/charts/average_response_times.rb
|
@@ -243,13 +258,23 @@ files:
|
|
243
258
|
- app/models/rails_pulse/routes/cards/request_count_totals.rb
|
244
259
|
- app/models/rails_pulse/routes/charts/average_response_times.rb
|
245
260
|
- app/models/rails_pulse/routes/tables/index.rb
|
261
|
+
- app/models/rails_pulse/summary.rb
|
262
|
+
- app/services/rails_pulse/analysis/backtrace_analyzer.rb
|
263
|
+
- app/services/rails_pulse/analysis/base_analyzer.rb
|
264
|
+
- app/services/rails_pulse/analysis/explain_plan_analyzer.rb
|
265
|
+
- app/services/rails_pulse/analysis/index_recommendation_engine.rb
|
266
|
+
- app/services/rails_pulse/analysis/n_plus_one_detector.rb
|
267
|
+
- app/services/rails_pulse/analysis/query_characteristics_analyzer.rb
|
268
|
+
- app/services/rails_pulse/analysis/suggestion_generator.rb
|
269
|
+
- app/services/rails_pulse/query_analysis_service.rb
|
246
270
|
- app/services/rails_pulse/sql_query_normalizer.rb
|
271
|
+
- app/services/rails_pulse/summary_service.rb
|
247
272
|
- app/views/layouts/rails_pulse/_menu_items.html.erb
|
248
273
|
- app/views/layouts/rails_pulse/_sidebar_menu.html.erb
|
249
274
|
- app/views/layouts/rails_pulse/application.html.erb
|
250
|
-
- app/views/rails_pulse/caches/show.html.erb
|
251
275
|
- app/views/rails_pulse/components/_breadcrumbs.html.erb
|
252
276
|
- app/views/rails_pulse/components/_code_panel.html.erb
|
277
|
+
- app/views/rails_pulse/components/_empty_state.html.erb
|
253
278
|
- app/views/rails_pulse/components/_metric_card.html.erb
|
254
279
|
- app/views/rails_pulse/components/_metric_row.html.erb
|
255
280
|
- app/views/rails_pulse/components/_operation_details_popover.html.erb
|
@@ -270,6 +295,10 @@ files:
|
|
270
295
|
- app/views/rails_pulse/operations/_operation_analysis_other.html.erb
|
271
296
|
- app/views/rails_pulse/operations/_operation_analysis_view.html.erb
|
272
297
|
- app/views/rails_pulse/operations/show.html.erb
|
298
|
+
- app/views/rails_pulse/queries/_analysis_error.html.erb
|
299
|
+
- app/views/rails_pulse/queries/_analysis_prompt.html.erb
|
300
|
+
- app/views/rails_pulse/queries/_analysis_results.html.erb
|
301
|
+
- app/views/rails_pulse/queries/_analysis_section.html.erb
|
273
302
|
- app/views/rails_pulse/queries/_show_table.html.erb
|
274
303
|
- app/views/rails_pulse/queries/_table.html.erb
|
275
304
|
- app/views/rails_pulse/queries/index.html.erb
|
@@ -289,21 +318,25 @@ files:
|
|
289
318
|
- config/initializers/rails_charts_csp_patch.rb
|
290
319
|
- config/initializers/rails_pulse.rb
|
291
320
|
- config/routes.rb
|
292
|
-
- db/migrate/
|
293
|
-
- db/migrate/
|
294
|
-
- db/
|
295
|
-
-
|
321
|
+
- db/migrate/20241222000001_create_rails_pulse_summaries.rb
|
322
|
+
- db/migrate/20250916031656_add_analysis_to_rails_pulse_queries.rb
|
323
|
+
- db/rails_pulse_schema.rb
|
324
|
+
- lib/generators/rails_pulse/convert_to_migrations_generator.rb
|
296
325
|
- lib/generators/rails_pulse/install_generator.rb
|
326
|
+
- lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb
|
327
|
+
- lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb
|
328
|
+
- lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb
|
297
329
|
- lib/generators/rails_pulse/templates/rails_pulse.rb
|
330
|
+
- lib/generators/rails_pulse/upgrade_generator.rb
|
298
331
|
- lib/rails_pulse.rb
|
299
332
|
- lib/rails_pulse/cleanup_service.rb
|
300
333
|
- lib/rails_pulse/configuration.rb
|
301
334
|
- lib/rails_pulse/engine.rb
|
302
335
|
- lib/rails_pulse/middleware/asset_server.rb
|
303
336
|
- lib/rails_pulse/middleware/request_collector.rb
|
304
|
-
- lib/rails_pulse/migration.rb
|
305
337
|
- lib/rails_pulse/subscribers/operation_subscriber.rb
|
306
338
|
- lib/rails_pulse/version.rb
|
339
|
+
- lib/tasks/rails_pulse.rake
|
307
340
|
- lib/tasks/rails_pulse_tasks.rake
|
308
341
|
- public/rails-pulse-assets/csp-test.js
|
309
342
|
- public/rails-pulse-assets/rails-pulse-icons.js
|
@@ -312,6 +345,7 @@ files:
|
|
312
345
|
- public/rails-pulse-assets/rails-pulse.css.map
|
313
346
|
- public/rails-pulse-assets/rails-pulse.js
|
314
347
|
- public/rails-pulse-assets/rails-pulse.js.map
|
348
|
+
- public/rails-pulse-assets/search.svg
|
315
349
|
homepage: https://www.railspulse.com
|
316
350
|
licenses:
|
317
351
|
- MIT
|
Binary file
|
Binary file
|
@@ -1,115 +0,0 @@
|
|
1
|
-
module RailsPulse
|
2
|
-
class CachesController < ApplicationController
|
3
|
-
def show
|
4
|
-
@component_id = params[:id]
|
5
|
-
@context = params[:context]
|
6
|
-
@cache_key = ComponentCacheKey.build(@component_id, @context)
|
7
|
-
|
8
|
-
# Preserve component options before refresh
|
9
|
-
existing_options = {}
|
10
|
-
if params[:refresh]
|
11
|
-
existing_cache = Rails.cache.read(@cache_key)
|
12
|
-
existing_options = existing_cache[:component_options] if existing_cache&.dig(:component_options)
|
13
|
-
Rails.cache.delete(@cache_key)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Check if cache exists with just options (from render_skeleton_with_frame)
|
17
|
-
cached_data = Rails.cache.read(@cache_key)
|
18
|
-
if cached_data && !cached_data[:component_data]
|
19
|
-
# Merge options with full data
|
20
|
-
cached_data = {
|
21
|
-
component_data: calculate_component_data,
|
22
|
-
cached_at: Time.current,
|
23
|
-
component_options: cached_data[:component_options] || {}
|
24
|
-
}
|
25
|
-
Rails.cache.write(@cache_key, cached_data, expires_in: ComponentCacheKey.cache_expires_in)
|
26
|
-
elsif !cached_data
|
27
|
-
# No cache exists, create new one (use preserved options if refreshing)
|
28
|
-
cached_data = {
|
29
|
-
component_data: calculate_component_data,
|
30
|
-
cached_at: Time.current,
|
31
|
-
component_options: existing_options
|
32
|
-
}
|
33
|
-
Rails.cache.write(@cache_key, cached_data, expires_in: ComponentCacheKey.cache_expires_in)
|
34
|
-
end
|
35
|
-
|
36
|
-
@component_data = cached_data[:component_data]
|
37
|
-
@cached_at = cached_data[:cached_at]
|
38
|
-
@component_options = cached_data[:component_options] || {}
|
39
|
-
|
40
|
-
# Update cached_at timestamp in component options if refresh action exists
|
41
|
-
if params[:refresh] && @component_options[:actions]
|
42
|
-
update_cached_at_in_actions(@component_options[:actions], @cached_at)
|
43
|
-
# Update the unified cache with new cached_at timestamp
|
44
|
-
cached_data[:component_options] = @component_options
|
45
|
-
Rails.cache.write(@cache_key, cached_data, expires_in: ComponentCacheKey.cache_expires_in)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def calculate_component_data
|
52
|
-
route = extract_route_from_context
|
53
|
-
query = extract_query_from_context
|
54
|
-
|
55
|
-
case @component_id
|
56
|
-
when "average_response_times"
|
57
|
-
Routes::Cards::AverageResponseTimes.new(route: route).to_metric_card
|
58
|
-
when "percentile_response_times"
|
59
|
-
Routes::Cards::PercentileResponseTimes.new(route: route).to_metric_card
|
60
|
-
when "request_count_totals"
|
61
|
-
Routes::Cards::RequestCountTotals.new(route: route).to_metric_card
|
62
|
-
when "error_rate_per_route"
|
63
|
-
Routes::Cards::ErrorRatePerRoute.new(route: route).to_metric_card
|
64
|
-
when "average_query_times"
|
65
|
-
Queries::Cards::AverageQueryTimes.new(query: query).to_metric_card
|
66
|
-
when "percentile_query_times"
|
67
|
-
Queries::Cards::PercentileQueryTimes.new(query: query).to_metric_card
|
68
|
-
when "execution_rate"
|
69
|
-
Queries::Cards::ExecutionRate.new(query: query).to_metric_card
|
70
|
-
when "dashboard_average_response_time"
|
71
|
-
Dashboard::Charts::AverageResponseTime.new.to_chart_data
|
72
|
-
when "dashboard_p95_response_time"
|
73
|
-
Dashboard::Charts::P95ResponseTime.new.to_chart_data
|
74
|
-
when "dashboard_slow_routes"
|
75
|
-
Dashboard::Tables::SlowRoutes.new.to_table_data
|
76
|
-
when "dashboard_slow_queries"
|
77
|
-
Dashboard::Tables::SlowQueries.new.to_table_data
|
78
|
-
else
|
79
|
-
{ title: "Unknown Metric", summary: "N/A" }
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def extract_route_from_context
|
84
|
-
return unless @context
|
85
|
-
|
86
|
-
# Extract route ID from context like "route_123" or return nil for "routes"/"requests"
|
87
|
-
if @context.match(/^route_(\d+)$/)
|
88
|
-
route_id = @context.match(/^route_(\d+)$/)[1]
|
89
|
-
Route.find(route_id)
|
90
|
-
else
|
91
|
-
nil
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def extract_query_from_context
|
96
|
-
return unless @context
|
97
|
-
|
98
|
-
# Extract query ID from context like "query_123" or return nil for other contexts
|
99
|
-
if @context.match(/^query_(\d+)$/)
|
100
|
-
query_id = @context.match(/^query_(\d+)$/)[1]
|
101
|
-
Query.find(query_id)
|
102
|
-
else
|
103
|
-
nil
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def update_cached_at_in_actions(actions, cached_at)
|
108
|
-
actions.each do |action|
|
109
|
-
if action.dig(:data, :rails_pulse__timezone_cached_at_value)
|
110
|
-
action[:data][:rails_pulse__timezone_cached_at_value] = cached_at.iso8601
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
module RailsPulse
|
2
|
-
module CachedComponentHelper
|
3
|
-
def cached_component(options)
|
4
|
-
# cache_key = ComponentCacheKey.build(options[:id], options[:context])
|
5
|
-
|
6
|
-
# Add refresh action for panels if requested
|
7
|
-
if options[:refresh_action] && options[:component] == "panel"
|
8
|
-
options[:actions] ||= []
|
9
|
-
options[:actions] << refresh_action_params(options[:id], options[:context], options[:content_partial])
|
10
|
-
end
|
11
|
-
|
12
|
-
# if Rails.cache.exist?(cache_key)
|
13
|
-
if false
|
14
|
-
render_cached_content(options)
|
15
|
-
else
|
16
|
-
render_skeleton_with_frame(options)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def render_cached_content(options)
|
23
|
-
cache_key = ComponentCacheKey.build(options[:id], options[:context])
|
24
|
-
cached_data = Rails.cache.read(cache_key)
|
25
|
-
@component_data = cached_data[:component_data]
|
26
|
-
@cached_at = cached_data[:cached_at]
|
27
|
-
component_options = cached_data[:component_options] || {}
|
28
|
-
|
29
|
-
# Wrap the cached content in a Turbo Frame so it can be refreshed using a refresh link in the component
|
30
|
-
turbo_frame_tag "#{options[:id]}_#{options[:component]}", class: options[:class] do
|
31
|
-
render "rails_pulse/components/#{options[:component]}", component_options
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def render_skeleton_with_frame(options)
|
36
|
-
# Store component options temporarily so CachesController can access them
|
37
|
-
cache_key = ComponentCacheKey.build(options[:id], options[:context])
|
38
|
-
Rails.cache.write(cache_key, component_options: options, expires_in: 5.minutes)
|
39
|
-
path_options = options.slice :id, :context
|
40
|
-
|
41
|
-
turbo_frame_tag "#{options[:id]}_#{options[:component]}",
|
42
|
-
src: rails_pulse.cache_path(**path_options),
|
43
|
-
loading: "eager",
|
44
|
-
class: options[:class] do
|
45
|
-
render "rails_pulse/skeletons/#{options[:component]}", options
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def refresh_action_params(id, context, content_partial)
|
50
|
-
refresh_params = {
|
51
|
-
id: id,
|
52
|
-
component_type: "panel",
|
53
|
-
refresh: true
|
54
|
-
}
|
55
|
-
|
56
|
-
# Include content_partial in refresh URL if available
|
57
|
-
refresh_params[:content_partial] = content_partial if content_partial
|
58
|
-
|
59
|
-
{
|
60
|
-
url: rails_pulse.cache_path(refresh_params),
|
61
|
-
icon: "refresh-cw",
|
62
|
-
title: "Refresh data",
|
63
|
-
data: {
|
64
|
-
controller: "rails-pulse--timezone",
|
65
|
-
rails_pulse__timezone_target_frame_value: "#{id}_panel",
|
66
|
-
rails_pulse__timezone_cached_at_value: Time.current.iso8601,
|
67
|
-
turbo_frame: "#{id}_panel",
|
68
|
-
turbo_prefetch: "false"
|
69
|
-
}
|
70
|
-
}
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus"
|
2
|
-
|
3
|
-
export default class extends Controller {
|
4
|
-
static targets = ["trigger", "details", "chevron"]
|
5
|
-
|
6
|
-
connect() {
|
7
|
-
// Ensure details row is initially hidden
|
8
|
-
this.detailsTarget.classList.add("hidden")
|
9
|
-
this.loaded = false
|
10
|
-
}
|
11
|
-
|
12
|
-
toggle(event) {
|
13
|
-
event.preventDefault()
|
14
|
-
event.stopPropagation()
|
15
|
-
|
16
|
-
const isExpanded = !this.detailsTarget.classList.contains("hidden")
|
17
|
-
console.log('Toggle clicked, currently expanded:', isExpanded)
|
18
|
-
|
19
|
-
console.log('isExpanded', isExpanded)
|
20
|
-
if (isExpanded) {
|
21
|
-
console.log('Collapsing...')
|
22
|
-
this.collapse()
|
23
|
-
} else {
|
24
|
-
console.log('Expanding...')
|
25
|
-
this.expand()
|
26
|
-
}
|
27
|
-
}
|
28
|
-
|
29
|
-
expand() {
|
30
|
-
// Show details row
|
31
|
-
this.detailsTarget.classList.remove("hidden")
|
32
|
-
|
33
|
-
// Rotate chevron to point down
|
34
|
-
this.chevronTarget.style.transform = "rotate(90deg)"
|
35
|
-
|
36
|
-
// Add expanded state class to trigger row
|
37
|
-
this.triggerTarget.classList.add("expanded")
|
38
|
-
|
39
|
-
// Load content lazily on first expansion
|
40
|
-
if (!this.loaded) {
|
41
|
-
this.loadOperationDetails()
|
42
|
-
this.loaded = true
|
43
|
-
}
|
44
|
-
}
|
45
|
-
|
46
|
-
loadOperationDetails() {
|
47
|
-
// Find the turbo frame and set its src to trigger loading
|
48
|
-
const turboFrame = this.detailsTarget.querySelector('turbo-frame')
|
49
|
-
if (turboFrame) {
|
50
|
-
const operationUrl = turboFrame.dataset.operationUrl
|
51
|
-
if (operationUrl) {
|
52
|
-
turboFrame.src = operationUrl
|
53
|
-
}
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
collapse() {
|
58
|
-
// Hide details row
|
59
|
-
this.detailsTarget.classList.add("hidden")
|
60
|
-
|
61
|
-
// Rotate chevron back to point right
|
62
|
-
this.chevronTarget.style.transform = "rotate(0deg)"
|
63
|
-
|
64
|
-
// Remove expanded state class from trigger row
|
65
|
-
this.triggerTarget.classList.remove("expanded")
|
66
|
-
}
|
67
|
-
}
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module RailsPulse
|
2
|
-
# Utility class for generating cache keys for cached components
|
3
|
-
class ComponentCacheKey
|
4
|
-
# Generate a cache key for a specific component type and context
|
5
|
-
#
|
6
|
-
# The cache key includes several parts:
|
7
|
-
# - A namespace prefix to avoid collisions with other cached data
|
8
|
-
# - The context (like "routes" or "route_123") to separate different views
|
9
|
-
# - The component type (like "average_response_times") to separate different components
|
10
|
-
# (Note: Time-based cache expiration is now handled via expires_in option)
|
11
|
-
def self.build(id, context = nil)
|
12
|
-
[ "rails_pulse_component", id, context ].compact
|
13
|
-
end
|
14
|
-
|
15
|
-
# Generate a cache expiration duration with jitter
|
16
|
-
#
|
17
|
-
# This returns a duration that can be used with the expires_in option.
|
18
|
-
# We add some randomness (jitter) so all components don't expire at exactly
|
19
|
-
# the same time, which would cause a "thundering herd" problem where all
|
20
|
-
# components recalculate simultaneously and overwhelm the database.
|
21
|
-
def self.cache_expires_in
|
22
|
-
# Get the configured cache duration (e.g., 5 minutes)
|
23
|
-
cache_duration = RailsPulse.configuration.component_cache_duration.to_i
|
24
|
-
|
25
|
-
# Add up to 25% random jitter to spread out cache expirations
|
26
|
-
max_jitter = (cache_duration * 0.25).to_i
|
27
|
-
jitter = rand(max_jitter)
|
28
|
-
|
29
|
-
# Return the base duration plus jitter
|
30
|
-
cache_duration + jitter
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,9 +0,0 @@
|
|
1
|
-
<%
|
2
|
-
id = "#{@component_id}_#{@component_options[:component]}"
|
3
|
-
%>
|
4
|
-
<%= turbo_frame_tag id do %>
|
5
|
-
<%= render partial: "rails_pulse/components/#{@component_options[:component]}", locals: @component_options %>
|
6
|
-
<script nonce="<%= rails_pulse_csp_nonce %>">
|
7
|
-
initializeChartsInContainer('<%= id %>');
|
8
|
-
</script>
|
9
|
-
<% end %>
|
@@ -1,12 +0,0 @@
|
|
1
|
-
class CreateRoutes < RailsPulse::Migration
|
2
|
-
def change
|
3
|
-
create_table :rails_pulse_routes do |t|
|
4
|
-
t.string :method, null: false, comment: "HTTP method (e.g., GET, POST)"
|
5
|
-
t.string :path, null: false, comment: "Request path (e.g., /posts/index)"
|
6
|
-
|
7
|
-
t.timestamps
|
8
|
-
end
|
9
|
-
|
10
|
-
add_index :rails_pulse_routes, [ :method, :path ], unique: true, name: 'index_rails_pulse_routes_on_method_and_path'
|
11
|
-
end
|
12
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class CreateRequests < RailsPulse::Migration
|
2
|
-
def change
|
3
|
-
create_table :rails_pulse_requests do |t|
|
4
|
-
t.references :route, null: false, foreign_key: { to_table: :rails_pulse_routes }, comment: "Link to the route"
|
5
|
-
t.decimal :duration, precision: 15, scale: 6, null: false, comment: "Total request duration in milliseconds"
|
6
|
-
t.integer :status, null: false, comment: "HTTP status code (e.g., 200, 500)"
|
7
|
-
t.boolean :is_error, null: false, default: false, comment: "True if status >= 500"
|
8
|
-
t.string :request_uuid, null: false, comment: "Unique identifier for the request (e.g., UUID)"
|
9
|
-
t.string :controller_action, comment: "Controller and action handling the request (e.g., PostsController#show)"
|
10
|
-
t.timestamp :occurred_at, null: false, comment: "When the request started"
|
11
|
-
|
12
|
-
t.timestamps
|
13
|
-
end
|
14
|
-
|
15
|
-
add_index :rails_pulse_requests, :occurred_at, name: 'index_rails_pulse_requests_on_occurred_at'
|
16
|
-
add_index :rails_pulse_requests, :request_uuid, unique: true, name: 'index_rails_pulse_requests_on_request_uuid'
|
17
|
-
add_index :rails_pulse_requests, [ :route_id, :occurred_at ], name: 'index_rails_pulse_requests_on_route_id_and_occurred_at'
|
18
|
-
end
|
19
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
class CreateQueries < RailsPulse::Migration
|
2
|
-
def change
|
3
|
-
create_table :rails_pulse_queries do |t|
|
4
|
-
# Use string with reasonable limit instead of text to avoid MySQL index length issues
|
5
|
-
# 1000 characters should be sufficient for most normalized SQL queries
|
6
|
-
t.string :normalized_sql, limit: 1000, null: false, comment: "Normalized SQL query string (e.g., SELECT * FROM users WHERE id = ?)"
|
7
|
-
|
8
|
-
t.timestamps
|
9
|
-
end
|
10
|
-
|
11
|
-
# Now using string instead of text, no need for database-specific logic
|
12
|
-
add_index :rails_pulse_queries, :normalized_sql, unique: true, name: "index_rails_pulse_queries_on_normalized_sql", length: 191
|
13
|
-
end
|
14
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
class CreateOperations < RailsPulse::Migration
|
2
|
-
def change
|
3
|
-
create_table :rails_pulse_operations do |t|
|
4
|
-
t.references :request, null: false, foreign_key: { to_table: :rails_pulse_requests }, comment: "Link to the request"
|
5
|
-
t.references :query, foreign_key: { to_table: :rails_pulse_queries }, index: true, comment: "Link to the normalized SQL query"
|
6
|
-
t.string :operation_type, null: false, comment: "Type of operation (e.g., database, view, gem_call)"
|
7
|
-
t.string :label, null: false, comment: "Descriptive name (e.g., SELECT FROM users WHERE id = 1, render layout)"
|
8
|
-
t.decimal :duration, precision: 15, scale: 6, null: false, comment: "Operation duration in milliseconds"
|
9
|
-
t.string :codebase_location, comment: "File and line number (e.g., app/models/user.rb:25)"
|
10
|
-
t.float :start_time, null: false, default: 0.0, comment: "Operation start time in milliseconds"
|
11
|
-
t.timestamp :occurred_at, null: false, comment: "When the request started"
|
12
|
-
|
13
|
-
t.timestamps
|
14
|
-
end
|
15
|
-
|
16
|
-
add_index :rails_pulse_operations, :operation_type, name: 'index_rails_pulse_operations_on_operation_type'
|
17
|
-
add_index :rails_pulse_operations, :occurred_at, name: 'index_rails_pulse_operations_on_occurred_at'
|
18
|
-
|
19
|
-
# Performance indexes for queries page optimization
|
20
|
-
add_index :rails_pulse_operations, [ :query_id, :occurred_at ], name: 'index_rails_pulse_operations_on_query_and_time'
|
21
|
-
add_index :rails_pulse_operations, [ :query_id, :duration, :occurred_at ], name: 'index_rails_pulse_operations_query_performance'
|
22
|
-
add_index :rails_pulse_operations, [ :occurred_at, :duration, :operation_type ], name: 'index_rails_pulse_operations_on_time_duration_type'
|
23
|
-
end
|
24
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module RailsPulse
|
2
|
-
# Determine the appropriate migration version based on Rails version
|
3
|
-
migration_version = case Rails.version
|
4
|
-
when /^8\./
|
5
|
-
8.0
|
6
|
-
when /^7\.2/
|
7
|
-
7.2
|
8
|
-
when /^7\.1/
|
9
|
-
7.1
|
10
|
-
when /^7\.0/
|
11
|
-
7.0
|
12
|
-
else
|
13
|
-
7.1 # Default fallback
|
14
|
-
end
|
15
|
-
|
16
|
-
class Migration < ActiveRecord::Migration[migration_version]
|
17
|
-
# This base migration class ensures that Rails Pulse migrations
|
18
|
-
# target the correct database when using multiple database configuration.
|
19
|
-
# The connection is determined by the connects_to configuration.
|
20
|
-
|
21
|
-
def connection
|
22
|
-
if RailsPulse.connects_to
|
23
|
-
RailsPulse::ApplicationRecord.connection
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|