gitlab-monitor 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.gitlab-ci.yml +18 -0
- data/.rubocop.yml +34 -0
- data/CONTRIBUTING.md +651 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +75 -0
- data/LICENSE +25 -0
- data/README.md +110 -0
- data/bin/gitlab-mon +17 -0
- data/config/gitlab-monitor.yml.example +112 -0
- data/gitlab-monitor.gemspec +33 -0
- data/lib/gitlab_monitor.rb +18 -0
- data/lib/gitlab_monitor/cli.rb +341 -0
- data/lib/gitlab_monitor/database.rb +13 -0
- data/lib/gitlab_monitor/database/base.rb +44 -0
- data/lib/gitlab_monitor/database/bloat.rb +74 -0
- data/lib/gitlab_monitor/database/bloat_btree.sql +84 -0
- data/lib/gitlab_monitor/database/bloat_table.sql +63 -0
- data/lib/gitlab_monitor/database/ci_builds.rb +527 -0
- data/lib/gitlab_monitor/database/remote_mirrors.rb +74 -0
- data/lib/gitlab_monitor/database/row_count.rb +164 -0
- data/lib/gitlab_monitor/database/tuple_stats.rb +53 -0
- data/lib/gitlab_monitor/git.rb +144 -0
- data/lib/gitlab_monitor/memstats.rb +98 -0
- data/lib/gitlab_monitor/memstats/mapping.rb +91 -0
- data/lib/gitlab_monitor/prober.rb +40 -0
- data/lib/gitlab_monitor/process.rb +122 -0
- data/lib/gitlab_monitor/prometheus.rb +64 -0
- data/lib/gitlab_monitor/sidekiq.rb +149 -0
- data/lib/gitlab_monitor/sidekiq_queue_job_stats.lua +42 -0
- data/lib/gitlab_monitor/util.rb +83 -0
- data/lib/gitlab_monitor/version.rb +5 -0
- data/lib/gitlab_monitor/web_exporter.rb +77 -0
- data/spec/cli_spec.rb +31 -0
- data/spec/database/bloat_spec.rb +99 -0
- data/spec/database/ci_builds_spec.rb +421 -0
- data/spec/database/row_count_spec.rb +37 -0
- data/spec/fixtures/smaps/sample.txt +10108 -0
- data/spec/git_process_proper_spec.rb +27 -0
- data/spec/git_spec.rb +52 -0
- data/spec/memstats_spec.rb +28 -0
- data/spec/prometheus_metrics_spec.rb +17 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/util_spec.rb +15 -0
- metadata +225 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gitlab-monitor (4.0.0)
|
5
|
+
connection_pool (~> 2.2.1)
|
6
|
+
pg (~> 1.1)
|
7
|
+
quantile (~> 0.2.0)
|
8
|
+
redis (~> 3.2)
|
9
|
+
redis-namespace (~> 1.6.0)
|
10
|
+
sidekiq (~> 5.2.1)
|
11
|
+
sinatra (~> 2.0.4)
|
12
|
+
|
13
|
+
GEM
|
14
|
+
remote: https://rubygems.org/
|
15
|
+
specs:
|
16
|
+
ast (2.4.0)
|
17
|
+
connection_pool (2.2.2)
|
18
|
+
diff-lcs (1.3)
|
19
|
+
mustermann (1.0.3)
|
20
|
+
parser (2.5.1.0)
|
21
|
+
ast (~> 2.4.0)
|
22
|
+
pg (1.1.4)
|
23
|
+
powerpack (0.1.1)
|
24
|
+
quantile (0.2.1)
|
25
|
+
rack (2.0.6)
|
26
|
+
rack-protection (2.0.5)
|
27
|
+
rack
|
28
|
+
rainbow (2.1.0)
|
29
|
+
redis (3.3.5)
|
30
|
+
redis-namespace (1.6.0)
|
31
|
+
redis (>= 3.0.4)
|
32
|
+
rspec (3.7.0)
|
33
|
+
rspec-core (~> 3.7.0)
|
34
|
+
rspec-expectations (~> 3.7.0)
|
35
|
+
rspec-mocks (~> 3.7.0)
|
36
|
+
rspec-core (3.7.1)
|
37
|
+
rspec-support (~> 3.7.0)
|
38
|
+
rspec-expectations (3.7.0)
|
39
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
40
|
+
rspec-support (~> 3.7.0)
|
41
|
+
rspec-mocks (3.7.0)
|
42
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
43
|
+
rspec-support (~> 3.7.0)
|
44
|
+
rspec-support (3.7.1)
|
45
|
+
rubocop (0.42.0)
|
46
|
+
parser (>= 2.3.1.1, < 3.0)
|
47
|
+
powerpack (~> 0.1)
|
48
|
+
rainbow (>= 1.99.1, < 3.0)
|
49
|
+
ruby-progressbar (~> 1.7)
|
50
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
51
|
+
ruby-progressbar (1.8.1)
|
52
|
+
sidekiq (5.2.5)
|
53
|
+
connection_pool (~> 2.2, >= 2.2.2)
|
54
|
+
rack (>= 1.5.0)
|
55
|
+
rack-protection (>= 1.5.0)
|
56
|
+
redis (>= 3.3.5, < 5)
|
57
|
+
sinatra (2.0.5)
|
58
|
+
mustermann (~> 1.0)
|
59
|
+
rack (~> 2.0)
|
60
|
+
rack-protection (= 2.0.5)
|
61
|
+
tilt (~> 2.0)
|
62
|
+
tilt (2.0.9)
|
63
|
+
unicode-display_width (1.1.0)
|
64
|
+
|
65
|
+
PLATFORMS
|
66
|
+
ruby
|
67
|
+
|
68
|
+
DEPENDENCIES
|
69
|
+
gitlab-monitor!
|
70
|
+
rspec (~> 3.5)
|
71
|
+
rspec-expectations (~> 3.7.0)
|
72
|
+
rubocop (~> 0.42)
|
73
|
+
|
74
|
+
BUNDLED WITH
|
75
|
+
1.17.3
|
data/LICENSE
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2011-2017 GitLab B.V.
|
2
|
+
|
3
|
+
With regard to the GitLab Software:
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
23
|
+
For all third party components incorporated into the GitLab Software, those
|
24
|
+
components are licensed under the original license provided by the owner of the
|
25
|
+
applicable component.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
## Introduction
|
2
|
+
|
3
|
+
gitlab-monitor is a [Prometheus Web exporter] that does the following:
|
4
|
+
|
5
|
+
1. Collects GitLab production metrics via custom probes defined in a [YAML
|
6
|
+
configuration file](config/gitlab-monitor.yml.example).
|
7
|
+
2. Custom probes gather measurements in the form of key/value pairs.
|
8
|
+
3. For each probe, gitlab-monitor creates an HTTP endpoint `/<probe_name>`
|
9
|
+
(by default on port 9168) that delivers these metrics to a Prometheus scraper.
|
10
|
+
|
11
|
+
A central Prometheus process is configured to poll exporters at a specified
|
12
|
+
frequency.
|
13
|
+
|
14
|
+
### Supported Probes
|
15
|
+
|
16
|
+
Below is a list of probes added by this exporter, and their corresponding
|
17
|
+
metrics.
|
18
|
+
|
19
|
+
1. Database
|
20
|
+
* [Per-table tuple stats](lib/gitlab_monitor/database/tuple_stats.rb) --
|
21
|
+
`gitlab_database_stat_table_*`
|
22
|
+
* [Row count queries](lib/gitlab_monitor/database/row_count.rb) --
|
23
|
+
`gitlab_database_rows`
|
24
|
+
* [CI builds](lib/gitlab_monitor/database/ci_builds.rb) --
|
25
|
+
`ci_pending_builds`, `ci_created_builds`, `ci_stale_builds`,
|
26
|
+
`ci_running_builds`
|
27
|
+
* [Bloat](lib/gitlab_monitor/database/bloat.rb) --
|
28
|
+
`gitlab_database_bloat_$type_$key` with type `btree` (index bloat) or `table`
|
29
|
+
(table bloat) and keys `bloat_ratio bloat_size extra_size real_size` (see below)
|
30
|
+
* [Remote mirrors](lib/gitlab_monitor/database/remote_mirrors.rb) --
|
31
|
+
`project_remote_mirror_last_successful_update_time_seconds`, `project_remote_mirror_last_update_time_seconds`
|
32
|
+
1. Git
|
33
|
+
* [git pull/push timings](lib/gitlab_monitor/git.rb) --
|
34
|
+
`git_pull_time_milliseconds`, `git_push_time_milliseconds`
|
35
|
+
* git processes stats (see Process below)
|
36
|
+
1. [Process](lib/gitlab_monitor/process.rb)
|
37
|
+
* CPU time -- `process_cpu_seconds_total`
|
38
|
+
* Start time -- `process_start_time_seconds`
|
39
|
+
* Count -- `process_count`
|
40
|
+
* Memory usage
|
41
|
+
* Data from /proc/<pid>/cmdline:
|
42
|
+
* `process_resident_memory_bytes`
|
43
|
+
* `process_virtual_memory_bytes`
|
44
|
+
* Data from /proc/<pid>/smaps -- `probe_smaps` (off by default):
|
45
|
+
* `process_smaps_size_bytes`
|
46
|
+
* `process_smaps_rss_bytes`
|
47
|
+
* `process_smaps_shared_clean_bytes`
|
48
|
+
* `process_smaps_shared_dirty_bytes`
|
49
|
+
* `process_smaps_private_clean_bytes`
|
50
|
+
* `process_smaps_private_dirty_bytes`
|
51
|
+
* `process_smaps_swap_bytes`
|
52
|
+
* `process_smaps_pss_bytes`
|
53
|
+
1. [Sidekiq](lib/gitlab_monitor/sidekiq.rb) -- `sidekiq_queue_size`, `sidekiq_queue_paused`,
|
54
|
+
`sidekiq_queue_latency_seconds`, `sidekiq_enqueued_jobs`, `sidekiq_dead_jobs`,
|
55
|
+
`sidekiq_running_jobs`, `sidekiq_to_be_retried_jobs`
|
56
|
+
|
57
|
+
### Setup with GitLab Development Kit
|
58
|
+
|
59
|
+
gitlab-monitor can be setup with the [GitLab Development Kit] for development.
|
60
|
+
When using the gitlab-monitor CLI, you'll need to set the `--db-conn` flag to
|
61
|
+
connect to the PostgreSQL instance in your GDK folder. For example:
|
62
|
+
|
63
|
+
```
|
64
|
+
bin/gitlab-mon row-counts --db-conn="dbname=gitlabhq_development host=/Users/<user>/gitlab-development-kit/postgresql"
|
65
|
+
```
|
66
|
+
|
67
|
+
### Running gitlab-monitor as a Web exporter
|
68
|
+
|
69
|
+
When serving the pages on `localhost`, you'll need to edit the YAML
|
70
|
+
configuration file. An example can be found under
|
71
|
+
[`config/gitlab-monitor.yml.example`](config/gitlab-monitor.yml.example). For
|
72
|
+
each probe that has to connect to the database, set the `connection_string` to
|
73
|
+
`dbname=gitlabhq_development
|
74
|
+
host=/Users/<user>/gitlab-development-kit/postgresql`
|
75
|
+
|
76
|
+
Once you have this configured, you can then run:
|
77
|
+
|
78
|
+
```
|
79
|
+
bin/gitlab-mon web -c config/gitlab-monitor.yml
|
80
|
+
```
|
81
|
+
|
82
|
+
Once running, you can point your browser or curl to the following URLs:
|
83
|
+
|
84
|
+
* http://localhost:9168/database
|
85
|
+
* http://localhost:9168/git_process
|
86
|
+
* http://localhost:9168/process
|
87
|
+
* http://localhost:9168/sidekiq
|
88
|
+
* http://localhost:9168/metrics (to get all of the above combined)
|
89
|
+
|
90
|
+
### Database Bloat Metrics
|
91
|
+
|
92
|
+
Database bloat is measured for indexes (`btree`) and/or tables
|
93
|
+
(`table`). Returned metrics contain:
|
94
|
+
|
95
|
+
* `bloat_ratio`: estimated ratio of the real size used by bloat_size.
|
96
|
+
* `bloat_size`: estimated size of the bloat without the extra space kept for the fillfactor.
|
97
|
+
* `extra_size`: estimated extra size not used/needed by the index. This extra size is composed by the fillfactor, bloat and alignment padding spaces.
|
98
|
+
* `real_size`: real size of the index
|
99
|
+
|
100
|
+
Also see the [original documentation](https://github.com/ioguix/pgsql-bloat-estimation/blob/master/README.md).
|
101
|
+
|
102
|
+
Note that all metrics returned are estimates without an upper bound for
|
103
|
+
the error.
|
104
|
+
|
105
|
+
## Contributing
|
106
|
+
|
107
|
+
gitlab-monitor is an open source project and we are very happy to accept community contributions. Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details.
|
108
|
+
|
109
|
+
[Prometheus Web exporter]: https://prometheus.io/docs/instrumenting/exporters/
|
110
|
+
[GitLab Development Kit]: https://gitlab.com/gitlab-org/gitlab-development-kit
|
data/bin/gitlab-mon
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
4
|
+
|
5
|
+
require "optparse"
|
6
|
+
require "gitlab_monitor"
|
7
|
+
|
8
|
+
def main
|
9
|
+
clazz = GitLab::Monitor::CLI.for(ARGV.shift)
|
10
|
+
runner = clazz.new(ARGV)
|
11
|
+
runner.run
|
12
|
+
rescue GitLab::Monitor::CLI::InvalidCLICommand => e
|
13
|
+
puts e
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
main
|
@@ -0,0 +1,112 @@
|
|
1
|
+
db_common: &db_common
|
2
|
+
methods:
|
3
|
+
- probe_db
|
4
|
+
opts: &db_common_opts
|
5
|
+
connection_string: dbname=gitlabhq_development user=postgres
|
6
|
+
|
7
|
+
# Web server config
|
8
|
+
server:
|
9
|
+
listen_address: 0.0.0.0
|
10
|
+
listen_port: 9168
|
11
|
+
# Maximum amount of memory to use in megabytes, after which the process is killed
|
12
|
+
memory_threshold: 1024
|
13
|
+
|
14
|
+
# Probes config
|
15
|
+
probes:
|
16
|
+
# Each key corresponds to an endpoint, so here metrics are available at http://localhost:9168/git.
|
17
|
+
# The server will search for a prober using the format `KeyProber`, so here it will be `GitProber`.
|
18
|
+
# If there's no prober matching the format above, `class_name` key should be provided (see `git_process` below).
|
19
|
+
git:
|
20
|
+
# Methods to call on the prober
|
21
|
+
methods:
|
22
|
+
- probe_pull
|
23
|
+
- probe_push
|
24
|
+
# Options to pass to the prober class initializer
|
25
|
+
opts:
|
26
|
+
source: /home/git/repo
|
27
|
+
|
28
|
+
git_process: &git_process
|
29
|
+
class_name: GitProcessProber # `class_name` is redundant here
|
30
|
+
methods:
|
31
|
+
- probe_git
|
32
|
+
opts:
|
33
|
+
quantiles: true
|
34
|
+
|
35
|
+
database_bloat:
|
36
|
+
class_name: Database::BloatProber
|
37
|
+
<<: *db_common
|
38
|
+
|
39
|
+
# We can group multiple probes under a single endpoint by setting the `multiple` key to `true`, followed
|
40
|
+
# by probe definitions as usual.
|
41
|
+
database:
|
42
|
+
multiple: true
|
43
|
+
ci_builds:
|
44
|
+
class_name: Database::CiBuildsProber
|
45
|
+
<<: *db_common
|
46
|
+
opts:
|
47
|
+
<<: *db_common_opts
|
48
|
+
allowed_repeated_commands_count: 2
|
49
|
+
created_builds_counting_disabled: true
|
50
|
+
unarchived_traces_offset_minutes: 1440
|
51
|
+
tuple_stats:
|
52
|
+
class_name: Database::TuplesProber
|
53
|
+
<<: *db_common
|
54
|
+
rows_count:
|
55
|
+
class_name: Database::RowCountProber
|
56
|
+
<<: *db_common
|
57
|
+
opts:
|
58
|
+
<<: *db_common_opts
|
59
|
+
selected_queries:
|
60
|
+
- soft_deleted_projects
|
61
|
+
- orphaned_projects
|
62
|
+
- uploads
|
63
|
+
remote_mirrors:
|
64
|
+
class_name: Database::RemoteMirrors
|
65
|
+
<<: *db_common
|
66
|
+
opts:
|
67
|
+
<<: *db_common_opts
|
68
|
+
project_ids:
|
69
|
+
- 1
|
70
|
+
|
71
|
+
process: &process
|
72
|
+
methods:
|
73
|
+
- probe_stat
|
74
|
+
- probe_count
|
75
|
+
opts:
|
76
|
+
- pid_or_pattern: "sidekiq .* \\[.*?\\]"
|
77
|
+
name: sidekiq
|
78
|
+
- pid_or_pattern: "unicorn.* worker\\[.*?\\]"
|
79
|
+
name: unicorn
|
80
|
+
- pid_or_pattern: "git-upload-pack --stateless-rpc"
|
81
|
+
name: git_upload_pack
|
82
|
+
quantiles: true
|
83
|
+
|
84
|
+
sidekiq: &sidekiq
|
85
|
+
methods:
|
86
|
+
- probe_queues
|
87
|
+
- probe_jobs
|
88
|
+
- probe_workers
|
89
|
+
- probe_retries
|
90
|
+
- probe_dead
|
91
|
+
opts:
|
92
|
+
redis_url: "redis://localhost:6379"
|
93
|
+
redis_enable_client: true
|
94
|
+
|
95
|
+
metrics:
|
96
|
+
multiple: true
|
97
|
+
git_process:
|
98
|
+
<<: *git_process
|
99
|
+
process:
|
100
|
+
<<: *process
|
101
|
+
sidekiq:
|
102
|
+
<<: *sidekiq
|
103
|
+
ci_builds:
|
104
|
+
class_name: Database::CiBuildsProber
|
105
|
+
<<: *db_common
|
106
|
+
tuple_stats:
|
107
|
+
class_name: Database::TuplesProber
|
108
|
+
<<: *db_common
|
109
|
+
rows_count:
|
110
|
+
class_name: Database::RowCountProber
|
111
|
+
<<: *db_common
|
112
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "gitlab_monitor/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "gitlab-monitor"
|
7
|
+
s.version = GitLab::Monitor::VERSION
|
8
|
+
s.date = "2016-07-27"
|
9
|
+
s.summary = "GitLab monitoring tools"
|
10
|
+
s.description = "GitLab monitoring tools to use with prometheus"
|
11
|
+
s.authors = ["Pablo Carranza"]
|
12
|
+
s.email = "pablo@gitlab.com"
|
13
|
+
|
14
|
+
s.files = `git ls-files -z`.split("\x0")
|
15
|
+
|
16
|
+
s.executables = ["gitlab-mon"]
|
17
|
+
s.test_files = s.files.grep(%r{^(spec)/})
|
18
|
+
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.homepage = "http://gitlab.com"
|
21
|
+
s.license = "MIT"
|
22
|
+
|
23
|
+
s.add_runtime_dependency "pg", "~> 1.1"
|
24
|
+
s.add_runtime_dependency "sinatra", "~> 2.0.4"
|
25
|
+
s.add_runtime_dependency "quantile", "~> 0.2.0"
|
26
|
+
s.add_runtime_dependency "sidekiq", "~> 5.2.1"
|
27
|
+
s.add_runtime_dependency "redis", "~> 3.2"
|
28
|
+
s.add_runtime_dependency "redis-namespace", "~> 1.6.0"
|
29
|
+
s.add_runtime_dependency "connection_pool", "~> 2.2.1"
|
30
|
+
|
31
|
+
s.add_development_dependency "rspec", "~> 3.7.0"
|
32
|
+
s.add_development_dependency "rspec-expectations", "~> 3.7.0"
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module GitLab
|
2
|
+
# GitLab Monitoring
|
3
|
+
module Monitor
|
4
|
+
autoload :CLI, "gitlab_monitor/cli"
|
5
|
+
autoload :TimeTracker, "gitlab_monitor/util"
|
6
|
+
autoload :Utils, "gitlab_monitor/util"
|
7
|
+
autoload :PrometheusMetrics, "gitlab_monitor/prometheus"
|
8
|
+
autoload :Utils, "gitlab_monitor/util"
|
9
|
+
autoload :Git, "gitlab_monitor/git"
|
10
|
+
autoload :GitProber, "gitlab_monitor/git"
|
11
|
+
autoload :GitProcessProber, "gitlab_monitor/git"
|
12
|
+
autoload :Database, "gitlab_monitor/database"
|
13
|
+
autoload :ProcessProber, "gitlab_monitor/process"
|
14
|
+
autoload :WebExporter, "gitlab_monitor/web_exporter"
|
15
|
+
autoload :Prober, "gitlab_monitor/prober"
|
16
|
+
autoload :SidekiqProber, "gitlab_monitor/sidekiq"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,341 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module GitLab
|
4
|
+
module Monitor
|
5
|
+
# Stores runner classes in a single place
|
6
|
+
#
|
7
|
+
# The entry point is the module method "for" which takes the name of a runner.
|
8
|
+
# In case the runner is invalid it will return a NullRunner which fails with an
|
9
|
+
# InvalidCLICommand error, which contains the general application usage instructions.
|
10
|
+
module CLI
|
11
|
+
EXECUTABLE_NAME = "gitlab-mon".freeze
|
12
|
+
|
13
|
+
def self.for(name)
|
14
|
+
commands.fetch(name, NullRunner)
|
15
|
+
end
|
16
|
+
|
17
|
+
class InvalidCLICommand < RuntimeError; end
|
18
|
+
|
19
|
+
# Empty runner that will raise an InvalidCLICommand when executed to provide the usage
|
20
|
+
# in the exception message
|
21
|
+
class NullRunner
|
22
|
+
def initialize(args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
fail InvalidCLICommand.new("Usage: #{EXECUTABLE_NAME} <command> [options] [arguments...]\n\n"\
|
27
|
+
"Available commands are: #{GitLab::Monitor::CLI.commands.keys.join(', ')}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Git runner.
|
32
|
+
#
|
33
|
+
# Takes something that behaves like ARGV with optparse included as an argument.
|
34
|
+
#
|
35
|
+
# It will take 2 positional arguments once parsed, the first one for the repository location,
|
36
|
+
# the optional second one is an IO like object to write to
|
37
|
+
class GIT
|
38
|
+
COMMAND_NAME = "git".freeze
|
39
|
+
|
40
|
+
attr_reader :source, :target, :labels
|
41
|
+
|
42
|
+
def initialize(args)
|
43
|
+
@options = options(args)
|
44
|
+
args = @options.parse!
|
45
|
+
@source = args.shift
|
46
|
+
@target = args.shift || STDOUT
|
47
|
+
@labels ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def run
|
51
|
+
validate!
|
52
|
+
|
53
|
+
::GitLab::Monitor::GitProber.new(labels: labels, source: source)
|
54
|
+
.probe_pull
|
55
|
+
.probe_push
|
56
|
+
.write_to(@target)
|
57
|
+
end
|
58
|
+
|
59
|
+
def options(args)
|
60
|
+
args.options do |opts|
|
61
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options] repository_path [target_file]"
|
62
|
+
opts.on("-l", "--labels=key=value,key2=value2", "Labels to append to the metrics") do |val|
|
63
|
+
@labels = val.split(",").map { |value| value.split("=").tap { |aa| aa[0] = aa[0].to_sym } }.to_h
|
64
|
+
end
|
65
|
+
opts
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def help
|
70
|
+
@options.help
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate!
|
74
|
+
fail InvalidCLICommand.new(help) if @source.nil?
|
75
|
+
fail InvalidCLICommand.new("Can't find repository #{@source}\n\n#{help}") unless File.directory? @source
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Database tuple stats runner.
|
80
|
+
#
|
81
|
+
# It will take a database connection string and print results to STDOUT
|
82
|
+
class DatabaseTupleStats
|
83
|
+
COMMAND_NAME = "db-tuple-stats".freeze
|
84
|
+
|
85
|
+
def initialize(args)
|
86
|
+
@options = options(args)
|
87
|
+
@options.parse!
|
88
|
+
|
89
|
+
@target = args.shift || STDOUT
|
90
|
+
@target = File.open(@target, "a") if @target.is_a?(String)
|
91
|
+
end
|
92
|
+
|
93
|
+
def options(args)
|
94
|
+
args.options do |opts|
|
95
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
96
|
+
opts.on("--db-conn=\"dbname=test port=5432\"", "Database connection string") do |val|
|
97
|
+
@db_connection_string = val
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def help
|
103
|
+
@options.help
|
104
|
+
end
|
105
|
+
|
106
|
+
def run
|
107
|
+
validate!
|
108
|
+
|
109
|
+
::GitLab::Monitor::Database::TuplesProber.new(connection_string: @db_connection_string)
|
110
|
+
.probe_db
|
111
|
+
.write_to(@target)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def validate!
|
117
|
+
fail InvalidCLICommand.new(help) unless @db_connection_string
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Database row counts query runner.
|
122
|
+
#
|
123
|
+
# This will take the database connection and print the result to STDOUT
|
124
|
+
class DatabaseRowCounts
|
125
|
+
COMMAND_NAME = "row-counts".freeze
|
126
|
+
|
127
|
+
def initialize(args)
|
128
|
+
@options = options(args)
|
129
|
+
@options.parse!
|
130
|
+
|
131
|
+
@target = args.shift || STDOUT
|
132
|
+
@target = File.open(@target, "a") if @target.is_a?(String)
|
133
|
+
end
|
134
|
+
|
135
|
+
def options(args)
|
136
|
+
args.options do |opts|
|
137
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
138
|
+
opts.on("--db-conn=\"dbname=test port=5432\"", "Database connection string") do |val|
|
139
|
+
@db_connection_string = val
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def help
|
145
|
+
@options.help
|
146
|
+
end
|
147
|
+
|
148
|
+
def run
|
149
|
+
validate!
|
150
|
+
|
151
|
+
::GitLab::Monitor::Database::RowCountProber.new(connection_string: @db_connection_string)
|
152
|
+
.probe_db
|
153
|
+
.write_to(@target)
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def validate!
|
159
|
+
fail InvalidCLICommand.new(help) unless @db_connection_string
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Run a web server that exposes the metrics specified in a config file
|
164
|
+
class Server
|
165
|
+
COMMAND_NAME = "web".freeze
|
166
|
+
|
167
|
+
def initialize(args)
|
168
|
+
@options = options(args)
|
169
|
+
@options.parse!
|
170
|
+
end
|
171
|
+
|
172
|
+
def options(args)
|
173
|
+
args.options do |opts|
|
174
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
175
|
+
opts.on("-c config.yml", "Monitoring config") do |val|
|
176
|
+
@config_file = val
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def help
|
182
|
+
@options.help
|
183
|
+
end
|
184
|
+
|
185
|
+
def run
|
186
|
+
validate!
|
187
|
+
|
188
|
+
config = Utils.deep_symbolize_hash_keys(YAML.load_file(@config_file))
|
189
|
+
|
190
|
+
WebExporter.setup(config)
|
191
|
+
WebExporter.run!
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def validate!
|
197
|
+
fail InvalidCLICommand.new(help) unless @config_file
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Process runner
|
202
|
+
#
|
203
|
+
# Takes a pid and name for metrics
|
204
|
+
class Process
|
205
|
+
COMMAND_NAME = "process".freeze
|
206
|
+
|
207
|
+
def initialize(args)
|
208
|
+
@options = options(args)
|
209
|
+
@options.parse!
|
210
|
+
|
211
|
+
@target = args.shift || STDOUT
|
212
|
+
@target = File.open(@target, "a") if @target.is_a?(String)
|
213
|
+
end
|
214
|
+
|
215
|
+
def options(args)
|
216
|
+
args.options do |opts|
|
217
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
218
|
+
opts.on("--pid=123", "Process ID") do |val|
|
219
|
+
@pid = val
|
220
|
+
end
|
221
|
+
opts.on("--pattern=unicorn", "Process command pattern") do |val|
|
222
|
+
@pattern = val
|
223
|
+
end
|
224
|
+
opts.on("--name=NAME", "Process name to be used in metrics") do |val|
|
225
|
+
@name = val
|
226
|
+
end
|
227
|
+
opts.on("--quantiles", "Return quantiles instead of exact metrics") do
|
228
|
+
@quantiles = true
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def help
|
234
|
+
@options.help
|
235
|
+
end
|
236
|
+
|
237
|
+
def run
|
238
|
+
::GitLab::Monitor::ProcessProber.new(pid_or_pattern: @pid || @pattern, name: @name, quantiles: @quantiles)
|
239
|
+
.probe_stat
|
240
|
+
.probe_count
|
241
|
+
.probe_smaps
|
242
|
+
.write_to(@target)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Sidekiq runner.
|
247
|
+
#
|
248
|
+
# It will take a Redis connection URL and print results to STDOUT
|
249
|
+
class SidekiqRunner
|
250
|
+
COMMAND_NAME = "sidekiq".freeze
|
251
|
+
|
252
|
+
def initialize(args)
|
253
|
+
@options = options(args)
|
254
|
+
@options.parse!
|
255
|
+
|
256
|
+
@target = args.shift || STDOUT
|
257
|
+
@target = File.open(@target, "a") if @target.is_a?(String)
|
258
|
+
end
|
259
|
+
|
260
|
+
def options(args)
|
261
|
+
args.options do |opts|
|
262
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
263
|
+
opts.on("--redis-url=\"redis://localhost:6379\"", "Redis URL") do |val|
|
264
|
+
@redis_url = val
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def help
|
270
|
+
@options.help
|
271
|
+
end
|
272
|
+
|
273
|
+
def run
|
274
|
+
validate!
|
275
|
+
|
276
|
+
::GitLab::Monitor::SidekiqProber.new(redis_url: @redis_url)
|
277
|
+
.probe_queues
|
278
|
+
.probe_jobs
|
279
|
+
.probe_workers
|
280
|
+
.probe_retries
|
281
|
+
.write_to(@target)
|
282
|
+
end
|
283
|
+
|
284
|
+
private
|
285
|
+
|
286
|
+
def validate!
|
287
|
+
fail InvalidCLICommand.new(help) unless @redis_url
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Process runner
|
292
|
+
#
|
293
|
+
# Takes a pid and name for metrics
|
294
|
+
class GitProcess
|
295
|
+
COMMAND_NAME = "git-process".freeze
|
296
|
+
|
297
|
+
def initialize(args)
|
298
|
+
@options = options(args)
|
299
|
+
@options.parse!
|
300
|
+
|
301
|
+
@target = args.shift || STDOUT
|
302
|
+
@target = File.open(@target, "a") if @target.is_a?(String)
|
303
|
+
end
|
304
|
+
|
305
|
+
def options(args)
|
306
|
+
args.options do |opts|
|
307
|
+
opts.banner = "Usage: #{EXECUTABLE_NAME} #{COMMAND_NAME} [options]"
|
308
|
+
opts.on("--quantiles", "Return quantiles instead of exact metrics") do
|
309
|
+
@quantiles = true
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def help
|
315
|
+
@options.help
|
316
|
+
end
|
317
|
+
|
318
|
+
def run
|
319
|
+
::GitLab::Monitor::GitProcessProber.new(quantiles: @quantiles)
|
320
|
+
.probe_git
|
321
|
+
.write_to(@target)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def self.commands
|
326
|
+
[
|
327
|
+
GIT,
|
328
|
+
DatabaseTupleStats,
|
329
|
+
DatabaseRowCounts,
|
330
|
+
Process,
|
331
|
+
GitProcess,
|
332
|
+
SidekiqRunner,
|
333
|
+
Server
|
334
|
+
].each_with_object({}) do |command_class, commands|
|
335
|
+
commands[command_class::COMMAND_NAME] = command_class
|
336
|
+
commands
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|