karafka-web 0.2.2 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7619f1c461afdd16646dfe0d7fff92badad1448de8d40afcc41b42af4ac37dfa
4
- data.tar.gz: c706ae9212b87947630be27d366d0d8a938b2307d8e2e969d7dbddad6488ab4b
3
+ metadata.gz: b03937826882f0a85bbd1718f2b09411148ec9647ab46d0d9a9701c278a07b66
4
+ data.tar.gz: 257bbf154f1d19aaaecd7150105ecd7233aa33f8bd42c4195a938c215061220b
5
5
  SHA512:
6
- metadata.gz: 1ee9e33f8a8a358b45ff1e8ad707252eaecc0d3801194ce313e45c3cb0c54e6d8fadf905fac3dc809fbb276a12adc9d29a91285e0f63f6ba09d97803b1c4f198
7
- data.tar.gz: 67a4d2902138aa73689b2c1ccd2f1a76c7d17993ff2584f29ab57a50b1893c01157239d37d7b125b07fce18556354bb88dfb6d87099e0d61cabb7bf5e8ddfaa6
6
+ metadata.gz: 1aed9e94d037cd9bdaa07e55f96281f83dc18ebe151ec4a7f936e1592ac9d530a45fbb45dbd7c5d843692ede58bbca5d1d81a63974e222b0649cd0aa6bd8a9b6
7
+ data.tar.gz: 4ce6deaac3737bb8d25952158ac2b022621187bb8b0d42f952261b831d664e21cf5e12ff15bbccc054927a0b9dd000f75361270adeb3cb6708df93144b848d08
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Karafka Web changelog
2
2
 
3
+ ## 0.2.4 (2023-03-14)
4
+ - [Improvement] Paginate topics list in cluster info on every 100 partitions.
5
+ - [Improvement] Provide current page in the pagination.
6
+ - [Improvement] Report usage of Karafka Pro on the status page view.
7
+ - [Fix] Add missing three months limit on errors storage.
8
+ - [Maintenance] Exclude Karafka Web UI topics from declarative topics.
9
+
10
+ ## 0.2.3 (2023-03-04)
11
+ - [Improvement] Snapshot current consumer tags upon consumer errors.
12
+ - [Improvement] Optimize exception message extraction from errors.
13
+ - [Improvement] Slightly change error reporting structure (backwards compatible) to collect process tags on errors and to align with other reports.
14
+
3
15
  ## 0.2.2 (2023-02-25)
4
16
  - [Fix] Fix status page reference in Pro.
5
17
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-web (0.2.2)
4
+ karafka-web (0.2.4)
5
5
  erubi (~> 1.4)
6
- karafka (>= 2.0.33, < 3.0.0)
6
+ karafka (>= 2.0.35, < 3.0.0)
7
7
  karafka-core (>= 2.0.12, < 3.0.0)
8
8
  roda (~> 3.63)
9
9
  tilt (~> 2.0)
@@ -17,7 +17,7 @@ GEM
17
17
  minitest (>= 5.1)
18
18
  tzinfo (~> 2.0)
19
19
  byebug (11.1.3)
20
- concurrent-ruby (1.2.0)
20
+ concurrent-ruby (1.2.2)
21
21
  diff-lcs (1.5.0)
22
22
  docile (1.4.0)
23
23
  erubi (1.12.0)
@@ -26,7 +26,7 @@ GEM
26
26
  ffi (1.15.5)
27
27
  i18n (1.12.0)
28
28
  concurrent-ruby (~> 1.0)
29
- karafka (2.0.33)
29
+ karafka (2.0.35)
30
30
  karafka-core (>= 2.0.12, < 3.0.0)
31
31
  thor (>= 0.20)
32
32
  waterdrop (>= 2.4.10, < 3.0.0)
@@ -70,13 +70,14 @@ GEM
70
70
  tilt (2.1.0)
