gitlab-monitor 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.gitlab-ci.yml +18 -0
  4. data/.rubocop.yml +34 -0
  5. data/CONTRIBUTING.md +651 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +75 -0
  8. data/LICENSE +25 -0
  9. data/README.md +110 -0
  10. data/bin/gitlab-mon +17 -0
  11. data/config/gitlab-monitor.yml.example +112 -0
  12. data/gitlab-monitor.gemspec +33 -0
  13. data/lib/gitlab_monitor.rb +18 -0
  14. data/lib/gitlab_monitor/cli.rb +341 -0
  15. data/lib/gitlab_monitor/database.rb +13 -0
  16. data/lib/gitlab_monitor/database/base.rb +44 -0
  17. data/lib/gitlab_monitor/database/bloat.rb +74 -0
  18. data/lib/gitlab_monitor/database/bloat_btree.sql +84 -0
  19. data/lib/gitlab_monitor/database/bloat_table.sql +63 -0
  20. data/lib/gitlab_monitor/database/ci_builds.rb +527 -0
  21. data/lib/gitlab_monitor/database/remote_mirrors.rb +74 -0
  22. data/lib/gitlab_monitor/database/row_count.rb +164 -0
  23. data/lib/gitlab_monitor/database/tuple_stats.rb +53 -0
  24. data/lib/gitlab_monitor/git.rb +144 -0
  25. data/lib/gitlab_monitor/memstats.rb +98 -0
  26. data/lib/gitlab_monitor/memstats/mapping.rb +91 -0
  27. data/lib/gitlab_monitor/prober.rb +40 -0
  28. data/lib/gitlab_monitor/process.rb +122 -0
  29. data/lib/gitlab_monitor/prometheus.rb +64 -0
  30. data/lib/gitlab_monitor/sidekiq.rb +149 -0
  31. data/lib/gitlab_monitor/sidekiq_queue_job_stats.lua +42 -0
  32. data/lib/gitlab_monitor/util.rb +83 -0
  33. data/lib/gitlab_monitor/version.rb +5 -0
  34. data/lib/gitlab_monitor/web_exporter.rb +77 -0
  35. data/spec/cli_spec.rb +31 -0
  36. data/spec/database/bloat_spec.rb +99 -0
  37. data/spec/database/ci_builds_spec.rb +421 -0
  38. data/spec/database/row_count_spec.rb +37 -0
  39. data/spec/fixtures/smaps/sample.txt +10108 -0
  40. data/spec/git_process_proper_spec.rb +27 -0
  41. data/spec/git_spec.rb +52 -0
  42. data/spec/memstats_spec.rb +28 -0
  43. data/spec/prometheus_metrics_spec.rb +17 -0
  44. data/spec/spec_helper.rb +63 -0
  45. data/spec/util_spec.rb +15 -0
  46. metadata +225 -0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem "rspec", "~>3.5"
7
+ gem "rubocop", "~>0.42"
8
+ end
@@ -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.
@@ -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
@@ -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