gitlab-monitor 4.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 +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
|