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.
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