karafka-web 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.
Files changed (178) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +3 -0
  3. data/.coditsu/ci.yml +3 -0
  4. data/.diffend.yml +3 -0
  5. data/.github/FUNDING.yml +1 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.github/workflows/ci.yml +49 -0
  9. data/.gitignore +69 -0
  10. data/.ruby-gemset +1 -0
  11. data/.ruby-version +1 -0
  12. data/CHANGELOG.md +9 -0
  13. data/CODE_OF_CONDUCT.md +46 -0
  14. data/Gemfile +7 -0
  15. data/Gemfile.lock +52 -0
  16. data/LICENSE +17 -0
  17. data/README.md +29 -0
  18. data/bin/karafka-web +33 -0
  19. data/certs/cert_chain.pem +26 -0
  20. data/config/locales/errors.yml +9 -0
  21. data/karafka-web.gemspec +44 -0
  22. data/lib/karafka/web/app.rb +17 -0
  23. data/lib/karafka/web/config.rb +80 -0
  24. data/lib/karafka/web/deserializer.rb +20 -0
  25. data/lib/karafka/web/errors.rb +25 -0
  26. data/lib/karafka/web/installer.rb +124 -0
  27. data/lib/karafka/web/processing/consumer.rb +66 -0
  28. data/lib/karafka/web/processing/consumers/aggregator.rb +130 -0
  29. data/lib/karafka/web/processing/consumers/state.rb +32 -0
  30. data/lib/karafka/web/tracking/base_contract.rb +31 -0
  31. data/lib/karafka/web/tracking/consumers/contracts/consumer_group.rb +33 -0
  32. data/lib/karafka/web/tracking/consumers/contracts/job.rb +26 -0
  33. data/lib/karafka/web/tracking/consumers/contracts/partition.rb +22 -0
  34. data/lib/karafka/web/tracking/consumers/contracts/report.rb +95 -0
  35. data/lib/karafka/web/tracking/consumers/contracts/topic.rb +29 -0
  36. data/lib/karafka/web/tracking/consumers/listeners/base.rb +33 -0
  37. data/lib/karafka/web/tracking/consumers/listeners/errors.rb +107 -0
  38. data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +45 -0
  39. data/lib/karafka/web/tracking/consumers/listeners/processing.rb +157 -0
  40. data/lib/karafka/web/tracking/consumers/listeners/statistics.rb +123 -0
  41. data/lib/karafka/web/tracking/consumers/listeners/status.rb +58 -0
  42. data/lib/karafka/web/tracking/consumers/sampler.rb +216 -0
  43. data/lib/karafka/web/tracking/memoized_shell.rb +48 -0
  44. data/lib/karafka/web/tracking/reporter.rb +144 -0
  45. data/lib/karafka/web/tracking/ttl_array.rb +59 -0
  46. data/lib/karafka/web/tracking/ttl_hash.rb +16 -0
  47. data/lib/karafka/web/ui/app.rb +78 -0
  48. data/lib/karafka/web/ui/base.rb +77 -0
  49. data/lib/karafka/web/ui/controllers/base.rb +40 -0
  50. data/lib/karafka/web/ui/controllers/become_pro.rb +17 -0
  51. data/lib/karafka/web/ui/controllers/cluster.rb +24 -0
  52. data/lib/karafka/web/ui/controllers/consumers.rb +27 -0
  53. data/lib/karafka/web/ui/controllers/errors.rb +43 -0
  54. data/lib/karafka/web/ui/controllers/jobs.rb +33 -0
  55. data/lib/karafka/web/ui/controllers/requests/params.rb +30 -0
  56. data/lib/karafka/web/ui/controllers/responses/data.rb +26 -0
  57. data/lib/karafka/web/ui/controllers/routing.rb +30 -0
  58. data/lib/karafka/web/ui/helpers/application_helper.rb +144 -0
  59. data/lib/karafka/web/ui/lib/hash_proxy.rb +66 -0
  60. data/lib/karafka/web/ui/lib/paginate_array.rb +38 -0
  61. data/lib/karafka/web/ui/models/consumer_group.rb +20 -0
  62. data/lib/karafka/web/ui/models/health.rb +44 -0
  63. data/lib/karafka/web/ui/models/job.rb +13 -0
  64. data/lib/karafka/web/ui/models/message.rb +99 -0
  65. data/lib/karafka/web/ui/models/partition.rb +13 -0
  66. data/lib/karafka/web/ui/models/process.rb +56 -0
  67. data/lib/karafka/web/ui/models/processes.rb +86 -0
  68. data/lib/karafka/web/ui/models/state.rb +67 -0
  69. data/lib/karafka/web/ui/models/topic.rb +19 -0
  70. data/lib/karafka/web/ui/pro/app.rb +120 -0
  71. data/lib/karafka/web/ui/pro/controllers/cluster.rb +16 -0
  72. data/lib/karafka/web/ui/pro/controllers/consumers.rb +54 -0
  73. data/lib/karafka/web/ui/pro/controllers/dlq.rb +44 -0
  74. data/lib/karafka/web/ui/pro/controllers/errors.rb +57 -0
  75. data/lib/karafka/web/ui/pro/controllers/explorer.rb +79 -0
  76. data/lib/karafka/web/ui/pro/controllers/health.rb +33 -0
  77. data/lib/karafka/web/ui/pro/controllers/jobs.rb +26 -0
  78. data/lib/karafka/web/ui/pro/controllers/routing.rb +26 -0
  79. data/lib/karafka/web/ui/pro/views/consumers/_breadcrumbs.erb +27 -0
  80. data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +60 -0
  81. data/lib/karafka/web/ui/pro/views/consumers/_counters.erb +50 -0
  82. data/lib/karafka/web/ui/pro/views/consumers/_summary.erb +81 -0
  83. data/lib/karafka/web/ui/pro/views/consumers/consumer/_consumer_group.erb +109 -0
  84. data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +26 -0
  85. data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +126 -0
  86. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +9 -0
  87. data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_subscriptions.erb +9 -0
  88. data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +32 -0
  89. data/lib/karafka/web/ui/pro/views/consumers/consumer/_stopped.erb +10 -0
  90. data/lib/karafka/web/ui/pro/views/consumers/consumer/_tabs.erb +20 -0
  91. data/lib/karafka/web/ui/pro/views/consumers/index.erb +30 -0
  92. data/lib/karafka/web/ui/pro/views/consumers/jobs.erb +42 -0
  93. data/lib/karafka/web/ui/pro/views/consumers/subscriptions.erb +23 -0
  94. data/lib/karafka/web/ui/pro/views/dlq/_breadcrumbs.erb +5 -0
  95. data/lib/karafka/web/ui/pro/views/dlq/_no_topics.erb +9 -0
  96. data/lib/karafka/web/ui/pro/views/dlq/_topic.erb +12 -0
  97. data/lib/karafka/web/ui/pro/views/dlq/index.erb +16 -0
  98. data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +25 -0
  99. data/lib/karafka/web/ui/pro/views/errors/_detail.erb +29 -0
  100. data/lib/karafka/web/ui/pro/views/errors/_error.erb +26 -0
  101. data/lib/karafka/web/ui/pro/views/errors/_partition_option.erb +7 -0
  102. data/lib/karafka/web/ui/pro/views/errors/index.erb +58 -0
  103. data/lib/karafka/web/ui/pro/views/errors/show.erb +56 -0
  104. data/lib/karafka/web/ui/pro/views/explorer/_breadcrumbs.erb +29 -0
  105. data/lib/karafka/web/ui/pro/views/explorer/_detail.erb +21 -0
  106. data/lib/karafka/web/ui/pro/views/explorer/_encryption_enabled.erb +18 -0
  107. data/lib/karafka/web/ui/pro/views/explorer/_failed_deserialization.erb +4 -0
  108. data/lib/karafka/web/ui/pro/views/explorer/_message.erb +16 -0
  109. data/lib/karafka/web/ui/pro/views/explorer/_partition_option.erb +7 -0
  110. data/lib/karafka/web/ui/pro/views/explorer/_topic.erb +12 -0
  111. data/lib/karafka/web/ui/pro/views/explorer/index.erb +17 -0
  112. data/lib/karafka/web/ui/pro/views/explorer/partition.erb +56 -0
  113. data/lib/karafka/web/ui/pro/views/explorer/show.erb +65 -0
  114. data/lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb +5 -0
  115. data/lib/karafka/web/ui/pro/views/health/_partition.erb +35 -0
  116. data/lib/karafka/web/ui/pro/views/health/index.erb +60 -0
  117. data/lib/karafka/web/ui/pro/views/jobs/_breadcrumbs.erb +5 -0
  118. data/lib/karafka/web/ui/pro/views/jobs/_job.erb +31 -0
  119. data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +9 -0
  120. data/lib/karafka/web/ui/pro/views/jobs/index.erb +34 -0
  121. data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +57 -0
  122. data/lib/karafka/web/ui/public/images/favicon.ico +0 -0
  123. data/lib/karafka/web/ui/public/images/logo.svg +28 -0
  124. data/lib/karafka/web/ui/public/javascripts/application.js +41 -0
  125. data/lib/karafka/web/ui/public/javascripts/bootstrap.min.js +7 -0
  126. data/lib/karafka/web/ui/public/javascripts/highlight.min.js +337 -0
  127. data/lib/karafka/web/ui/public/javascripts/live_poll.js +124 -0
  128. data/lib/karafka/web/ui/public/javascripts/timeago.min.js +1 -0
  129. data/lib/karafka/web/ui/public/stylesheets/application.css +106 -0
  130. data/lib/karafka/web/ui/public/stylesheets/bootstrap.min.css +7 -0
  131. data/lib/karafka/web/ui/public/stylesheets/bootstrap.min.css.map +1 -0
  132. data/lib/karafka/web/ui/public/stylesheets/highlight.min.css +10 -0
  133. data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +5 -0
  134. data/lib/karafka/web/ui/views/cluster/_broker.erb +5 -0
  135. data/lib/karafka/web/ui/views/cluster/_partition.erb +22 -0
  136. data/lib/karafka/web/ui/views/cluster/index.erb +72 -0
  137. data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +27 -0
  138. data/lib/karafka/web/ui/views/consumers/_consumer.erb +43 -0
  139. data/lib/karafka/web/ui/views/consumers/_counters.erb +44 -0
  140. data/lib/karafka/web/ui/views/consumers/_summary.erb +81 -0
  141. data/lib/karafka/web/ui/views/consumers/consumer/_consumer_group.erb +109 -0
  142. data/lib/karafka/web/ui/views/consumers/consumer/_job.erb +26 -0
  143. data/lib/karafka/web/ui/views/consumers/consumer/_metrics.erb +126 -0
  144. data/lib/karafka/web/ui/views/consumers/consumer/_no_jobs.erb +9 -0
  145. data/lib/karafka/web/ui/views/consumers/consumer/_no_subscriptions.erb +9 -0
  146. data/lib/karafka/web/ui/views/consumers/consumer/_partition.erb +32 -0
  147. data/lib/karafka/web/ui/views/consumers/consumer/_stopped.erb +10 -0
  148. data/lib/karafka/web/ui/views/consumers/consumer/_tabs.erb +20 -0
  149. data/lib/karafka/web/ui/views/consumers/index.erb +29 -0
  150. data/lib/karafka/web/ui/views/errors/_breadcrumbs.erb +19 -0
  151. data/lib/karafka/web/ui/views/errors/_detail.erb +29 -0
  152. data/lib/karafka/web/ui/views/errors/_error.erb +26 -0
  153. data/lib/karafka/web/ui/views/errors/index.erb +38 -0
  154. data/lib/karafka/web/ui/views/errors/show.erb +30 -0
  155. data/lib/karafka/web/ui/views/jobs/_breadcrumbs.erb +5 -0
  156. data/lib/karafka/web/ui/views/jobs/_job.erb +22 -0
  157. data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +9 -0
  158. data/lib/karafka/web/ui/views/jobs/index.erb +31 -0
  159. data/lib/karafka/web/ui/views/layout.erb +23 -0
  160. data/lib/karafka/web/ui/views/routing/_breadcrumbs.erb +15 -0
  161. data/lib/karafka/web/ui/views/routing/_consumer_group.erb +34 -0
  162. data/lib/karafka/web/ui/views/routing/_detail.erb +25 -0
  163. data/lib/karafka/web/ui/views/routing/_topic.erb +18 -0
  164. data/lib/karafka/web/ui/views/routing/index.erb +10 -0
  165. data/lib/karafka/web/ui/views/routing/show.erb +26 -0
  166. data/lib/karafka/web/ui/views/shared/_become_pro.erb +13 -0
  167. data/lib/karafka/web/ui/views/shared/_brand.erb +3 -0
  168. data/lib/karafka/web/ui/views/shared/_content.erb +31 -0
  169. data/lib/karafka/web/ui/views/shared/_header.erb +20 -0
  170. data/lib/karafka/web/ui/views/shared/_navigation.erb +57 -0
  171. data/lib/karafka/web/ui/views/shared/_pagination.erb +21 -0
  172. data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +39 -0
  173. data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +52 -0
  174. data/lib/karafka/web/version.rb +8 -0
  175. data/lib/karafka/web.rb +60 -0
  176. data.tar.gz.sig +0 -0
  177. metadata +328 -0
  178. metadata.gz.sig +0 -0
