rails_event_viewer 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +42 -0
- data/README.md +8 -0
- data/app/assets/javascripts/rails_event_viewer/application.js +2 -1
- data/app/assets/stylesheets/rails_event_viewer/application.css +1 -1
- data/app/controllers/rails_event_viewer/application_controller.rb +7 -16
- data/app/controllers/rails_event_viewer/events_controller.rb +16 -29
- data/app/controllers/rails_event_viewer/groups_controller.rb +0 -9
- data/app/helpers/rails_event_viewer/application_helper.rb +23 -77
- data/app/models/rails_event_viewer/entry.rb +0 -26
- data/app/models/rails_event_viewer/page.rb +9 -0
- data/app/views/layouts/rails_event_viewer/application.html.erb +11 -4
- data/app/views/rails_event_viewer/analytics/overview.html.erb +13 -9
- data/app/views/rails_event_viewer/dashboard/index.html.erb +16 -15
- data/app/views/rails_event_viewer/event_types/index.html.erb +7 -7
- data/app/views/rails_event_viewer/event_types/show.html.erb +18 -16
- data/app/views/rails_event_viewer/events/_event_row.html.erb +9 -9
- data/app/views/rails_event_viewer/events/_filters_sidebar.html.erb +18 -15
- data/app/views/rails_event_viewer/events/index.html.erb +10 -10
- data/app/views/rails_event_viewer/events/show.html.erb +28 -28
- data/app/views/rails_event_viewer/groups/index.html.erb +8 -8
- data/app/views/rails_event_viewer/groups/show.html.erb +12 -16
- data/app/views/rails_event_viewer/shared/_navigation.html.erb +13 -17
- data/app/views/rails_event_viewer/shared/_pagination.html.erb +10 -10
- data/app/views/rails_event_viewer/shared/_stat_card.html.erb +5 -5
- data/config/routes.rb +1 -5
- data/db/migrate/20261002121337_create_rails_event_viewer_entries.rb +0 -1
- data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb +0 -1
- data/lib/rails_event_viewer/adapter.rb +0 -8
- data/lib/rails_event_viewer/adapters/active_record.rb +12 -44
- data/lib/rails_event_viewer/adapters/concerns/in_memory_filtering.rb +7 -7
- data/lib/rails_event_viewer/adapters/memory.rb +0 -4
- data/lib/rails_event_viewer/adapters/null.rb +0 -8
- data/lib/rails_event_viewer/adapters/redis.rb +6 -20
- data/lib/rails_event_viewer/events_relation.rb +1 -1
- data/lib/rails_event_viewer/flusher.rb +5 -5
- data/lib/rails_event_viewer/logging.rb +1 -11
- data/lib/rails_event_viewer/version.rb +1 -1
- data/lib/rails_event_viewer.rb +5 -5
- data/lib/tasks/rails_event_viewer_tasks.rake +1 -1
- metadata +7 -2
- data/app/assets/stylesheets/rails_event_viewer/tailwind.css +0 -29
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
<path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
|
|
41
41
|
</svg>
|
|
42
42
|
<div class="ml-3">
|
|
43
|
-
<
|
|
43
|
+
<h2 class="text-sm font-medium text-yellow-800">No group keys configured</h2>
|
|
44
44
|
<p class="mt-1 text-sm text-yellow-700">
|
|
45
45
|
Configure group keys in your initializer:
|
|
46
46
|
<code class="bg-yellow-100 px-1 rounded">config.group_keys = [:request_id, :order_id]</code>
|
|
@@ -49,17 +49,17 @@
|
|
|
49
49
|
</div>
|
|
50
50
|
</div>
|
|
51
51
|
<% elsif @groups.any? %>
|
|
52
|
-
<div class="bg-white shadow rounded-lg overflow-
|
|
52
|
+
<div class="bg-white shadow rounded-lg overflow-x-auto">
|
|
53
53
|
<table class="min-w-full divide-y divide-gray-200">
|
|
54
54
|
<thead class="bg-gray-50">
|
|
55
55
|
<tr>
|
|
56
|
-
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
56
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
57
57
|
<%= @selected_key.to_s.titleize %>
|
|
58
58
|
</th>
|
|
59
|
-
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Events</th>
|
|
60
|
-
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">First Event</th>
|
|
61
|
-
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Event</th>
|
|
62
|
-
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Duration</th>
|
|
59
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Events</th>
|
|
60
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">First Event</th>
|
|
61
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Event</th>
|
|
62
|
+
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Duration</th>
|
|
63
63
|
</tr>
|
|
64
64
|
</thead>
|
|
65
65
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
107
107
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
108
108
|
</svg>
|
|
109
|
-
<
|
|
109
|
+
<h2 class="mt-2 text-sm font-medium text-gray-900">No groups found</h2>
|
|
110
110
|
<p class="mt-1 text-sm text-gray-500">
|
|
111
111
|
No events with <code class="bg-gray-100 px-1 rounded"><%= @selected_key %></code> in their <%= @source %> data.
|
|
112
112
|
</p>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<!-- Header -->
|
|
5
5
|
<div class="flex items-center justify-between">
|
|
6
6
|
<div class="flex items-center space-x-4">
|
|
7
|
-
<%= link_to rails_event_viewer.groups_path(key: @key, source: @source), class: "text-gray-
|
|
7
|
+
<%= link_to rails_event_viewer.groups_path(key: @key, source: @source), class: "text-gray-500 hover:text-gray-700", aria: { label: "Back to groups" } do %>
|
|
8
8
|
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
9
9
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
10
10
|
</svg>
|
|
@@ -19,15 +19,11 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
<!-- Stats -->
|
|
22
|
-
<div class="grid grid-cols-1 gap-5 sm:grid-cols-
|
|
22
|
+
<div class="grid grid-cols-1 gap-5 sm:grid-cols-3">
|
|
23
23
|
<%= render "rails_event_viewer/shared/stat_card",
|
|
24
24
|
title: "Total Events",
|
|
25
25
|
value: number_with_delimiter(@total_count),
|
|
26
26
|
icon: "database" %>
|
|
27
|
-
<%= render "rails_event_viewer/shared/stat_card",
|
|
28
|
-
title: "Event Types",
|
|
29
|
-
value: @event_names.size,
|
|
30
|
-
icon: "tag" %>
|
|
31
27
|
<%= render "rails_event_viewer/shared/stat_card",
|
|
32
28
|
title: "Duration",
|
|
33
29
|
value: @duration ? format_duration(@duration) : "-",
|
|
@@ -41,7 +37,7 @@
|
|
|
41
37
|
<!-- Timeline -->
|
|
42
38
|
<div class="bg-white shadow rounded-lg overflow-hidden">
|
|
43
39
|
<div class="px-6 py-4 border-b border-gray-200">
|
|
44
|
-
<
|
|
40
|
+
<h2 class="text-lg font-medium text-gray-900">Event Timeline</h2>
|
|
45
41
|
<% if @first_event_at && @last_event_at %>
|
|
46
42
|
<p class="text-sm text-gray-500">
|
|
47
43
|
<%= @first_event_at.strftime("%Y-%m-%d %H:%M:%S") %> to <%= @last_event_at.strftime("%Y-%m-%d %H:%M:%S") %>
|
|
@@ -55,13 +51,13 @@
|
|
|
55
51
|
|
|
56
52
|
<div class="divide-y divide-gray-100">
|
|
57
53
|
<% @events.each_with_index do |event, index| %>
|
|
58
|
-
<a href="<%= rails_event_viewer.event_path(
|
|
54
|
+
<a href="<%= rails_event_viewer.event_path(event[:id]) %>" class="relative pl-16 pr-6 py-4 hover:bg-gray-50 block">
|
|
59
55
|
<div class="absolute left-10 top-6 w-4 h-4 rounded-full border-2 border-indigo-500 bg-white"></div>
|
|
60
56
|
|
|
61
|
-
<% if @first_event_at &&
|
|
62
|
-
<% offset =
|
|
57
|
+
<% if @first_event_at && event[:occurred_at] %>
|
|
58
|
+
<% offset = event[:occurred_at] - @first_event_at %>
|
|
63
59
|
<div class="absolute left-0 top-4 w-9 text-right">
|
|
64
|
-
<span class="text-xs text-gray-
|
|
60
|
+
<span class="text-xs text-gray-500" title="<%= format_duration(offset) %> from start">
|
|
65
61
|
<% if offset < 0.001 %>
|
|
66
62
|
0
|
|
67
63
|
<% elsif offset < 1 %>
|
|
@@ -79,7 +75,7 @@
|
|
|
79
75
|
<div class="flex-1 min-w-0">
|
|
80
76
|
<!-- Event name badge -->
|
|
81
77
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800">
|
|
82
|
-
<%=
|
|
78
|
+
<%= event[:name] %>
|
|
83
79
|
</span>
|
|
84
80
|
|
|
85
81
|
<!-- Payload preview -->
|
|
@@ -110,11 +106,11 @@
|
|
|
110
106
|
|
|
111
107
|
<div class="ml-4 flex-shrink-0 text-right">
|
|
112
108
|
<p class="text-sm text-gray-500">
|
|
113
|
-
<%= time_ago_with_title(
|
|
109
|
+
<%= time_ago_with_title(event[:occurred_at]) %>
|
|
114
110
|
</p>
|
|
115
|
-
<% if
|
|
116
|
-
<p class="text-xs text-gray-
|
|
117
|
-
<%= event_short_filepath(event) %>:<%=
|
|
111
|
+
<% if event[:source_file].present? %>
|
|
112
|
+
<p class="text-xs text-gray-500 mt-1">
|
|
113
|
+
<%= event_short_filepath(event) %>:<%= event[:source_line] %>
|
|
118
114
|
</p>
|
|
119
115
|
<% end %>
|
|
120
116
|
</div>
|
|
@@ -1,44 +1,40 @@
|
|
|
1
1
|
<nav class="bg-indigo-600">
|
|
2
2
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
3
|
-
<div class="flex h-16 items-center justify-between">
|
|
3
|
+
<div class="flex min-h-16 flex-wrap items-center justify-between gap-y-2 py-2">
|
|
4
4
|
<div class="flex items-center">
|
|
5
5
|
<div class="flex-shrink-0">
|
|
6
6
|
<%= link_to rails_event_viewer.root_path, class: "text-white text-xl font-bold" do %>
|
|
7
7
|
Event Viewer
|
|
8
8
|
<% end %>
|
|
9
9
|
</div>
|
|
10
|
-
<div class="ml-10 flex items-baseline space-x-4">
|
|
11
|
-
<%=
|
|
12
|
-
|
|
13
|
-
<%=
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class: nav_link_class("groups") %>
|
|
19
|
-
<%= link_to "Analytics", rails_event_viewer.analytics_overview_path,
|
|
20
|
-
class: nav_link_class("analytics") %>
|
|
10
|
+
<div class="ml-10 flex flex-wrap items-baseline space-x-4">
|
|
11
|
+
<%= nav_link "Dashboard", rails_event_viewer.root_path, "dashboard" %>
|
|
12
|
+
<%= nav_link "Events", rails_event_viewer.events_path, "events" %>
|
|
13
|
+
<%= nav_link "Event Types", rails_event_viewer.event_types_path, "event_types" %>
|
|
14
|
+
<%= nav_link "Groups", rails_event_viewer.groups_path, "groups" %>
|
|
15
|
+
<% if analytics_supported? %>
|
|
16
|
+
<%= nav_link "Analytics", rails_event_viewer.analytics_overview_path, "analytics" %>
|
|
17
|
+
<% end %>
|
|
21
18
|
</div>
|
|
22
19
|
</div>
|
|
23
20
|
|
|
24
21
|
<div class="flex items-center space-x-4">
|
|
25
|
-
|
|
26
|
-
<%= form_with url: rails_event_viewer.search_events_path, method: :get, class: "relative", data: { turbo_frame: "_top" } do |f| %>
|
|
22
|
+
<%= form_with url: rails_event_viewer.events_path, method: :get, class: "relative" do |f| %>
|
|
27
23
|
<div class="relative">
|
|
28
24
|
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
|
29
|
-
<svg class="h-4 w-4 text-indigo-200" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
25
|
+
<svg class="h-4 w-4 text-indigo-200" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
30
26
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
31
27
|
</svg>
|
|
32
28
|
</div>
|
|
33
29
|
<%= f.search_field :q,
|
|
34
30
|
value: params[:q],
|
|
35
31
|
placeholder: "Search events...",
|
|
32
|
+
aria: { label: "Search events" },
|
|
36
33
|
class: "w-64 rounded-md bg-indigo-500 py-2 pl-10 pr-3 text-sm text-white placeholder-indigo-200 focus:bg-white focus:text-gray-900 focus:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-white" %>
|
|
37
34
|
</div>
|
|
38
35
|
<% end %>
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
<div class="text-indigo-200 text-sm" title="Current timezone">
|
|
37
|
+
<div class="text-indigo-200 text-sm" title="Times are shown in this time zone">
|
|
42
38
|
<%= Time.zone.name %>
|
|
43
39
|
</div>
|
|
44
40
|
</div>
|
|
@@ -15,41 +15,41 @@
|
|
|
15
15
|
<nav class="flex items-center space-x-1" aria-label="Pagination">
|
|
16
16
|
<% if pagination.previous %>
|
|
17
17
|
<%= link_to page_path(pagination.previous), class: "relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" do %>
|
|
18
|
-
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
18
|
+
<svg aria-hidden="true" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
19
19
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
20
20
|
</svg>
|
|
21
21
|
<span class="ml-1">Prev</span>
|
|
22
22
|
<% end %>
|
|
23
23
|
<% else %>
|
|
24
24
|
<span class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-300 bg-white border border-gray-200 rounded-md cursor-not-allowed">
|
|
25
|
-
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
25
|
+
<svg aria-hidden="true" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
26
26
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
|
27
27
|
</svg>
|
|
28
28
|
<span class="ml-1">Prev</span>
|
|
29
29
|
</span>
|
|
30
30
|
<% end %>
|
|
31
31
|
|
|
32
|
-
<%
|
|
33
|
-
<% if page_num ==
|
|
34
|
-
<span class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-indigo-600 border border-indigo-600 rounded-md"><%= page_num %></span>
|
|
35
|
-
<% elsif page_num == 1 || page_num == pagination.pages || (page_num >= pagination.page - 2 && page_num <= pagination.page + 2) %>
|
|
36
|
-
<%= link_to page_num, page_path(page_num), class: "relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" %>
|
|
37
|
-
<% elsif page_num == pagination.page - 3 || page_num == pagination.page + 3 %>
|
|
32
|
+
<% pagination.window.each do |page_num| %>
|
|
33
|
+
<% if page_num == :gap %>
|
|
38
34
|
<span class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-700">...</span>
|
|
35
|
+
<% elsif page_num == pagination.page %>
|
|
36
|
+
<span class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-white bg-indigo-600 border border-indigo-600 rounded-md" aria-current="page"><%= page_num %></span>
|
|
37
|
+
<% else %>
|
|
38
|
+
<%= link_to page_num, page_path(page_num), class: "relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" %>
|
|
39
39
|
<% end %>
|
|
40
40
|
<% end %>
|
|
41
41
|
|
|
42
42
|
<% if pagination.next %>
|
|
43
43
|
<%= link_to page_path(pagination.next), class: "relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50" do %>
|
|
44
44
|
<span class="mr-1">Next</span>
|
|
45
|
-
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
45
|
+
<svg aria-hidden="true" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
46
46
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
47
47
|
</svg>
|
|
48
48
|
<% end %>
|
|
49
49
|
<% else %>
|
|
50
50
|
<span class="relative inline-flex items-center px-3 py-2 text-sm font-medium text-gray-300 bg-white border border-gray-200 rounded-md cursor-not-allowed">
|
|
51
51
|
<span class="mr-1">Next</span>
|
|
52
|
-
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
52
|
+
<svg aria-hidden="true" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
53
53
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
54
54
|
</svg>
|
|
55
55
|
</span>
|
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
<div class="flex-shrink-0">
|
|
5
5
|
<% case icon %>
|
|
6
6
|
<% when "database" %>
|
|
7
|
-
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
7
|
+
<svg aria-hidden="true" class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
8
8
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
|
9
9
|
</svg>
|
|
10
10
|
<% when "calendar" %>
|
|
11
|
-
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
11
|
+
<svg aria-hidden="true" class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
12
12
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
13
13
|
</svg>
|
|
14
14
|
<% when "tag" %>
|
|
15
|
-
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
15
|
+
<svg aria-hidden="true" class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
16
16
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
|
17
17
|
</svg>
|
|
18
18
|
<% when "clock" %>
|
|
19
|
-
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
19
|
+
<svg aria-hidden="true" class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
20
20
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
21
21
|
</svg>
|
|
22
22
|
<% else %>
|
|
23
|
-
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
23
|
+
<svg aria-hidden="true" class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
24
24
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
25
25
|
</svg>
|
|
26
26
|
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -18,7 +18,6 @@ class CreateRailsEventViewerEntries < ActiveRecord::Migration[8.1]
|
|
|
18
18
|
t.timestamps
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
add_index :rails_event_viewer_entries, :name
|
|
22
21
|
add_index :rails_event_viewer_entries, :occurred_at
|
|
23
22
|
add_index :rails_event_viewer_entries, [:name, :occurred_at]
|
|
24
23
|
|
data/lib/generators/rails_event_viewer/install/templates/create_rails_event_viewer_entries.rb.erb
CHANGED
|
@@ -17,7 +17,6 @@ class CreateRailsEventViewerEntries < ActiveRecord::Migration<%= migration_versi
|
|
|
17
17
|
t.timestamps
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
add_index :rails_event_viewer_entries, :name
|
|
21
20
|
add_index :rails_event_viewer_entries, :occurred_at
|
|
22
21
|
add_index :rails_event_viewer_entries, [:name, :occurred_at]
|
|
23
22
|
|
|
@@ -10,10 +10,6 @@ module RailsEventViewer
|
|
|
10
10
|
true
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def supports_retention?
|
|
14
|
-
true
|
|
15
|
-
end
|
|
16
|
-
|
|
17
13
|
def write_events(events)
|
|
18
14
|
raise NotImplementedError, "#{self.class} must implement #write_events"
|
|
19
15
|
end
|
|
@@ -59,10 +55,6 @@ module RailsEventViewer
|
|
|
59
55
|
end.sort_by { |s| -s[:count] }
|
|
60
56
|
end
|
|
61
57
|
|
|
62
|
-
def distinct_group_values(key, source: :context)
|
|
63
|
-
raise NotImplementedError, "#{self.class} must implement #distinct_group_values"
|
|
64
|
-
end
|
|
65
|
-
|
|
66
58
|
def group_instances(key, source: :context, limit: 100)
|
|
67
59
|
raise NotImplementedError, "#{self.class} must implement #group_instances"
|
|
68
60
|
end
|
|
@@ -3,10 +3,6 @@ module RailsEventViewer
|
|
|
3
3
|
class ActiveRecord
|
|
4
4
|
include Adapter
|
|
5
5
|
|
|
6
|
-
def initialize(**options)
|
|
7
|
-
@options = options
|
|
8
|
-
end
|
|
9
|
-
|
|
10
6
|
def table_exists?
|
|
11
7
|
Entry.table_exists?
|
|
12
8
|
rescue ::ActiveRecord::NoDatabaseError
|
|
@@ -16,7 +12,6 @@ module RailsEventViewer
|
|
|
16
12
|
def write_events(events)
|
|
17
13
|
return if events.empty?
|
|
18
14
|
|
|
19
|
-
# Use insert_all for bulk inserts (more efficient)
|
|
20
15
|
records = events.map do |event|
|
|
21
16
|
{
|
|
22
17
|
name: event[:name],
|
|
@@ -32,7 +27,7 @@ module RailsEventViewer
|
|
|
32
27
|
}
|
|
33
28
|
end
|
|
34
29
|
|
|
35
|
-
Entry.insert_all(records)
|
|
30
|
+
Entry.insert_all(records, returning: false)
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
def fetch_events(relation)
|
|
@@ -57,6 +52,10 @@ module RailsEventViewer
|
|
|
57
52
|
Entry.where("occurred_at < ?", timestamp).in_batches.delete_all
|
|
58
53
|
end
|
|
59
54
|
|
|
55
|
+
def clear!
|
|
56
|
+
Entry.in_batches.delete_all
|
|
57
|
+
end
|
|
58
|
+
|
|
60
59
|
def events_over_time(range:, interval:)
|
|
61
60
|
Entry.where(occurred_at: range).group_by_period(interval, :occurred_at).count
|
|
62
61
|
end
|
|
@@ -84,16 +83,6 @@ module RailsEventViewer
|
|
|
84
83
|
end
|
|
85
84
|
end
|
|
86
85
|
|
|
87
|
-
def distinct_group_values(key, source: :context)
|
|
88
|
-
column = source == :tags ? :tags : :context
|
|
89
|
-
json_path = JsonQuery.extract_path(column, key)
|
|
90
|
-
|
|
91
|
-
Entry
|
|
92
|
-
.where(JsonQuery.extract_path_present(column, key))
|
|
93
|
-
.distinct
|
|
94
|
-
.pluck(Arel.sql(json_path))
|
|
95
|
-
end
|
|
96
|
-
|
|
97
86
|
def group_instances(key, source: :context, limit: 100)
|
|
98
87
|
column = source == :tags ? :tags : :context
|
|
99
88
|
json_path = JsonQuery.extract_path(column, key)
|
|
@@ -144,39 +133,18 @@ module RailsEventViewer
|
|
|
144
133
|
end
|
|
145
134
|
|
|
146
135
|
def build_scope(relation)
|
|
147
|
-
scope = Entry.order(occurred_at: :desc)
|
|
148
|
-
scope =
|
|
149
|
-
scope =
|
|
150
|
-
scope =
|
|
136
|
+
scope = Entry.order(occurred_at: :desc, id: :desc)
|
|
137
|
+
scope = scope.where(name: relation.names) if relation.names.present?
|
|
138
|
+
scope = apply_json_filters(scope, :tags, relation.tags)
|
|
139
|
+
scope = apply_json_filters(scope, :context, relation.contexts)
|
|
151
140
|
scope = apply_time_filters(scope, relation)
|
|
152
141
|
scope = apply_search(scope, relation)
|
|
153
142
|
scope
|
|
154
143
|
end
|
|
155
144
|
|
|
156
|
-
def
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if relation.names.size == 1
|
|
160
|
-
scope.where(name: relation.names.first)
|
|
161
|
-
else
|
|
162
|
-
scope.where(name: relation.names)
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def apply_tag_filters(scope, relation)
|
|
167
|
-
return scope if relation.tags.blank?
|
|
168
|
-
|
|
169
|
-
relation.tags.each do |key, value|
|
|
170
|
-
scope = scope.where(*JsonQuery.contains(:tags, key, value))
|
|
171
|
-
end
|
|
172
|
-
scope
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
def apply_context_filters(scope, relation)
|
|
176
|
-
return scope if relation.contexts.blank?
|
|
177
|
-
|
|
178
|
-
relation.contexts.each do |key, value|
|
|
179
|
-
scope = scope.where(*JsonQuery.contains(:context, key, value))
|
|
145
|
+
def apply_json_filters(scope, column, filters)
|
|
146
|
+
filters.each do |key, value|
|
|
147
|
+
scope = scope.where(*JsonQuery.contains(column, key, value))
|
|
180
148
|
end
|
|
181
149
|
scope
|
|
182
150
|
end
|
|
@@ -6,13 +6,6 @@ module RailsEventViewer
|
|
|
6
6
|
module InMemoryFiltering
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
8
|
|
|
9
|
-
def distinct_group_values(key, source: :context)
|
|
10
|
-
in_memory_events
|
|
11
|
-
.map { |e| group_value_for(e, key, source) }
|
|
12
|
-
.compact
|
|
13
|
-
.uniq
|
|
14
|
-
end
|
|
15
|
-
|
|
16
9
|
def group_instances(key, source: :context, limit: 100)
|
|
17
10
|
in_memory_events
|
|
18
11
|
.group_by { |e| group_value_for(e, key, source).to_s }
|
|
@@ -41,6 +34,13 @@ module RailsEventViewer
|
|
|
41
34
|
(limit ? counts.first(limit) : counts).to_h
|
|
42
35
|
end
|
|
43
36
|
|
|
37
|
+
def event_type_statistics
|
|
38
|
+
statistics = in_memory_events.group_by { |e| e[:name] }.map do |name, events|
|
|
39
|
+
{ name: name, count: events.size, last_event_at: events.filter_map { |e| e[:occurred_at] }.max }
|
|
40
|
+
end
|
|
41
|
+
statistics.sort_by { |statistic| -statistic[:count] }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
44
|
private
|
|
45
45
|
|
|
46
46
|
def events_in(range)
|
|
@@ -11,10 +11,6 @@ module RailsEventViewer
|
|
|
11
11
|
false
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def supports_retention?
|
|
15
|
-
false
|
|
16
|
-
end
|
|
17
|
-
|
|
18
14
|
def write_events(events)
|
|
19
15
|
nil
|
|
20
16
|
end
|
|
@@ -51,10 +47,6 @@ module RailsEventViewer
|
|
|
51
47
|
0
|
|
52
48
|
end
|
|
53
49
|
|
|
54
|
-
def distinct_group_values(key, source: :context)
|
|
55
|
-
[]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
50
|
def group_instances(key, source: :context, limit: 100)
|
|
59
51
|
[]
|
|
60
52
|
end
|
|
@@ -26,10 +26,6 @@ module RailsEventViewer
|
|
|
26
26
|
@max_events = max_events
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def supports_analytics?
|
|
30
|
-
true
|
|
31
|
-
end
|
|
32
|
-
|
|
33
29
|
def write_events(events)
|
|
34
30
|
return if events.empty?
|
|
35
31
|
|
|
@@ -57,7 +53,7 @@ module RailsEventViewer
|
|
|
57
53
|
|
|
58
54
|
def fetch_events(relation)
|
|
59
55
|
if relation.filtered?
|
|
60
|
-
return
|
|
56
|
+
return filter_events(in_memory_events, relation).drop(relation.offset_value).take(relation.limit_value)
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
start_idx = relation.offset_value
|
|
@@ -68,7 +64,7 @@ module RailsEventViewer
|
|
|
68
64
|
|
|
69
65
|
def count_events(relation)
|
|
70
66
|
if relation.filtered?
|
|
71
|
-
|
|
67
|
+
filter_events(in_memory_events, relation).size
|
|
72
68
|
else
|
|
73
69
|
@redis.with { |conn| conn.zcard(events_key) }
|
|
74
70
|
end
|
|
@@ -99,7 +95,7 @@ module RailsEventViewer
|
|
|
99
95
|
def delete_before(timestamp)
|
|
100
96
|
score = (timestamp.to_f * 1_000_000).to_i
|
|
101
97
|
@redis.with do |conn|
|
|
102
|
-
deleted = conn.zremrangebyscore(events_key, "-inf", score)
|
|
98
|
+
deleted = conn.zremrangebyscore(events_key, "-inf", "(#{score}")
|
|
103
99
|
prune_names(conn)
|
|
104
100
|
deleted
|
|
105
101
|
end
|
|
@@ -155,8 +151,8 @@ module RailsEventViewer
|
|
|
155
151
|
|
|
156
152
|
def serialize_event(event, timestamp_usec = nil)
|
|
157
153
|
timestamp_usec ||= (event[:occurred_at].to_f * 1_000_000).to_i
|
|
158
|
-
|
|
159
|
-
JSON.generate(
|
|
154
|
+
stored = event.merge(id: "#{timestamp_usec}-#{SecureRandom.hex(8)}", occurred_at: event[:occurred_at]&.iso8601(6))
|
|
155
|
+
JSON.generate(stored)
|
|
160
156
|
end
|
|
161
157
|
|
|
162
158
|
def deserialize_event(json)
|
|
@@ -171,18 +167,8 @@ module RailsEventViewer
|
|
|
171
167
|
data
|
|
172
168
|
end
|
|
173
169
|
|
|
174
|
-
def fetch_all_events
|
|
175
|
-
serialized_events = @redis.with { |conn| conn.zrevrange(events_key, 0, -1) }
|
|
176
|
-
serialized_events.map { |json| deserialize_event(json) }
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def fetch_all_filtered(relation)
|
|
180
|
-
events = fetch_all_events
|
|
181
|
-
filter_events(events, relation)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
170
|
def in_memory_events
|
|
185
|
-
|
|
171
|
+
@redis.with { |conn| conn.zrevrange(events_key, 0, -1) }.map { |json| deserialize_event(json) }
|
|
186
172
|
end
|
|
187
173
|
end
|
|
188
174
|
end
|
|
@@ -18,7 +18,7 @@ module RailsEventViewer
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def with_name(*event_names)
|
|
21
|
-
event_names = event_names.flatten.compact
|
|
21
|
+
event_names = event_names.flatten.compact.map(&:to_s)
|
|
22
22
|
return self if event_names.empty?
|
|
23
23
|
|
|
24
24
|
clone_with { |rel| rel.names.concat(event_names) }
|
|
@@ -56,7 +56,7 @@ module RailsEventViewer
|
|
|
56
56
|
if @thread&.alive?
|
|
57
57
|
@flush_requests << true
|
|
58
58
|
@thread.join(shutdown_timeout)
|
|
59
|
-
effective_logger
|
|
59
|
+
RailsEventViewer.effective_logger.warn("[RailsEventViewer] Flusher thread still busy after #{shutdown_timeout}s writing #{@writing} events") if @thread.alive?
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
@mutex.synchronize { @thread = nil }
|
|
@@ -72,7 +72,7 @@ module RailsEventViewer
|
|
|
72
72
|
|
|
73
73
|
report_dropped_events
|
|
74
74
|
leftover = @buffer.size
|
|
75
|
-
effective_logger
|
|
75
|
+
RailsEventViewer.effective_logger.warn("[RailsEventViewer] #{leftover} events still buffered at shutdown") if leftover.positive?
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def running?
|
|
@@ -126,7 +126,7 @@ module RailsEventViewer
|
|
|
126
126
|
end
|
|
127
127
|
return if dropped.zero?
|
|
128
128
|
|
|
129
|
-
effective_logger
|
|
129
|
+
RailsEventViewer.effective_logger.warn("[RailsEventViewer] Dropped #{dropped} events because the buffer was full")
|
|
130
130
|
end
|
|
131
131
|
|
|
132
132
|
def request_flush
|
|
@@ -143,7 +143,7 @@ module RailsEventViewer
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def run_loop
|
|
146
|
-
effective_logger
|
|
146
|
+
RailsEventViewer.effective_logger.debug("[RailsEventViewer] Flusher thread started (PID: #{Process.pid})")
|
|
147
147
|
|
|
148
148
|
until shutdown_requested?
|
|
149
149
|
begin
|
|
@@ -157,7 +157,7 @@ module RailsEventViewer
|
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
-
effective_logger
|
|
160
|
+
RailsEventViewer.effective_logger.debug("[RailsEventViewer] Flusher thread stopped (PID: #{Process.pid})")
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def wait_for_flush
|
|
@@ -3,17 +3,7 @@ module RailsEventViewer
|
|
|
3
3
|
private
|
|
4
4
|
|
|
5
5
|
def log_error(message)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if logger
|
|
9
|
-
logger.error(message)
|
|
10
|
-
else
|
|
11
|
-
warn message
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def effective_logger
|
|
16
|
-
RailsEventViewer.logger || (defined?(Rails) && Rails.logger)
|
|
6
|
+
RailsEventViewer.effective_logger.error(message)
|
|
17
7
|
end
|
|
18
8
|
end
|
|
19
9
|
end
|