71
71
  tzinfo (2.0.6)
72
72
  concurrent-ruby (~> 1.0)
73
- waterdrop (2.4.10)
74
- karafka-core (>= 2.0.9, < 3.0.0)
73
+ waterdrop (2.4.11)
74
+ karafka-core (>= 2.0.12, < 3.0.0)
75
75
  zeitwerk (~> 2.3)
76
76
  webrick (1.8.1)
77
77
  zeitwerk (2.6.7)
78
78
 
79
79
  PLATFORMS
80
+ arm64-darwin-21
80
81
  x86_64-linux
81
82
 
82
83
  DEPENDENCIES
@@ -88,4 +89,4 @@ DEPENDENCIES
88
89
  simplecov
89
90
 
90
91
  BUNDLED WITH
91
- 2.4.6
92
+ 2.4.7
data/karafka-web.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.licenses = %w[LGPL-3.0 Commercial]
18
18
 
19
19
  spec.add_dependency 'erubi', '~> 1.4'
20
- spec.add_dependency 'karafka', '>= 2.0.33', '< 3.0.0'
20
+ spec.add_dependency 'karafka', '>= 2.0.35', '< 3.0.0'
21
21
  spec.add_dependency 'karafka-core', '>= 2.0.12', '< 3.0.0'
22
22
  spec.add_dependency 'roda', '~> 3.63'
23
23
  spec.add_dependency 'tilt', '~> 2.0'
@@ -45,6 +45,7 @@ module Karafka
45
45
  consumer_group ::Karafka::Web.config.processing.consumer_group do
46
46
  # Topic we listen on to materialize the states
47
47
  topic ::Karafka::Web.config.topics.consumers.reports do
48
+ config(active: false)
48
49
  active ::Karafka::Web.config.processing.active
49
50
  # Since we materialize state in intervals, we can poll for half of this time without
50
51
  # impacting the reporting responsiveness
@@ -58,11 +59,13 @@ module Karafka
58
59
  # We define those two here without consumption, so Web understands how to deserialize
59
60
  # them when used / viewed
60
61
  topic ::Karafka::Web.config.topics.consumers.states do
62
+ config(active: false)
61
63
  active false
62
64
  deserializer web_deserializer
63
65
  end
64
66
 
65
67
  topic ::Karafka::Web.config.topics.errors do
68
+ config(active: false)
66
69
  active false
67
70
  deserializer web_deserializer
68
71
  end
@@ -126,7 +129,9 @@ module Karafka
126
129
  ::Karafka::Admin.create_topic(
127
130
  errors_topic,
128
131
  1,
129
- replication_factor
132
+ replication_factor,
133
+ # Remove really old errors (older than 3 months just to preserve space)
134
+ { 'retention.ms': 3 * 31 * 24 * 60 * 60 * 1_000 }
130
135
  )
131
136
  end
132
137
  end
@@ -28,7 +28,8 @@ module Karafka
28
28
  error_message: error_message,
29
29
  backtrace: backtrace,
30
30
  details: details,
31
- occurred_at: float_now
31
+ occurred_at: float_now,
32
+ process: sampler.to_report[:process].slice(:name, :tags)
32
33
  }
33
34
 
34
35
  sampler.counters[:errors] += 1
@@ -55,6 +56,13 @@ module Karafka
55
56
 
56
57
  private
57
58
 
59
+ # @return [Object] sampler for the metrics
60
+ # @note We use this sampler to get basic process details that we want to assign
61
+ # to the error
62
+ def consumer_sampler
63
+ @consumer_sampler ||= ::Karafka::Web.config.tracking.consumers.sampler
64
+ end
65
+
58
66
  # @param consumer [::Karafka::BaseConsumer]
59
67
  # @return [Hash] hash with consumer specific info for details of error
60
68
  def extract_consumer_info(consumer)
@@ -65,7 +73,8 @@ module Karafka
65
73
  first_offset: consumer.messages.first.offset,