@@ -0,0 +1,9 @@
1
+ <div class="container mb-4">
2
+ <div class="row">
3
+ <div class="col-lg-12">
4
+ <div class="alert alert-info" role="alert">
5
+ No Dead Letter Queue topics exist in Kafka.
6
+ </div>
7
+ </div>
8
+ </div>
9
+ </div>
@@ -0,0 +1,12 @@
1
+ <div class="col">
2
+ <div class="card" >
3
+ <div class="card-body p-2">
4
+ <p class="card-text mb-0 p-2">
5
+ <a href="<%= root_path('explorer', topic[:topic_name], 0) %>">
6
+ <%= topic[:topic_name] %> /
7
+ <%= topic[:partition_count] %>
8
+ </a>
9
+ </p>
10
+ </div>
11
+ </div>
12
+ </div>
@@ -0,0 +1,16 @@
1
+ <%== view_title('Dead Letter Queue topics', hr: true) %>
2
+
3
+ <% if @dlq_topics.empty? %>
4
+ <%== partial 'dlq/no_topics' %>
5
+ <% else %>
6
+ <div class="container">
7
+ <div class="row mb-5 row-cols-1 row-cols-md-4 g-4">
8
+ <%==
9
+ each_partial(
10
+ @dlq_topics,
11
+ 'dlq/topic'
12
+ )
13
+ %>
14
+ </div>
15
+ </div>
16
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <li class="breadcrumb-item">
2
+ <a href="<%= root_path('errors', 0) %>">
3
+ Errors
4
+ </a>
5
+ </li>
6
+
7
+ <li class="breadcrumb-item">
8
+ <a href="<%= root_path('errors', @partition_id) %>">
9
+ Partition <%= @partition_id %>
10
+ </a>
11
+ </li>
12
+
13
+ <% if @offset %>
14
+ <li class="breadcrumb-item">
15
+ <a href="<%= root_path('errors', @partition_id, @offset) %>">
16
+ <%=
17
+ type = @error_message.payload[:type]
18
+ error_class = @error_message.payload[:error_class]
19
+ offset = @error_message.offset
20
+
21
+ "#{type}: #{error_class} #{offset}"
22
+ %>
23
+ </a>
24
+ </li>
25
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <% if v.is_a?(Hash) %>
2
+ <% v.each do |k2, v2| %>
3
+ <tr>
4
+ <td>
5
+ <%= "#{k}.#{k2}" %>
6
+ </td>
7
+ <td>
8
+ <% if k2.to_s.include?('ssl') %>
9
+ ***
10
+ <% else %>
11
+ <%= v2 %>
12
+ <% end %>
13
+ </td>
14
+ </tr>
15
+ <% end %>
16
+ <% else %>
17
+ <tr>
18
+ <td>
19
+ <%= k %>
20
+ </td>
21
+ <td>
22
+ <% if k == :occurred_at %>
23
+ <%== relative_time v %>
24
+ <% else %>
25
+ <%= v %>
26
+ <% end %>
27
+ </td>
28
+ </tr>
29
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% error = error_msg.payload %>
2
+
3
+ <tr>
4
+ <td>
5
+ <% if error[:details].key?(:topic) %>
6
+ <%= error[:details][:topic] %>: <%= error[:details][:partition] %>
7
+ <% else %>
8
+ <%= error[:type] %>
9
+ <% end %>
10
+ </td>
11
+ <td>
12
+ <%== error[:process_name] %>
13
+ </td>
14
+ <td>
15
+ <%= error[:error_class] %>:
16
+ <%= error[:error_message].first(200) %>
17
+ </td>
18
+ <td>
19
+ <%== relative_time error[:occurred_at] %>
20
+ </td>
21
+ <td>
22
+ <a href="<%= root_path('errors', error_msg.partition, error_msg.offset) %>" class="btn btn-sm btn-secondary text-white">
23
+ Details
24
+ </a>
25
+ </td>
26
+ </tr>
@@ -0,0 +1,7 @@
1
+ <% if @partition_id == partition %>
2
+ <option value="<%= root_path('errors', partition) %>" selected=selected>
3
+ <% else %>
4
+ <option value="<%= root_path('errors', partition) %>">
5
+ <% end %>
6
+ <%= partition %>
7
+ </option>
@@ -0,0 +1,58 @@
1
+ <div class="container mb-5">
2
+ <div class="row">
3
+ <div class="col">
4
+ <h3>
5
+ Errors
6
+ </h3>
7
+ </div>
8
+
9
+ <div class="col">
10
+ <div class="col-auto text-end">
11
+ <label class="col-form-label">Partition</label>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="col pt-1 mb-0 pb-0">
16
+ <div class="col-auto">
17
+ <select class="form-select form-select-sm mb-0 form-control" id="current-partition">
18
+ <%==
19
+ each_partial(
20
+ @partitions_count.times.to_a,
21
+ 'errors/partition_option',
22
+ local: :partition
23
+ )
24
+ %>
25
+ </select>
26
+ </div>
27
+ </div>
28
+ </div>
29
+
30
+ <hr>
31
+ </div>
32
+
33
+ <div class="container">
34
+ <div class="row mb-5">
35
+ <div class="col-sm-12">
36
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
37
+ <thead>
38
+ <tr class="align-middle">
39
+ <th>Origin</th>
40
+ <th>Process name</th>
41
+ <th>Error</th>
42
+ <th>Occurred at</th>
43
+ <th></th>
44
+ </tr>
45
+ </thead>
46
+ <tbody>
47
+ <%==
48
+ each_partial(
49
+ @error_messages,
50
+ 'errors/error',
51
+ local: :error_msg
52
+ )
53
+ %>
54
+ </tbody>
55
+ </table>
56
+ </div>
57
+ </div>
58
+ </div>
@@ -0,0 +1,56 @@
1
+ <%==
2
+ type = @error_message.payload[:type]
3
+ error_class = @error_message.payload[:error_class]
4
+ offset = @error_message.offset
5
+
6
+ view_title("#{type}: #{error_class} #{offset}")
7
+ %>
8
+
9
+ <div class="container">
10
+ <div class="row mb-4">
11
+ <div class="col-sm-12">
12
+ <h5 class="mb-2">
13
+ Metadata
14
+ </h5>
15
+ <hr/>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="row mb-5">
20
+ <div class="col-sm-12 table-responsive">
21
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
22
+ <tbody>
23
+ <% @error_message.payload.each do |k, v| %>
24
+ <% next if k == :backtrace %>
25
+ <%==
26
+ partial(
27
+ 'errors/detail',
28
+ locals: {
29
+ k: k,
30
+ v: v
31
+ }
32
+ )
33
+ %>
34
+ <% end %>
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ </div>
39
+ <div class="row mb-4">
40
+ <div class="col-sm-12">
41
+ <h5 class="mb-2">
42
+ Backtrace
43
+ </h5>
44
+ <hr/>
45
+ </div>
46
+ </div>
47
+ <div class="row mb-4">
48
+ <div class="col-sm-12">
49
+ <div class="card">
50
+ <div class="card-body">
51
+ <pre class="m-0 p-0"><code class="wrapped json p-0 m-0"><%= @error_message.payload[:backtrace] %></code></pre>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
@@ -0,0 +1,29 @@
1
+ <li class="breadcrumb-item">
2
+ <a href="<%= root_path('explorer') %>">
3
+ Explorer
4
+ </a>
5
+ </li>
6
+
7
+ <% if @topic_id %>
8
+ <li class="breadcrumb-item">
9
+ <a href="<%= root_path('explorer', @topic_id, 0) %>">
10
+ <%= @topic_id %>
11
+ </a>
12
+ </li>
13
+ <% end %>
14
+
15
+ <% if @partition_id %>
16
+ <li class="breadcrumb-item">
17
+ <a href="<%= root_path('explorer', @topic_id, @partition_id) %>">
18
+ Partition <%= @partition_id %>
19
+ </a>
20
+ </li>
21
+ <% end %>
22
+
23
+ <% if @offset %>
24
+ <li class="breadcrumb-item active">
25
+ <a href="<%= root_path('explorer', @topic_id, @partition_id, @offset) %>">
26
+ <%= @offset %>
27
+ </a>
28
+ </li>
29
+ <% end %>
@@ -0,0 +1,21 @@
1
+ <% if v.is_a?(Hash) && !v.empty? %>
2
+ <% v.each do |k2,v2| %>
3
+ <tr class="align-middle">
4
+ <td>
5
+ <%= "#{k}.#{k2}" %>
6
+ </td>
7
+ <td>
8
+ <%= v2 %>
9
+ </td>
10
+ </tr>
11
+ <% end %>
12
+ <% else %>
13
+ <tr>
14
+ <td>
15
+ <%= k %>
16
+ </td>
17
+ <td>
18
+ <%= object_value_to_s(v) %>
19
+ </td>
20
+ </tr>
21
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <div class="row">
2
+ <div class="col-sm-12">
3
+ <div class="alert alert-warning" role="alert">
4
+ <h4 class="alert-heading">
5
+ The payload cannot be displayed.
6
+ </h4>
7
+ <p>
8
+ Encryption is enabled.
9
+ </p>
10
+ <hr>
11
+ <p class="mb-0">
12
+ When encryption is enabled, the payload is not visible via the web UI.
13
+
14
+ Please set the <code>ui.decrypt</code> config option to <code>true</code> if you want the payload to be visible here.
15
+ </p>
16
+ </div>
17
+ </div>
18
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="alert alert-warning" role="alert">
2
+ We could not deserialize the data due to the following error:
3
+ <code><%= @payload_error.class %></code>. Raw payload displayed instead.
4
+ </div>
@@ -0,0 +1,16 @@
1
+ <tr>
2
+ <td>
3
+ <%= message.offset %>
4
+ </td>
5
+ <td>
6
+ <%== relative_time message.timestamp %>
7
+ </td>
8
+ <td>
9
+ <%= message.key %>
10
+ </td>
11
+ <td class="text-center">
12
+ <a href="<%= root_path('explorer', message.topic, message.partition, message.offset) %>" class="btn btn-sm btn-secondary">
13
+ Details
14
+ </a>
15
+ </td>
16
+ </tr>
@@ -0,0 +1,7 @@
1
+ <% if @partition_id == partition %>
2
+ <option value="<%= root_path('explorer', @topic_id, partition) %>" selected=selected>
3
+ <% else %>
4
+ <option value="<%= root_path('explorer', @topic_id, partition) %>">
5
+ <% end %>
6
+ <%= partition %>
7
+ </option>
@@ -0,0 +1,12 @@
1
+ <div class="col">
2
+ <div class="card" >
3
+ <div class="card-body p-2">
4
+ <p class="card-text mb-0 p-2">
5
+ <a href="<%= root_path('explorer', topic[:topic_name], 0) %>">
6
+ <%= topic[:topic_name] %> /
7
+ <%= topic[:partition_count] %>
8
+ </a>
9
+ </p>
10
+ </div>
11
+ </div>
12
+ </div>
@@ -0,0 +1,17 @@
1
+ <div class="container mb-5">
2
+ <div class="row">
3
+ <h3>Explorer</h3>
4
+ </div>
5
+ <hr>
6
+ </div>
7
+
8
+ <div class="container">
9
+ <div class="row mb-5 row-cols-1 row-cols-md-4 g-4">
10
+ <%==
11
+ each_partial(
12
+ @topics,
13
+ 'explorer/topic'
14
+ )
15
+ %>
16
+ </div>
17
+ </div>
@@ -0,0 +1,56 @@
1
+ <div class="container mb-5">
2
+ <div class="row">
3
+ <div class="col">
4
+ <h3>
5
+ <%= @topic_id %>
6
+ </h3>
7
+ </div>
8
+
9
+ <div class="col">
10
+ <div class="col-auto text-end">
11
+ <label class="col-form-label">Partition</label>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="col pt-1 mb-0 pb-0">
16
+ <div class="col-auto">
17
+ <select class="form-select form-select-sm mb-0 form-control" id="current-partition">
18
+ <%==
19
+ each_partial(
20
+ @partitions_count.times.to_a,
21
+ 'explorer/partition_option',
22
+ local: :partition
23
+ )
24
+ %>
25
+ </select>
26
+ </div>
27
+ </div>
28
+ </div>
29
+
30
+ <hr>
31
+ </div>
32
+
33
+ <div class="container">
34
+ <div class="row mb-5">
35
+ <div class="col-sm-12">
36
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
37
+ <thead>
38
+ <tr class="align-middle">
39
+ <th>Offset</th>
40
+ <th>Timestamp</th>
41
+ <th>Key</th>
42
+ <th></th>
43
+ </tr>
44
+ </thead>
45
+ <tbody>
46
+ <%==
47
+ each_partial(
48
+ @messages,
49
+ 'explorer/message'
50
+ )
51
+ %>
52
+ </tbody>
53
+ </table>
54
+ </div>
55
+ </div>
56
+ </div>
@@ -0,0 +1,65 @@
1
+ <div class="container">
2
+ <div class="row mb-4">
3
+ <div class="col-sm-12">
4
+ <h5 class="mb-2">
5
+ Metadata
6
+ </h5>
7
+ <hr/>
8
+
9
+ </div>
10
+ </div>
11
+
12
+ <div class="row mb-5">
13
+ <div class="col-sm-12">
14
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
15
+ <tbody>
16
+ <% @message.metadata.to_h.except(:received_at).each do |k, v| %>
17
+ <%==
18
+ partial(
19
+ 'explorer/detail',
20
+ locals: {
21
+ k: k,
22
+ v: v
23
+ }
24
+ )
25
+ %>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ </div>
30
+ </div>
31
+ </div>
32
+
33
+ <div class="container">
34
+ <div class="row mb-4">
35
+ <div class="col-sm-12">
36
+ <h5 class="mb-2">
37
+ Payload
38
+ </h5>
39
+ <hr/>
40
+
41
+ </div>
42
+ </div>
43
+
44
+ <% if @decrypt %>
45
+ <div class="row">
46
+ <div class="col-sm-12">
47
+ <% if @payload_error %>
48
+ <%== partial 'explorer/failed_deserialization' %>
49
+ <% end %>
50
+
51
+ <div class="card">
52
+ <div class="card-body">
53
+ <% if @payload_error %>
54
+ <pre class="m-0 p-0"><code class="wrapped json p-0 m-0"><%= @message.raw_payload %></code></pre>
55
+ <% else %>
56
+ <pre class="m-0 p-0"><code class="wrapped json p-0 m-0"><%= @pretty_payload %></code></pre>
57
+ <% end %>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ <% else %>
63
+ <%== partial 'explorer/encryption_enabled' %>
64
+ <% end %>
65
+ </div>
@@ -0,0 +1,5 @@
1
+ <li class="breadcrumb-item">
2
+ <a href="<%= root_path('health') %>">
3
+ Consumers groups health
4
+ </a>
5
+ </li>
@@ -0,0 +1,35 @@
1
+ <tr class="align-middle status-row-<%= details[:process].status %>">
2
+ <td>
3
+ <%= topic_name %>
4
+ </td>
5
+ <td>
6
+ <%= partition_id %>
7
+ </td>
8
+ <td><%= details[:lag_stored] %></td>
9
+ <td>
10
+ <span class="badge <%= lag_trend_bg(details[:lag_stored_d]) %>">
11
+ <%= details[:lag_stored_d] %>
12
+ </span>
13
+ </td>
14
+ <td>
15
+ <%= details[:committed_offset] %>
16
+ </td>
17
+ <td>
18
+ <%= details[:stored_offset] %>
19
+ </td>
20
+ <td>
21
+ <span class="badge <%= kafka_state_bg(details[:fetch_state]) %> mt-1 mb-1">
22
+ <%= details[:fetch_state] %>
23
+ </span>
24
+ </td>
25
+ <td>
26
+ <span class="badge <%= kafka_state_bg(details[:poll_state]) %> mt-1 mb-1">
27
+ <%= details[:poll_state] %>
28
+ </span>
29
+ </td>
30
+ <td>
31
+ <a href="<%= root_path('consumers', details[:process].id, 'subscriptions') %>">
32
+ <%= details[:process].name %>
33
+ </a>
34
+ </td>
35
+ </tr>
@@ -0,0 +1,60 @@
1
+ <%== view_title('Consumers groups health') %>
2
+
3
+ <% if @stats.empty? %>
4
+ <div class="container mb-4">
5
+ <div class="row">
6
+ <div class="col-lg-12">
7
+ <div class="alert alert-info" role="alert">
8
+ No health data is available. It may mean no processes are running.
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ <% end %>
14
+
15
+ <% @stats.each do |cg_name, details| %>
16
+ <div class="container mb-5">
17
+ <div class="row mb-3">
18
+ <div class="col-sm-12">
19
+ <h4 class="mb-4"><%= cg_name %></h4>
20
+ <hr/>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="row mb-5">
25
+ <div class="col-sm-12">
26
+ <table class="processes bg-white table table-hover table-bordered table-striped mb-0 align-middle">
27
+ <thead>
28
+ <tr class="align-middle">
29
+ <th class="align-middle">Topic</th>
30
+ <th>Partition</th>
31
+ <th>Lag stored</th>
32
+ <th>Lag trend</th>
33
+ <th>Committed offset</th>
34
+ <th>Stored offset</th>
35
+ <th>Fetch state</th>
36
+ <th>Poll state</th>
37
+ <th>Process name</th>
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ <% details.sort_by(&:first).each do |topic_name, partitions| %>
42
+ <% partitions.sort_by(&:first).each do |partition_id, details| %>
43
+ <%==
44
+ partial(
45
+ 'health/partition',
46
+ locals: {
47
+ topic_name: topic_name,
48
+ partition_id: partition_id,
49
+ details: details
50
+ }
51
+ )
52
+ %>
53
+ <% end %>
54
+ <% end %>
55
+ </tbody>
56
+ </table>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <li class="breadcrumb-item">
2
+ <a href="<%= root_path('jobs') %>">
3
+ Running jobs
4
+ </a>
5
+ </li>
@@ -0,0 +1,31 @@
1
+ <tr>
2
+ <td>
3
+ <a href="<%= root_path('consumers', job.process.id, 'subscriptions') %>">
4
+ <%= job.process.name %>
5
+ </a>
6
+ </td>
7
+ <td>
8
+ <span class="badge bg-secondary badge-topic" title="Consumer group: <%= job.consumer_group %>">
9
+ <%= job.topic %>:
10
+ <%= job.partition %>
11
+ </span>
12
+ </td>
13
+ <td>
14
+ <code><%= job.consumer %></code>
15
+ </td>
16
+ <td>
17
+ <code>#<%= job.type %></code>
18
+ </td>
19
+ <td>
20
+ <%= job.first_offset %>
21
+ </td>
22
+ <td>
23
+ <%= job.last_offset %>
24
+ </td>
25
+ <td>
26
+ <%= job.comitted_offset %>
27
+ </td>
28
+ <td>
29
+ <%== relative_time job.started_at %>
30
+ </td>
31
+ </tr>
@@ -0,0 +1,9 @@
1
+ <div class="container mb-4">
2
+ <div class="row">
3
+ <div class="col-lg-12">
4
+ <div class="alert alert-info" role="alert">
5
+ There are no running jobs at the moment.
6
+ </div>
7
+ </div>
8
+ </div>
9
+ </div>