gitlab-exporter 10.2.0 → 11.0.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/.gitlab-ci.yml +4 -0
- data/Gemfile.lock +10 -10
- data/config/gitlab-exporter.yml.example +0 -2
- data/gitlab-exporter.gemspec +8 -8
- data/lib/gitlab_exporter/cli.rb +2 -2
- data/lib/gitlab_exporter/prometheus.rb +17 -0
- data/lib/gitlab_exporter/sidekiq.rb +28 -8
- data/lib/gitlab_exporter/version.rb +1 -1
- data/spec/prometheus_metrics_spec.rb +17 -0
- metadata +31 -32
- data/lib/gitlab_exporter/sidekiq_queue_job_stats.lua +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f22f9c58b0d64fdec6134fa8b59cfd474f1333ebe59a8a94c620749fc4be33c
|
4
|
+
data.tar.gz: 0dc26d62d7aab597a13aab640844a42774a85c2ce6459f9eeeef7441b81f3d19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56a1c9f3a267b32ead646a1c6a95a8d81744498d67613e0c6dbb849a15e624d96215800a1688504e08c8b25a2b1222a4b86f2e3b1cb7b898ff892f6a445dcdbd
|
7
|
+
data.tar.gz: e08257d194617fd5467526403a8629aeab1d46719bb34c57b6aaaa77cb94b138b139544771b071bfccc6ec47e8440d1241ae3adb8fceb226694366d4e89a058f
|
data/.gitlab-ci.yml
CHANGED
@@ -6,6 +6,10 @@ include:
|
|
6
6
|
- template: Security/SAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-foss/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
|
7
7
|
- template: Security/Secret-Detection.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab-foss/-/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml
|
8
8
|
|
9
|
+
stages:
|
10
|
+
- test
|
11
|
+
- dast
|
12
|
+
|
9
13
|
default:
|
10
14
|
image: ruby:2.7
|
11
15
|
cache:
|
data/Gemfile.lock
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gitlab-exporter (
|
5
|
-
connection_pool (
|
6
|
-
pg (
|
7
|
-
puma (
|
8
|
-
quantile (
|
9
|
-
redis (
|
10
|
-
redis-namespace (
|
11
|
-
sidekiq (
|
12
|
-
sinatra (
|
4
|
+
gitlab-exporter (11.0.0)
|
5
|
+
connection_pool (= 2.2.5)
|
6
|
+
pg (= 1.2.3)
|
7
|
+
puma (= 5.3.2)
|
8
|
+
quantile (= 0.2.1)
|
9
|
+
redis (= 4.1.4)
|
10
|
+
redis-namespace (= 1.6.0)
|
11
|
+
sidekiq (= 5.2.9)
|
12
|
+
sinatra (= 2.0.8.1)
|
13
13
|
|
14
14
|
GEM
|
15
15
|
remote: https://rubygems.org/
|
@@ -24,7 +24,7 @@ GEM
|
|
24
24
|
parser (3.0.0.0)
|
25
25
|
ast (~> 2.4.1)
|
26
26
|
pg (1.2.3)
|
27
|
-
puma (5.
|
27
|
+
puma (5.3.2)
|
28
28
|
nio4r (~> 2.0)
|
29
29
|
quantile (0.2.1)
|
30
30
|
rack (2.2.3)
|
data/gitlab-exporter.gemspec
CHANGED
@@ -20,14 +20,14 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.homepage = "http://gitlab.com"
|
21
21
|
s.license = "MIT"
|
22
22
|
|
23
|
-
s.add_runtime_dependency "connection_pool", "
|
24
|
-
s.add_runtime_dependency "pg", "
|
25
|
-
s.add_runtime_dependency "puma", "
|
26
|
-
s.add_runtime_dependency "quantile", "
|
27
|
-
s.add_runtime_dependency "redis", "
|
28
|
-
s.add_runtime_dependency "redis-namespace", "
|
29
|
-
s.add_runtime_dependency "sidekiq", "
|
30
|
-
s.add_runtime_dependency "sinatra", "
|
23
|
+
s.add_runtime_dependency "connection_pool", "2.2.5"
|
24
|
+
s.add_runtime_dependency "pg", "1.2.3"
|
25
|
+
s.add_runtime_dependency "puma", "5.3.2"
|
26
|
+
s.add_runtime_dependency "quantile", "0.2.1"
|
27
|
+
s.add_runtime_dependency "redis", "4.1.4"
|
28
|
+
s.add_runtime_dependency "redis-namespace", "1.6.0"
|
29
|
+
s.add_runtime_dependency "sidekiq", "5.2.9"
|
30
|
+
s.add_runtime_dependency "sinatra", "2.0.8.1"
|
31
31
|
|
32
32
|
s.add_development_dependency "rspec", "~> 3.7.0"
|
33
33
|
s.add_development_dependency "rspec-expectations", "~> 3.7.0"
|
data/lib/gitlab_exporter/cli.rb
CHANGED
@@ -217,7 +217,7 @@ module GitLab
|
|
217
217
|
opts.on("--pid=123", "Process ID") do |val|
|
218
218
|
@pid = val
|
219
219
|
end
|
220
|
-
opts.on("--pattern=
|
220
|
+
opts.on("--pattern=worker", "Process command pattern") do |val|
|
221
221
|
@pattern = val
|
222
222
|
end
|
223
223
|
opts.on("--name=NAME", "Process name to be used in metrics") do |val|
|
@@ -275,7 +275,7 @@ module GitLab
|
|
275
275
|
::GitLab::Exporter::SidekiqProber.new(redis_url: @redis_url)
|
276
276
|
.probe_stats
|
277
277
|
.probe_queues
|
278
|
-
.
|
278
|
+
.probe_jobs_limit
|
279
279
|
.probe_workers
|
280
280
|
.probe_retries
|
281
281
|
.write_to(@target)
|
@@ -15,6 +15,21 @@ module GitLab
|
|
15
15
|
@include_timestamp = include_timestamp
|
16
16
|
end
|
17
17
|
|
18
|
+
class << self
|
19
|
+
def describe(name, description)
|
20
|
+
@metric_descriptions ||= {}
|
21
|
+
@metric_descriptions[name] = description
|
22
|
+
end
|
23
|
+
|
24
|
+
def description(name)
|
25
|
+
@metric_descriptions && @metric_descriptions[name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear_descriptions
|
29
|
+
@metric_descriptions = {}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
18
33
|
def add(name, value, quantile = false, **labels)
|
19
34
|
fail "value '#{value}' must be a number" unless value.is_a?(Numeric)
|
20
35
|
|
@@ -32,6 +47,8 @@ module GitLab
|
|
32
47
|
|
33
48
|
buffer = ""
|
34
49
|
@metrics.each do |name, measurements|
|
50
|
+
buffer << "# HELP #{name} #{self.class.description(name)}\n" if self.class.description(name)
|
51
|
+
|
35
52
|
measurements.each do |measurement|
|
36
53
|
buffer << name.to_s
|
37
54
|
labels = (measurement[:labels] || {}).map { |label, value| "#{label}=\"#{value}\"" }.join(",")
|
@@ -7,8 +7,12 @@ module GitLab
|
|
7
7
|
#
|
8
8
|
# It takes the Redis URL Sidekiq is connected to
|
9
9
|
class SidekiqProber
|
10
|
-
|
11
|
-
|
10
|
+
# The maximum depth (from the head) of each queue to probe. Probing the
|
11
|
+
# entirety of a very large queue will take longer and run the risk of
|
12
|
+
# timing out. But when we have a very large queue, we are most in need of
|
13
|
+
# reliable metrics. This trades off completeness for predictability by
|
14
|
+
# only taking a limited amount of items from the head of the queue.
|
15
|
+
PROBE_JOBS_LIMIT = 1_000
|
12
16
|
|
13
17
|
POOL_SIZE = 3
|
14
18
|
|
@@ -17,6 +21,9 @@ module GitLab
|
|
17
21
|
# needed to be re-initialized
|
18
22
|
POOL_TIMEOUT = 90
|
19
23
|
|
24
|
+
PrometheusMetrics.describe("sidekiq_enqueued_jobs",
|
25
|
+
"Total number of jobs enqueued by class name. Only inspects the first #{PROBE_JOBS_LIMIT} jobs per queue.") # rubocop:disable Layout/LineLength
|
26
|
+
|
20
27
|
def self.connection_pool
|
21
28
|
@@connection_pool ||= Hash.new do |h, connection_hash| # rubocop:disable Style/ClassVars
|
22
29
|
config = connection_hash.merge(pool_timeout: POOL_TIMEOUT, size: POOL_SIZE)
|
@@ -63,17 +70,30 @@ module GitLab
|
|
63
70
|
end
|
64
71
|
|
65
72
|
def probe_jobs
|
73
|
+
puts "[REMOVED] probe_jobs is now considered obsolete and does not emit any metrics,"\
|
74
|
+
" please use probe_jobs_limit instead"
|
75
|
+
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Count worker classes present in Sidekiq queues. This only looks at the
|
80
|
+
# first PROBE_JOBS_LIMIT jobs in each queue. This means that we run a
|
81
|
+
# single LRANGE command for each queue, which does not block other
|
82
|
+
# commands. For queues over PROBE_JOBS_LIMIT in size, this means that we
|
83
|
+
# will not have completely accurate statistics, but the probe performance
|
84
|
+
# will also not degrade as the queue gets larger.
|
85
|
+
def probe_jobs_limit
|
66
86
|
with_sidekiq do
|
67
|
-
job_stats =
|
87
|
+
job_stats = Hash.new(0)
|
68
88
|
|
69
89
|
Sidekiq::Queue.all.each do |queue|
|
70
90
|
Sidekiq.redis do |conn|
|
71
|
-
|
72
|
-
|
91
|
+
conn.lrange("queue:#{queue.name}", 0, PROBE_JOBS_LIMIT).each do |job|
|
92
|
+
job_class = Sidekiq.load_json(job)["class"]
|
93
|
+
|
94
|
+
job_stats[job_class] += 1
|
95
|
+
end
|
73
96
|
end
|
74
|
-
rescue Redis::CommandError # Could happen if the script exceeded the maximum run time (5 seconds by default)
|
75
|
-
# FIXME: Should we call SCRIPT KILL?
|
76
|
-
return self
|
77
97
|
end
|
78
98
|
|
79
99
|
job_stats.each do |class_name, count|
|
@@ -23,4 +23,21 @@ describe GitLab::Exporter::PrometheusMetrics do
|
|
23
23
|
subject.add("mymetric", "invalid", mylabel: "x", myotherlabel: "y").to_s
|
24
24
|
}.to raise_error(RuntimeError)
|
25
25
|
end
|
26
|
+
|
27
|
+
it "supports described metrics" do
|
28
|
+
time = Time.now
|
29
|
+
|
30
|
+
allow(Time).to receive(:now).and_return(time)
|
31
|
+
|
32
|
+
described_class.describe("mymetric", "description")
|
33
|
+
described_class.describe("missingmetric", "otherdescription")
|
34
|
+
subject.add("mymetric", 1.3, mylabel: "x", myotherlabel: "y")
|
35
|
+
|
36
|
+
expect(subject.to_s).to eq(<<~METRICS)
|
37
|
+
# HELP mymetric description
|
38
|
+
mymetric{mylabel="x",myotherlabel="y"} 1.3 #{(time.to_f * 1000).to_i}
|
39
|
+
METRICS
|
40
|
+
|
41
|
+
described_class.clear_descriptions
|
42
|
+
end
|
26
43
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-exporter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 11.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pablo Carranza
|
@@ -14,114 +14,114 @@ dependencies:
|
|
14
14
|
name: connection_pool
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.2.
|
19
|
+
version: 2.2.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 2.2.
|
26
|
+
version: 2.2.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.2.3
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.2.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: puma
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - '='
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: 5.
|
47
|
+
version: 5.3.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - '='
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 5.
|
54
|
+
version: 5.3.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: quantile
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.2.
|
61
|
+
version: 0.2.1
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.2.
|
68
|
+
version: 0.2.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: redis
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - '='
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 4.1.
|
75
|
+
version: 4.1.4
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - '='
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 4.1.
|
82
|
+
version: 4.1.4
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: redis-namespace
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 1.6.0
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.6.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: sidekiq
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 5.2.
|
103
|
+
version: 5.2.9
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 5.2.
|
110
|
+
version: 5.2.9
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: sinatra
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - '='
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 2.0.
|
117
|
+
version: 2.0.8.1
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - '='
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 2.0.
|
124
|
+
version: 2.0.8.1
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rspec
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -189,7 +189,6 @@ files:
|
|
189
189
|
- lib/gitlab_exporter/prometheus.rb
|
190
190
|
- lib/gitlab_exporter/ruby.rb
|
191
191
|
- lib/gitlab_exporter/sidekiq.rb
|
192
|
-
- lib/gitlab_exporter/sidekiq_queue_job_stats.lua
|
193
192
|
- lib/gitlab_exporter/util.rb
|
194
193
|
- lib/gitlab_exporter/version.rb
|
195
194
|
- lib/gitlab_exporter/web_exporter.rb
|
@@ -1,42 +0,0 @@
|
|
1
|
-
--
|
2
|
-
-- Adapted from https://github.com/mperham/sidekiq/blob/2f9258e4fe77991c526f7a65c92bcf792eef8338/lib/sidekiq/api.rb#L231
|
3
|
-
--
|
4
|
-
local queue_name = KEYS[1]
|
5
|
-
local initial_size = redis.call('llen', queue_name)
|
6
|
-
local deleted_size = 0
|
7
|
-
local page = 0
|
8
|
-
local page_size = 2000
|
9
|
-
local temp_job_stats = {}
|
10
|
-
local final_job_stats = {}
|
11
|
-
|
12
|
-
while true do
|
13
|
-
local range_start = page * page_size - deleted_size
|
14
|
-
local range_end = range_start + page_size - 1
|
15
|
-
local entries = redis.call('lrange', queue_name, range_start, range_end)
|
16
|
-
|
17
|
-
if #entries == 0 then
|
18
|
-
break
|
19
|
-
end
|
20
|
-
|
21
|
-
page = page + 1
|
22
|
-
|
23
|
-
for index, entry in next, entries do
|
24
|
-
local class = cjson.decode(entry)['class']
|
25
|
-
if class ~= nil then
|
26
|
-
if temp_job_stats[class] ~= nil then
|
27
|
-
temp_job_stats[class] = temp_job_stats[class] + 1
|
28
|
-
else
|
29
|
-
temp_job_stats[class] = 1
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
deleted_size = initial_size - redis.call('llen', queue_name)
|
35
|
-
end
|
36
|
-
|
37
|
-
for class, count in next, temp_job_stats do
|
38
|
-
local stat_entry = {class, count}
|
39
|
-
table.insert(final_job_stats, stat_entry)
|
40
|
-
end
|
41
|
-
|
42
|
-
return final_job_stats
|