66
74
  last_offset: consumer.messages.last.offset,
67
75
  comitted_offset: consumer.coordinator.seek_offset - 1,
68
- consumer: consumer.class.to_s
76
+ consumer: consumer.class.to_s,
77
+ tags: consumer.tags
69
78
  }
70
79
  end
71
80
 
@@ -74,12 +83,6 @@ module Karafka
74
83
  # @param error [StandardError] error that occurred
75
84
  # @return [Array<String, String, String>] array with error name, message and backtrace
76
85
  def extract_error_info(error)
77
- error_message = error.message.to_s.dup
78
- error_message.force_encoding('utf-8')
79
- error_message.scrub!
80
-
81
- backtrace = (error.backtrace || [])
82
-
83
86
  app_root = "#{::Karafka.root}/"
84
87
 
85
88
  gem_home = if ENV.key?('GEM_HOME')
@@ -90,15 +93,27 @@ module Karafka
90
93
 
91
94
  gem_home = "#{gem_home}/"
92
95
 
96
+ backtrace = error.backtrace || []
93
97
  backtrace.map! { |line| line.gsub(app_root, '') }
94
98
  backtrace.map! { |line| line.gsub(gem_home, '') }
95
99
 
96
100
  [
97
101
  error.class.name,
98
- error_message,
102
+ extract_exception_message(error),
99
103
  backtrace.join("\n")
100
104
  ]
101
105
  end
106
+
107
+ # @param error [StandardError] error that occurred
108
+ # @return [String] formatted exception message
109
+ def extract_exception_message(error)
110
+ error_message = error.message.to_s[0, 10_000]
111
+ error_message.force_encoding('utf-8')
112
+ error_message.scrub! if error_message.respond_to?(:scrub!)
113
+ error_message
114
+ rescue StandardError
115
+ '!!! Error message extraction failed !!!'
116
+ end
102
117
  end
103
118
  end
104
119
  end
@@ -60,7 +60,7 @@ module Karafka
60
60
  started_at: @started_at,
61
61
  name: process_name,
62
62
  status: ::Karafka::App.config.internal.status.to_s,
63
- listeners: Karafka::Server.listeners.count,
63
+ listeners: listeners,
64
64
  concurrency: concurrency,
65
65
  memory_usage: memory_usage,
66
66
  memory_total_usage: memory_total_usage,
@@ -76,7 +76,7 @@ module Karafka
76
76
  waterdrop: ::WaterDrop::VERSION
77
77
  },
78
78
 
