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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +3 -0
- data/.coditsu/ci.yml +3 -0
- data/.diffend.yml +3 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +50 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/workflows/ci.yml +49 -0
- data/.gitignore +69 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +52 -0
- data/LICENSE +17 -0
- data/README.md +29 -0
- data/bin/karafka-web +33 -0
- data/certs/cert_chain.pem +26 -0
- data/config/locales/errors.yml +9 -0
- data/karafka-web.gemspec +44 -0
- data/lib/karafka/web/app.rb +17 -0
- data/lib/karafka/web/config.rb +80 -0
- data/lib/karafka/web/deserializer.rb +20 -0
- data/lib/karafka/web/errors.rb +25 -0
- data/lib/karafka/web/installer.rb +124 -0
- data/lib/karafka/web/processing/consumer.rb +66 -0
- data/lib/karafka/web/processing/consumers/aggregator.rb +130 -0
- data/lib/karafka/web/processing/consumers/state.rb +32 -0
- data/lib/karafka/web/tracking/base_contract.rb +31 -0
- data/lib/karafka/web/tracking/consumers/contracts/consumer_group.rb +33 -0
- data/lib/karafka/web/tracking/consumers/contracts/job.rb +26 -0
- data/lib/karafka/web/tracking/consumers/contracts/partition.rb +22 -0
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +95 -0
- data/lib/karafka/web/tracking/consumers/contracts/topic.rb +29 -0
- data/lib/karafka/web/tracking/consumers/listeners/base.rb +33 -0
- data/lib/karafka/web/tracking/consumers/listeners/errors.rb +107 -0
- data/lib/karafka/web/tracking/consumers/listeners/pausing.rb +45 -0
- data/lib/karafka/web/tracking/consumers/listeners/processing.rb +157 -0
- data/lib/karafka/web/tracking/consumers/listeners/statistics.rb +123 -0
- data/lib/karafka/web/tracking/consumers/listeners/status.rb +58 -0
- data/lib/karafka/web/tracking/consumers/sampler.rb +216 -0
- data/lib/karafka/web/tracking/memoized_shell.rb +48 -0
- data/lib/karafka/web/tracking/reporter.rb +144 -0
- data/lib/karafka/web/tracking/ttl_array.rb +59 -0
- data/lib/karafka/web/tracking/ttl_hash.rb +16 -0
- data/lib/karafka/web/ui/app.rb +78 -0
- data/lib/karafka/web/ui/base.rb +77 -0
- data/lib/karafka/web/ui/controllers/base.rb +40 -0
- data/lib/karafka/web/ui/controllers/become_pro.rb +17 -0
- data/lib/karafka/web/ui/controllers/cluster.rb +24 -0
- data/lib/karafka/web/ui/controllers/consumers.rb +27 -0
- data/lib/karafka/web/ui/controllers/errors.rb +43 -0
- data/lib/karafka/web/ui/controllers/jobs.rb +33 -0
- data/lib/karafka/web/ui/controllers/requests/params.rb +30 -0
- data/lib/karafka/web/ui/controllers/responses/data.rb +26 -0
- data/lib/karafka/web/ui/controllers/routing.rb +30 -0
- data/lib/karafka/web/ui/helpers/application_helper.rb +144 -0
- data/lib/karafka/web/ui/lib/hash_proxy.rb +66 -0
- data/lib/karafka/web/ui/lib/paginate_array.rb +38 -0
- data/lib/karafka/web/ui/models/consumer_group.rb +20 -0
- data/lib/karafka/web/ui/models/health.rb +44 -0
- data/lib/karafka/web/ui/models/job.rb +13 -0
- data/lib/karafka/web/ui/models/message.rb +99 -0
- data/lib/karafka/web/ui/models/partition.rb +13 -0
- data/lib/karafka/web/ui/models/process.rb +56 -0
- data/lib/karafka/web/ui/models/processes.rb +86 -0
- data/lib/karafka/web/ui/models/state.rb +67 -0
- data/lib/karafka/web/ui/models/topic.rb +19 -0
- data/lib/karafka/web/ui/pro/app.rb +120 -0
- data/lib/karafka/web/ui/pro/controllers/cluster.rb +16 -0
- data/lib/karafka/web/ui/pro/controllers/consumers.rb +54 -0
- data/lib/karafka/web/ui/pro/controllers/dlq.rb +44 -0
- data/lib/karafka/web/ui/pro/controllers/errors.rb +57 -0
- data/lib/karafka/web/ui/pro/controllers/explorer.rb +79 -0
- data/lib/karafka/web/ui/pro/controllers/health.rb +33 -0
- data/lib/karafka/web/ui/pro/controllers/jobs.rb +26 -0
- data/lib/karafka/web/ui/pro/controllers/routing.rb +26 -0
- data/lib/karafka/web/ui/pro/views/consumers/_breadcrumbs.erb +27 -0
- data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +60 -0
- data/lib/karafka/web/ui/pro/views/consumers/_counters.erb +50 -0
- data/lib/karafka/web/ui/pro/views/consumers/_summary.erb +81 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_consumer_group.erb +109 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +26 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +126 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_jobs.erb +9 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_no_subscriptions.erb +9 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_partition.erb +32 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_stopped.erb +10 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_tabs.erb +20 -0
- data/lib/karafka/web/ui/pro/views/consumers/index.erb +30 -0
- data/lib/karafka/web/ui/pro/views/consumers/jobs.erb +42 -0
- data/lib/karafka/web/ui/pro/views/consumers/subscriptions.erb +23 -0
- data/lib/karafka/web/ui/pro/views/dlq/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/pro/views/dlq/_no_topics.erb +9 -0
- data/lib/karafka/web/ui/pro/views/dlq/_topic.erb +12 -0
- data/lib/karafka/web/ui/pro/views/dlq/index.erb +16 -0
- data/lib/karafka/web/ui/pro/views/errors/_breadcrumbs.erb +25 -0
- data/lib/karafka/web/ui/pro/views/errors/_detail.erb +29 -0
- data/lib/karafka/web/ui/pro/views/errors/_error.erb +26 -0
- data/lib/karafka/web/ui/pro/views/errors/_partition_option.erb +7 -0
- data/lib/karafka/web/ui/pro/views/errors/index.erb +58 -0
- data/lib/karafka/web/ui/pro/views/errors/show.erb +56 -0
- data/lib/karafka/web/ui/pro/views/explorer/_breadcrumbs.erb +29 -0
- data/lib/karafka/web/ui/pro/views/explorer/_detail.erb +21 -0
- data/lib/karafka/web/ui/pro/views/explorer/_encryption_enabled.erb +18 -0
- data/lib/karafka/web/ui/pro/views/explorer/_failed_deserialization.erb +4 -0
- data/lib/karafka/web/ui/pro/views/explorer/_message.erb +16 -0
- data/lib/karafka/web/ui/pro/views/explorer/_partition_option.erb +7 -0
- data/lib/karafka/web/ui/pro/views/explorer/_topic.erb +12 -0
- data/lib/karafka/web/ui/pro/views/explorer/index.erb +17 -0
- data/lib/karafka/web/ui/pro/views/explorer/partition.erb +56 -0
- data/lib/karafka/web/ui/pro/views/explorer/show.erb +65 -0
- data/lib/karafka/web/ui/pro/views/health/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/pro/views/health/_partition.erb +35 -0
- data/lib/karafka/web/ui/pro/views/health/index.erb +60 -0
- data/lib/karafka/web/ui/pro/views/jobs/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/pro/views/jobs/_job.erb +31 -0
- data/lib/karafka/web/ui/pro/views/jobs/_no_jobs.erb +9 -0
- data/lib/karafka/web/ui/pro/views/jobs/index.erb +34 -0
- data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +57 -0
- data/lib/karafka/web/ui/public/images/favicon.ico +0 -0
- data/lib/karafka/web/ui/public/images/logo.svg +28 -0
- data/lib/karafka/web/ui/public/javascripts/application.js +41 -0
- data/lib/karafka/web/ui/public/javascripts/bootstrap.min.js +7 -0
- data/lib/karafka/web/ui/public/javascripts/highlight.min.js +337 -0
- data/lib/karafka/web/ui/public/javascripts/live_poll.js +124 -0
- data/lib/karafka/web/ui/public/javascripts/timeago.min.js +1 -0
- data/lib/karafka/web/ui/public/stylesheets/application.css +106 -0
- data/lib/karafka/web/ui/public/stylesheets/bootstrap.min.css +7 -0
- data/lib/karafka/web/ui/public/stylesheets/bootstrap.min.css.map +1 -0
- data/lib/karafka/web/ui/public/stylesheets/highlight.min.css +10 -0
- data/lib/karafka/web/ui/views/cluster/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/views/cluster/_broker.erb +5 -0
- data/lib/karafka/web/ui/views/cluster/_partition.erb +22 -0
- data/lib/karafka/web/ui/views/cluster/index.erb +72 -0
- data/lib/karafka/web/ui/views/consumers/_breadcrumbs.erb +27 -0
- data/lib/karafka/web/ui/views/consumers/_consumer.erb +43 -0
- data/lib/karafka/web/ui/views/consumers/_counters.erb +44 -0
- data/lib/karafka/web/ui/views/consumers/_summary.erb +81 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_consumer_group.erb +109 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_job.erb +26 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_metrics.erb +126 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_no_jobs.erb +9 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_no_subscriptions.erb +9 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_partition.erb +32 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_stopped.erb +10 -0
- data/lib/karafka/web/ui/views/consumers/consumer/_tabs.erb +20 -0
- data/lib/karafka/web/ui/views/consumers/index.erb +29 -0
- data/lib/karafka/web/ui/views/errors/_breadcrumbs.erb +19 -0
- data/lib/karafka/web/ui/views/errors/_detail.erb +29 -0
- data/lib/karafka/web/ui/views/errors/_error.erb +26 -0
- data/lib/karafka/web/ui/views/errors/index.erb +38 -0
- data/lib/karafka/web/ui/views/errors/show.erb +30 -0
- data/lib/karafka/web/ui/views/jobs/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/views/jobs/_job.erb +22 -0
- data/lib/karafka/web/ui/views/jobs/_no_jobs.erb +9 -0
- data/lib/karafka/web/ui/views/jobs/index.erb +31 -0
- data/lib/karafka/web/ui/views/layout.erb +23 -0
- data/lib/karafka/web/ui/views/routing/_breadcrumbs.erb +15 -0
- data/lib/karafka/web/ui/views/routing/_consumer_group.erb +34 -0
- data/lib/karafka/web/ui/views/routing/_detail.erb +25 -0
- data/lib/karafka/web/ui/views/routing/_topic.erb +18 -0
- data/lib/karafka/web/ui/views/routing/index.erb +10 -0
- data/lib/karafka/web/ui/views/routing/show.erb +26 -0
- data/lib/karafka/web/ui/views/shared/_become_pro.erb +13 -0
- data/lib/karafka/web/ui/views/shared/_brand.erb +3 -0
- data/lib/karafka/web/ui/views/shared/_content.erb +31 -0
- data/lib/karafka/web/ui/views/shared/_header.erb +20 -0
- data/lib/karafka/web/ui/views/shared/_navigation.erb +57 -0
- data/lib/karafka/web/ui/views/shared/_pagination.erb +21 -0
- data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +39 -0
- data/lib/karafka/web/ui/views/shared/exceptions/pro_only.erb +52 -0
- data/lib/karafka/web/version.rb +8 -0
- data/lib/karafka/web.rb +60 -0
- data.tar.gz.sig +0 -0
- metadata +328 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Models
|
|
7
|
+
# Single consumer process representation
|
|
8
|
+
class Process < Lib::HashProxy
|
|
9
|
+
class << self
|
|
10
|
+
# Looks for a given process based on its id
|
|
11
|
+
# @param state [State] state of the system based on which we will do the lookup
|
|
12
|
+
# @param process_id [String] id of the process we are looking for
|
|
13
|
+
# @return [Process] selected process or error raised
|
|
14
|
+
# @raise [::Karafka::Web::Errors::Ui::NotFoundError] raised if process not found
|
|
15
|
+
def find(state, process_id)
|
|
16
|
+
found_process = Processes.active(state).find { |process| process.id == process_id }
|
|
17
|
+
found_process || raise(::Karafka::Web::Errors::Ui::NotFoundError, process_id)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [String] process id without the name and ip
|
|
22
|
+
def id
|
|
23
|
+
@id ||= name.split(':').last
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Array<ConsumerGroup>] consumer groups to which this process is subscribed in
|
|
27
|
+
# an alphabetical order
|
|
28
|
+
def consumer_groups
|
|
29
|
+
super
|
|
30
|
+
.values
|
|
31
|
+
.map { |cg_hash| ConsumerGroup.new(cg_hash) }
|
|
32
|
+
.sort_by(&:id)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Jobs sorted from longest running to youngest
|
|
36
|
+
# @return [Array<Job>] current jobs of this process
|
|
37
|
+
def jobs
|
|
38
|
+
super
|
|
39
|
+
.map { |job| Job.new(job) }
|
|
40
|
+
.sort_by(&:started_at)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Integer] collective lag on this process
|
|
44
|
+
def lag_stored
|
|
45
|
+
consumer_groups
|
|
46
|
+
.flat_map(&:topics)
|
|
47
|
+
.flat_map(&:partitions)
|
|
48
|
+
.map(&:lag_stored)
|
|
49
|
+
.delete_if(&:negative?)
|
|
50
|
+
.sum
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Models
|
|
7
|
+
# Represents the active processes data
|
|
8
|
+
# @note Active also includes processes stopped recently. We use it to provide better
|
|
9
|
+
# visibility via UI.
|
|
10
|
+
module Processes
|
|
11
|
+
class << self
|
|
12
|
+
include ::Karafka::Core::Helpers::Time
|
|
13
|
+
|
|
14
|
+
# Returns the active processes in an array and alongside of that the current state of
|
|
15
|
+
# the system. We use those together in the UI and it would be expensive to pick it up
|
|
16
|
+
# while we've already had it.
|
|
17
|
+
#
|
|
18
|
+
# @param state [State] current system state from which we can get processes metadata
|
|
19
|
+
# @return [Array<Process>]
|
|
20
|
+
def active(state)
|
|
21
|
+
processes = fetch_reports(state)
|
|
22
|
+
evict_expired_processes(processes)
|
|
23
|
+
processes = squash_processes_data(processes)
|
|
24
|
+
processes = sort_processes(processes)
|
|
25
|
+
|
|
26
|
+
processes.map { |process_hash| Process.new(process_hash) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Fetches the relevant processes reports from the reports topic
|
|
32
|
+
# @param state [State]
|
|
33
|
+
# @return [Array<Hash>] array with deserialized processes reports
|
|
34
|
+
def fetch_reports(state)
|
|
35
|
+
offsets = state[:processes]
|
|
36
|
+
.values
|
|
37
|
+
.map { |process| process[:offset] }
|
|
38
|
+
.sort
|
|
39
|
+
|
|
40
|
+
::Karafka::Admin.read_topic(
|
|
41
|
+
::Karafka::Web.config.topics.consumers.reports,
|
|
42
|
+
0,
|
|
43
|
+
# We set 10k here because we start from the latest offset of the reports, hence
|
|
44
|
+
# we will never get this much. Do do not know however exactly how many reports
|
|
45
|
+
# we may get as for some processes we may get few if the reporting interval
|
|
46
|
+
# was bypassed by state changes in the processes
|
|
47
|
+
10_000,
|
|
48
|
+
offsets.first || -1
|
|
49
|
+
).map(&:payload)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Collapses processes data and only keeps the most recent report for give process
|
|
53
|
+
# @param processes [Array<Hash>]
|
|
54
|
+
# @return [Array<Hash>] unique processes data
|
|
55
|
+
def squash_processes_data(processes)
|
|
56
|
+
processes
|
|
57
|
+
.reverse
|
|
58
|
+
.uniq { |consumer| consumer[:process][:name] }
|
|
59
|
+
.reverse
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Removes processes that are no longer active. They may still be here because the state
|
|
63
|
+
# may have a small lag but we want to compensate for it that way.
|
|
64
|
+
# @param processes [Array<Hash>]
|
|
65
|
+
# @return [Array<Hash>] only active processes data
|
|
66
|
+
def evict_expired_processes(processes)
|
|
67
|
+
max_ttl = ::Karafka::Web.config.ttl / 1_000
|
|
68
|
+
now = float_now
|
|
69
|
+
|
|
70
|
+
processes.delete_if do |details|
|
|
71
|
+
now - details[:dispatched_at] > max_ttl
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Ensures that we always return processes sorted by their name
|
|
76
|
+
# @param processes [Array<Hash>]
|
|
77
|
+
# @return [Array<Hash>] sorted processes data
|
|
78
|
+
def sort_processes(processes)
|
|
79
|
+
processes.sort_by { |consumer| consumer[:process][:name] }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Models
|
|
7
|
+
# Represents the current consumer processes aggregated state
|
|
8
|
+
# This state is the core of Karafka reporting. It holds the most important aggregated data
|
|
9
|
+
# as well as pointers to states of particular consumers and their details.
|
|
10
|
+
class State < Lib::HashProxy
|
|
11
|
+
extend ::Karafka::Core::Helpers::Time
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
# @return [State, false] current aggregated state or false if not found
|
|
15
|
+
# @note Current state may contain expired data, for example of processes that were
|
|
16
|
+
# forcefully killed, etc. We clean this prior to returning the state.
|
|
17
|
+
def current
|
|
18
|
+
state = fetch
|
|
19
|
+
|
|
20
|
+
return false unless state
|
|
21
|
+
|
|
22
|
+
state = state.payload
|
|
23
|
+
evict_expired_processes(state)
|
|
24
|
+
sort_processes(state)
|
|
25
|
+
|
|
26
|
+
new(state)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [State] current aggregated state
|
|
30
|
+
# @raise [::Karafka::Web::Errors::Ui::NotFoundError] raised when there is no current
|
|
31
|
+
# state. Probably because `karafka server` was never executed
|
|
32
|
+
def current!
|
|
33
|
+
current || raise(::Karafka::Web::Errors::Ui::NotFoundError)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# @return [::Karafka::Messages::Message, nil] most recent state or nil if none
|
|
39
|
+
def fetch
|
|
40
|
+
::Karafka::Admin.read_topic(
|
|
41
|
+
Karafka::Web.config.topics.consumers.states,
|
|
42
|
+
0,
|
|
43
|
+
1
|
|
44
|
+
).last
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Evicts (removes) details about processes that are beyond our TTL on liveliness
|
|
48
|
+
# @param state_hash [Hash] raw message state hash
|
|
49
|
+
def evict_expired_processes(state_hash)
|
|
50
|
+
max_ttl = ::Karafka::Web.config.ttl / 1_000
|
|
51
|
+
|
|
52
|
+
state_hash[:processes].delete_if do |_, details|
|
|
53
|
+
float_now - details[:dispatched_at] > max_ttl
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Sorts the processes based on their unique ids, so they are always in order
|
|
58
|
+
# @param state_hash [Hash] raw message state hash
|
|
59
|
+
def sort_processes(state_hash)
|
|
60
|
+
state_hash[:processes] = state_hash[:processes].to_a.sort_by(&:first).to_h
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Models
|
|
7
|
+
# Single topic data representation model
|
|
8
|
+
class Topic < Lib::HashProxy
|
|
9
|
+
# @return [Array<Partition>] All topic partitions data
|
|
10
|
+
def partitions
|
|
11
|
+
super.values.map do |partition_hash|
|
|
12
|
+
Partition.new(partition_hash)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
# Pro Web UI components
|
|
18
|
+
module Pro
|
|
19
|
+
# Main Roda Web App that servers all the metrics and stats
|
|
20
|
+
class App < Ui::Base
|
|
21
|
+
opts[:root] = Karafka::Web.gem_root.join('lib/karafka/web/ui/pro')
|
|
22
|
+
|
|
23
|
+
instance_exec(&CONTEXT_DETAILS)
|
|
24
|
+
|
|
25
|
+
plugin :render, escape: true, engine: 'erb', allowed_paths: [
|
|
26
|
+
Karafka::Web.gem_root.join('lib/karafka/web/ui/pro/views'),
|
|
27
|
+
Karafka::Web.gem_root.join('lib/karafka/web/ui/views')
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
plugin :additional_view_directories, [
|
|
31
|
+
Karafka::Web.gem_root.join('lib/karafka/web/ui/views')
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
route do |r|
|
|
35
|
+
r.root { r.redirect root_path('consumers') }
|
|
36
|
+
|
|
37
|
+
@current_page = params.current_page
|
|
38
|
+
|
|
39
|
+
r.on 'consumers' do
|
|
40
|
+
controller = Controllers::Consumers.new(params)
|
|
41
|
+
|
|
42
|
+
r.get String, 'jobs' do |process_id|
|
|
43
|
+
render_response controller.jobs(process_id)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
r.get String, 'subscriptions' do |process_id|
|
|
47
|
+
render_response controller.subscriptions(process_id)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
r.get do
|
|
51
|
+
@breadcrumbs = false
|
|
52
|
+
render_response controller.index
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
r.get 'jobs' do
|
|
57
|
+
controller = Controllers::Jobs.new(params)
|
|
58
|
+
render_response controller.index
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
r.on 'routing' do
|
|
62
|
+
controller = Controllers::Routing.new(params)
|
|
63
|
+
|
|
64
|
+
r.get String do |topic_id|
|
|
65
|
+
render_response controller.show(topic_id)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
r.get do
|
|
69
|
+
render_response controller.index
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
r.on 'explorer' do
|
|
74
|
+
controller = Controllers::Explorer.new(params)
|
|
75
|
+
|
|
76
|
+
r.get String, Integer, Integer do |topic_id, partition_id, offset|
|
|
77
|
+
render_response controller.show(topic_id, partition_id, offset)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
r.get String, Integer do |topic_id, partition_id|
|
|
81
|
+
render_response controller.partition(topic_id, partition_id)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
r.get do
|
|
85
|
+
render_response controller.index
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
r.get 'health' do
|
|
90
|
+
controller = Controllers::Health.new(params)
|
|
91
|
+
render_response controller.index
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
r.get 'cluster' do
|
|
95
|
+
controller = Controllers::Cluster.new(params)
|
|
96
|
+
render_response controller.index
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
r.on 'errors' do
|
|
100
|
+
controller = Controllers::Errors.new(params)
|
|
101
|
+
|
|
102
|
+
r.get Integer do |partition_id|
|
|
103
|
+
render_response controller.index(partition_id)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
r.get Integer, Integer do |partition_id, offset|
|
|
107
|
+
render_response controller.show(partition_id, offset)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
r.get 'dlq' do
|
|
112
|
+
controller = Controllers::Dlq.new(params)
|
|
113
|
+
render_response controller.index
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Karafka
|
|
4
|
+
module Web
|
|
5
|
+
module Ui
|
|
6
|
+
module Pro
|
|
7
|
+
# Namespace for Pro controllers
|
|
8
|
+
module Controllers
|
|
9
|
+
# Cluster controller
|
|
10
|
+
class Cluster < Ui::Controllers::Cluster
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Controller for displaying consumers states and details about them
|
|
20
|
+
class Consumers < Ui::Controllers::Base
|
|
21
|
+
# Consumers list
|
|
22
|
+
def index
|
|
23
|
+
@current_state = Models::State.current!
|
|
24
|
+
processes_total = Models::Processes.active(@current_state)
|
|
25
|
+
@counters = Lib::HashProxy.new(@current_state[:stats])
|
|
26
|
+
@processes, @next_page = Lib::PaginateArray.new.call(
|
|
27
|
+
processes_total,
|
|
28
|
+
@params.current_page
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
respond
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param process_id [String] id of the process we're interested in
|
|
35
|
+
def jobs(process_id)
|
|
36
|
+
current_state = Models::State.current!
|
|
37
|
+
@process = Models::Process.find(current_state, process_id)
|
|
38
|
+
|
|
39
|
+
respond
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param process_id [String] id of the process we're interested in
|
|
43
|
+
def subscriptions(process_id)
|
|
44
|
+
current_state = Models::State.current!
|
|
45
|
+
@process = Models::Process.find(current_state, process_id)
|
|
46
|
+
|
|
47
|
+
respond
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# DLQ topics overview
|
|
20
|
+
class Dlq < Ui::Controllers::Base
|
|
21
|
+
# Lists DLQ topics
|
|
22
|
+
def index
|
|
23
|
+
topics = Karafka::App.consumer_groups.flat_map(&:topics).flat_map(&:to_a)
|
|
24
|
+
|
|
25
|
+
dlq_topic_names = topics
|
|
26
|
+
.map { |source_topic| source_topic.dead_letter_queue.topic }
|
|
27
|
+
.uniq
|
|
28
|
+
.compact
|
|
29
|
+
.select(&:itself)
|
|
30
|
+
|
|
31
|
+
@dlq_topics = Karafka::Admin
|
|
32
|
+
.cluster_info
|
|
33
|
+
.topics
|
|
34
|
+
.select { |topic| dlq_topic_names.include?(topic[:topic_name]) }
|
|
35
|
+
.sort_by { |topic| topic[:topic_name] }
|
|
36
|
+
|
|
37
|
+
respond
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Errors details controller
|
|
20
|
+
class Errors < Ui::Controllers::Base
|
|
21
|
+
# @param partition_id [Integer] id of the partition of errors we are interested in
|
|
22
|
+
def index(partition_id)
|
|
23
|
+
errors_topic = ::Karafka::Web.config.topics.errors
|
|
24
|
+
@partition_id = partition_id
|
|
25
|
+
@previous_page, @error_messages, @next_page, @partitions_count = \
|
|
26
|
+
Models::Message.page(
|
|
27
|
+
errors_topic,
|
|
28
|
+
@partition_id,
|
|
29
|
+
@params.current_page
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
respond
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Shows given error details
|
|
36
|
+
#
|
|
37
|
+
# @param partition_id [Integer]
|
|
38
|
+
# @param offset [Integer]
|
|
39
|
+
def show(partition_id, offset)
|
|
40
|
+
errors_topic = ::Karafka::Web.config.topics.errors
|
|
41
|
+
|
|
42
|
+
@partition_id = partition_id
|
|
43
|
+
@offset = offset
|
|
44
|
+
@error_message = Models::Message.find(
|
|
45
|
+
errors_topic,
|
|
46
|
+
@partition_id,
|
|
47
|
+
@offset
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
respond
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Data explorer controller
|
|
20
|
+
class Explorer < Ui::Controllers::Base
|
|
21
|
+
# Lists all the topics we can explore
|
|
22
|
+
def index
|
|
23
|
+
@topics = Karafka::Admin
|
|
24
|
+
.cluster_info
|
|
25
|
+
.topics
|
|
26
|
+
.reject { |topic| topic[:topic_name] == '__consumer_offsets' }
|
|
27
|
+
.sort_by { |topic| topic[:topic_name] }
|
|
28
|
+
|
|
29
|
+
respond
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Shows messages available in a given partition
|
|
33
|
+
#
|
|
34
|
+
# @param topic_id [String]
|
|
35
|
+
# @param partition_id [Integer]
|
|
36
|
+
def partition(topic_id, partition_id)
|
|
37
|
+
@topic_id = topic_id
|
|
38
|
+
@partition_id = partition_id
|
|
39
|
+
@previous_page, @messages, @next_page, @partitions_count = Ui::Models::Message.page(
|
|
40
|
+
@topic_id,
|
|
41
|
+
@partition_id,
|
|
42
|
+
@params.current_page
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
respond
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Displays given message
|
|
49
|
+
#
|
|
50
|
+
# @param topic_id [String]
|
|
51
|
+
# @param partition_id [Integer]
|
|
52
|
+
# @param offset [Integer] offset of the message we want to display
|
|
53
|
+
def show(topic_id, partition_id, offset)
|
|
54
|
+
@topic_id = topic_id
|
|
55
|
+
@partition_id = partition_id
|
|
56
|
+
@offset = offset
|
|
57
|
+
@message = Ui::Models::Message.find(@topic_id, @partition_id, @offset)
|
|
58
|
+
@payload_error = false
|
|
59
|
+
|
|
60
|
+
@decrypt = if ::Karafka::App.config.encryption.active
|
|
61
|
+
::Karafka::Web.config.ui.decrypt
|
|
62
|
+
else
|
|
63
|
+
true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
@pretty_payload = JSON.pretty_generate(@message.payload)
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
@payload_error = e
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
respond
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Health state controller
|
|
20
|
+
class Health < Ui::Controllers::Base
|
|
21
|
+
# Displays the current system state
|
|
22
|
+
def index
|
|
23
|
+
current_state = Models::State.current!
|
|
24
|
+
@stats = Models::Health.current(current_state)
|
|
25
|
+
|
|
26
|
+
respond
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Displays list of active jobs
|
|
20
|
+
class Jobs < Ui::Controllers::Jobs
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
|
5
|
+
#
|
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
|
7
|
+
# repository and their usage requires commercial license agreement.
|
|
8
|
+
#
|
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
|
10
|
+
#
|
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
|
12
|
+
# your code to Maciej Mensfeld.
|
|
13
|
+
|
|
14
|
+
module Karafka
|
|
15
|
+
module Web
|
|
16
|
+
module Ui
|
|
17
|
+
module Pro
|
|
18
|
+
module Controllers
|
|
19
|
+
# Routing details - same as in OSS
|
|
20
|
+
class Routing < Ui::Controllers::Routing
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|