79
- stats: Karafka::Server.jobs_queue.statistics.merge(
79
+ stats: jobs_queue_statistics.merge(
80
80
  utilization: utilization
81
81
  ).merge(total: @counters),
82
82
 
@@ -112,6 +112,12 @@ module Karafka
112
112
  times[:total].sum / 1_000 / concurrency / timefactor * 100
113
113
  end
114
114
 
115
+ # @return [Integer] number of listeners
116
+ def listeners
117
+ # This can be zero before the server starts
118
+ Karafka::Server.listeners&.count.to_i
119
+ end
120
+
115
121
  # @return [String] Unique process name
116
122
  def process_name
117
123
  @process_name ||= "#{Socket.gethostname}:#{::Process.pid}:#{SecureRandom.hex(6)}"
@@ -142,6 +148,12 @@ module Karafka
142
148
  end
143
149
  end
144
150
 
151
+ # @return [Hash] job queue statistics
152
+ def jobs_queue_statistics
153
+ # We return empty stats in case jobs queue is not yet initialized
154
+ Karafka::Server.jobs_queue&.statistics || { busy: 0, enqueued: 0 }
155
+ end
156
+
145
157
  # Total memory used in the OS
146
158
  def memory_total_usage
147
159
  case RUBY_PLATFORM
@@ -44,12 +44,14 @@ module Karafka
44
44
 
45
45
  @consumer_contract.validate!(consumer_report)
46
46
 
47
+ process_name = consumer_report[:process][:name]
48
+
47
49
  # Report consumers statuses
48
50
  messages = [
49
51
  {
50
52
  topic: ::Karafka::Web.config.topics.consumers.reports,
51
53
  payload: consumer_report.to_json,
52
- key: consumer_report[:process][:name],
54
+ key: process_name,
53
55
  partition: 0
54
56
  }
55
57
  ]
@@ -58,12 +60,9 @@ module Karafka
58
60
  messages += consumer_sampler.errors.map do |error|
59
61
  {
60
62
  topic: Karafka::Web.config.topics.errors,
61
- # Inject process name into the error details for easier tracing
62
- payload: error.merge(
63
- process_name: consumer_report[:process][:name]
64
- ).to_json,
63
+ payload: error.to_json,
65
64
  # Always dispatch errors from the same process to the same partition
66
- key: consumer_report[:process][:name]
65
+ key: process_name
67
66
  }
68
67
  end
69
68
 
@@ -10,13 +10,33 @@ module Karafka
10
10
  def index
11
11
  @cluster_info = Karafka::Admin.cluster_info
12
12
 
13
- @topics = @cluster_info
14
- .topics
15
- .reject { |topic| topic[:topic_name] == '__consumer_offsets' }
16
- .sort_by { |topic| topic[:topic_name] }
13
+ partitions_total = []
14
+
15
+ displayable_topics(@cluster_info).each do |topic|
16
+ topic[:partitions].each do |partition|
17
+ partitions_total << partition.merge(topic: topic)
18
+ end
19
+ end
20
+
21
+ @partitions, @next_page = Ui::Lib::PaginateArray.new.call(
22
+ partitions_total,
23
+ @params.current_page
24
+ )
17
25
 
18
26
  respond
19
27
  end
28
+
29
+ private
30
+
31
+ # @param cluster_info [Rdkafka::Metadata] cluster metadata
32
+ # @return [Array<Hash>] array with topics to be displayed sorted in an alphabetical
33
+ # order
34
+ def displayable_topics(cluster_info)
35
+ cluster_info
36
+ .topics
37
+ .reject { |topic| topic[:topic_name] == '__consumer_offsets' }
38
+ .sort_by { |topic| topic[:topic_name] }
39
+ end
20
40
  end
21
41
  end
22
42
  end
@@ -74,6 +74,16 @@ module Karafka
74
74
  bg
75
75
  end
76
76
 
77
+ # Renders tags one after another
78
+ #
79
+ # @param tags_array [Array<String>]
80
+ # @return [String] tags badges
81
+ def tags(tags_array)
82
+ tags_array
83
+ .map { |tag| %(<span class="badge bg-info">#{tag}</span>) }
84
+ .join(' ')
85
+ end
86
+
77
87
  # Takes a kafka report state and recommends background style color
78
88
  # @param state [String] state
79
89
  # @return [String] background style
@@ -118,6 +118,22 @@ module Karafka
118
118
  )
119
119
  end
120
120
 
121
+ # @return [Status::Step] is Pro enabled with all of its features.
122
+ # @note It's not an error not to have it but we want to warn, that some of the features
123
+ # may not work without Pro.
124
+ def pro_subscription
125
+ status = if state_calculation.success?
126
+ ::Karafka.pro? ? :success : :warning
127
+ else
128
+ :halted
129
+ end
130
+
131
+ Step.new(
132
+ status,
133
+ nil
134
+ )
135
+ end
136
+
121
137
  private
122
138
 
123
139
  # @return [String] consumers states topic name
@@ -20,11 +20,7 @@
20
20
  </p>
21
21
 
22
22
  <p class="mt-0 mb-1">
23
- <% process.tags.each do |tag| %>
24
- <span class="badge bg-info badge-topic">
25
- <%= tag %>
26
- </span>
27
- <% end %>
23
+ <%== tags(process.tags) %>
28
24
  </p>
29
25
  </td>
30
26
 
@@ -30,11 +30,7 @@
30
30
 
31
31
  <% unless @process.tags.empty? %>
32
32
  <p class="mb-0 text-end">
33
- <% @process.tags.each do |tag| %>
34
- <span class="badge bg-info badge-topic">
35
- <%= tag %>
36
- </span>
37
- <% end %>
33
+ <%== tags(@process.tags) %>
38
34
  </p>
39
35
  <% end %>
40
36
  </div>
@@ -7,6 +7,8 @@
7
7
  <td>
8
8
  <% if k2.to_s.include?('ssl') %>
9
9
  ***
10
+ <% elsif k2.to_s == 'tags' %>
11
+ <%== tags(v2) %>
10
12
  <% else %>
11
13
  <%= v2 %>
12
14
  <% end %>
@@ -13,9 +13,7 @@
13
13
  <td>
14
14
  <code><%= job.consumer %></code>
15
15
 
16
- <% job.tags.each do |tag| %>
17
- <span class="badge bg-info"><%= tag %></span>
18
- <% end %>
16
+ <%== tags(job.tags) %>
19
17
  </td>
20
18
  <td>
21
19
  <code>#<%= job.type %></code>
@@ -1,3 +1,5 @@
1
+ <% topic = partition[:topic] %>
2
+
1
3
  <% if topic[:topic_name] != '__consumer_offsets' %>
2
4
  <tr>
3
5
  <td>
@@ -56,15 +56,12 @@
56
56
  </tr>
57
57
  </thead>
58
58
  <tbody>
59
- <% @topics.each do |topic| %>
60
- <%==
61
- each_partial(
62
- topic[:partitions],
63
- 'cluster/partition',
64
- locals: { topic: topic }
65
- )
66
- %>
67
- <% end %>
59
+ <%==
60
+ each_partial(
61
+ @partitions,
62
+ 'cluster/partition'
63
+ )
64
+ %>
68
65
  </tbody>
69
66
  </table>
70
67
  </div>
@@ -20,11 +20,7 @@
20
20
  </p>
21
21
 
22
22
  <p class="mt-0 mb-1">
23
- <% process.tags.each do |tag| %>
24
- <span class="badge bg-info badge-topic">
25
- <%= tag %>
26
- </span>
27
- <% end %>
23
+ <%== tags(process.tags) %>
28
24
  </p>
29
25
  </td>
30
26
 
@@ -7,6 +7,8 @@
7
7
  <td>
8
8
  <% if k2.to_s.include?('ssl') %>
9
9
  ***
10
+ <% elsif k2.to_s == 'tags' %>
11
+ <%== tags(v2) %>
10
12
  <% else %>
11
13
  <%= v2 %>
12
14
  <% end %>
@@ -13,9 +13,7 @@
13
13
  <td>
14
14
  <code><%= job.consumer %></code>
15
15
 
16
- <% job.tags.each do |tag| %>
17
- <span class="badge bg-info"><%= tag %></span>
18
- <% end %>
16
+ <%== tags(job.tags) %>
19
17
  </td>
20
18
  <td>
21
19
  <code>#<%= job.type %></code>
@@ -9,6 +9,12 @@
9
9
  </a>
10
10
  </li>
11
11
 
12
+ <li class="page-item active disabled">
13
+ <a class="page-link" href="<%= current_path(page: @current_page) %>">
14
+ <span><%= @current_page %></span>
15
+ </a>
16
+ </li>
17
+
12
18
  <li class="page-item <%= 'disabled' unless @next_page %>">
13
19
  <a class="page-link" href="<%= current_path(page: @next_page) %>">
14
20
  <span>Next &raquo;</span>
@@ -0,0 +1,14 @@
1
+ <div class="card border-warning mb-3">
2
+ <div class="card-header text-bg-warning">
3
+ <span>
4
+ <%= title %>
5
+ </span>
6
+
7
+ <span class="float-end">
8
+ <span class="badge text-bg-light">Warning</span>
9
+ </span>
10
+ </div>
11
+ <div class="card-body">
12
+ <%== description %>
13
+ </div>
14
+ </div>
@@ -89,6 +89,21 @@
89
89
  )
90
90
  %>
91
91
 
92
+ <%==
93
+ partial(
94
+ "status/#{@status.pro_subscription.to_s}",
95
+ locals: {
96
+ title: 'Karafka Pro subscription',
97
+ description: partial(
98
+ 'status/warnings/pro_subscription',
99
+ locals: {
100
+ details: @status.pro_subscription.details
101
+ }
102
+ )
103
+ }
104
+ )
105
+ %>
106
+
92
107
  </div>
93
108
  </div>
94
109
  </div>
@@ -0,0 +1,9 @@
1
+ <p>
2
+ You are using the OSS version of the Karafka ecosystem, and some of the Karafka ecosystem features are unavailable in the OSS version.
3
+ </p>
4
+
5
+ <p class="mb-0">
6
+ Please help us make the Karafka ecosystem better by subscribing to our
7
+ <a target="_blank" href="https://karafka.io/#become-pro">Pro</a>
8
+ offering.
9
+ </p>
@@ -3,6 +3,6 @@
3
3
  module Karafka
4
4
  module Web
5
5
  # Current gem version
6
- VERSION = '0.2.2'
6
+ VERSION = '0.2.4'
7
7
  end
8
8
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-web
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -35,7 +35,7 @@ cert_chain:
35
35
  Qf04B9ceLUaC4fPVEz10FyobjaFoY4i32xRto3XnrzeAgfEe4swLq8bQsR3w/EF3
36
36
  MGU0FeSV2Yj7Xc2x/7BzLK8xQn5l7Yy75iPF+KP3vVmDHnNl
37
37
  -----END CERTIFICATE-----
38
- date: 2023-02-25 00:00:00.000000000 Z
38
+ date: 2023-03-14 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: erubi
@@ -57,7 +57,7 @@ dependencies:
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 2.0.33
60
+ version: 2.0.35
61
61
  - - "<"
62
62
  - !ruby/object:Gem::Version
63
63
  version: 3.0.0
@@ -67,7 +67,7 @@ dependencies:
67
67
  requirements:
68
68
  - - ">="
69
69
  - !ruby/object:Gem::Version
70
- version: 2.0.33
70
+ version: 2.0.35
71
71
  - - "<"
72
72
  - !ruby/object:Gem::Version
73
73
  version: 3.0.0
@@ -314,6 +314,7 @@ files:
314
314
  - lib/karafka/web/ui/views/status/_failure.erb
315
315
  - lib/karafka/web/ui/views/status/_halted.erb
316
316
  - lib/karafka/web/ui/views/status/_success.erb
317
+ - lib/karafka/web/ui/views/status/_warning.erb
317
318
  - lib/karafka/web/ui/views/status/failures/_connection.erb
318
319
  - lib/karafka/web/ui/views/status/failures/_initial_state.erb
319
320
  - lib/karafka/web/ui/views/status/failures/_live_reporting.erb
@@ -321,6 +322,7 @@ files:
321
322
  - lib/karafka/web/ui/views/status/failures/_state_calculation.erb
322
323
  - lib/karafka/web/ui/views/status/failures/_topics.erb
323
324
  - lib/karafka/web/ui/views/status/show.erb
325
+ - lib/karafka/web/ui/views/status/warnings/_pro_subscription.erb
324
326
  - lib/karafka/web/version.rb
325
327
  homepage: https://karafka.io
326
328
  licenses:
metadata.gz.sig CHANGED
Binary file