metrux 1.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 +13 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -0
- data/.ruby-version.sample +1 -0
- data/CHANGELOG.md +110 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +429 -0
- data/Rakefile +2 -0
- data/bin/console +9 -0
- data/bin/playground +38 -0
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/bin/rubocop +17 -0
- data/bin/setup +15 -0
- data/config/metrux.sample.yml +46 -0
- data/lib/metrux.rb +60 -0
- data/lib/metrux/client.rb +40 -0
- data/lib/metrux/commands.rb +12 -0
- data/lib/metrux/commands/base.rb +62 -0
- data/lib/metrux/commands/gauge.rb +21 -0
- data/lib/metrux/commands/meter.rb +15 -0
- data/lib/metrux/commands/notice_error.rb +38 -0
- data/lib/metrux/commands/periodic_gauge.rb +25 -0
- data/lib/metrux/commands/periodic_gauge/agent.rb +68 -0
- data/lib/metrux/commands/periodic_gauge/registry.rb +39 -0
- data/lib/metrux/commands/periodic_gauge/reporter.rb +33 -0
- data/lib/metrux/commands/periodic_gauge/supervisor.rb +53 -0
- data/lib/metrux/commands/timer.rb +30 -0
- data/lib/metrux/commands/write.rb +9 -0
- data/lib/metrux/config_builders.rb +11 -0
- data/lib/metrux/config_builders/common.rb +53 -0
- data/lib/metrux/config_builders/influx.rb +71 -0
- data/lib/metrux/config_builders/logger.rb +48 -0
- data/lib/metrux/config_builders/periodic_gauge.rb +21 -0
- data/lib/metrux/config_builders/yaml.rb +64 -0
- data/lib/metrux/configuration.rb +52 -0
- data/lib/metrux/connections.rb +7 -0
- data/lib/metrux/connections/influx_db.rb +17 -0
- data/lib/metrux/connections/null.rb +19 -0
- data/lib/metrux/constants.rb +19 -0
- data/lib/metrux/loggable.rb +24 -0
- data/lib/metrux/plugin_register.rb +57 -0
- data/lib/metrux/plugins.rb +10 -0
- data/lib/metrux/plugins/gc.rb +52 -0
- data/lib/metrux/plugins/periodic_gauge.rb +32 -0
- data/lib/metrux/plugins/process.rb +71 -0
- data/lib/metrux/plugins/thread.rb +13 -0
- data/lib/metrux/plugins/yarv.rb +26 -0
- data/lib/metrux/sleeper.rb +8 -0
- data/lib/metrux/version.rb +3 -0
- data/metrux.gemspec +40 -0
- metadata +250 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
module Metrux
|
2
|
+
module ConfigBuilders
|
3
|
+
class Yaml
|
4
|
+
FileLoadError = Class.new(ConfigurationError)
|
5
|
+
EnvironmentNotFoundError = Class.new(FileLoadError)
|
6
|
+
|
7
|
+
def initialize(config_path, env)
|
8
|
+
@config_path = config_path
|
9
|
+
@env = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def build
|
13
|
+
file_exists? ? yaml_content : null_content
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :config_path, :env
|
19
|
+
|
20
|
+
def file_exists?
|
21
|
+
File.exist?(config_path)
|
22
|
+
end
|
23
|
+
|
24
|
+
def null_content
|
25
|
+
{}
|
26
|
+
end
|
27
|
+
|
28
|
+
def yaml_content
|
29
|
+
from_environment(load_file(config_path)).with_indifferent_access
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_file(config_path)
|
33
|
+
content = File.read(config_path)
|
34
|
+
template = ERB.new(content)
|
35
|
+
YAML.load(template.result)
|
36
|
+
rescue => e
|
37
|
+
raise(FileLoadError, "#{e.class}: #{e.message}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def from_environment(config_content)
|
41
|
+
config_content.fetch(env)
|
42
|
+
rescue KeyError => e
|
43
|
+
if env == default_environment
|
44
|
+
raise(EnvironmentNotFoundError, "#{e.class}: #{e.message}")
|
45
|
+
end
|
46
|
+
|
47
|
+
warn_environment_change
|
48
|
+
@env = default_environment
|
49
|
+
retry
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_environment
|
53
|
+
Configuration::DEFAULT_ENVIRONMENT
|
54
|
+
end
|
55
|
+
|
56
|
+
def warn_environment_change
|
57
|
+
Kernel.warn(
|
58
|
+
"[WARNING] Metrux's configuration wasn't found for environment "\
|
59
|
+
"\"#{env}\". Switching to default: \"#{default_environment}\"."
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Metrux
|
2
|
+
class Configuration
|
3
|
+
DEFAULT_ENVIRONMENT = 'development'.freeze
|
4
|
+
|
5
|
+
def initialize(
|
6
|
+
config_path = File.join(File.expand_path('.'), 'config', 'metrux.yml')
|
7
|
+
)
|
8
|
+
@config_path = config_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def env
|
12
|
+
@env ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || DEFAULT_ENVIRONMENT
|
13
|
+
end
|
14
|
+
|
15
|
+
def app_name
|
16
|
+
@app_name ||= commons[:app_name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def prefix
|
20
|
+
@prefix ||= commons[:prefix]
|
21
|
+
end
|
22
|
+
|
23
|
+
def active?
|
24
|
+
@active ||= commons[:active]
|
25
|
+
end
|
26
|
+
|
27
|
+
def influx
|
28
|
+
@influx ||= ConfigBuilders::Influx.new(yaml).build
|
29
|
+
end
|
30
|
+
|
31
|
+
def periodic_gauge_interval
|
32
|
+
@periodic_gauge_interval ||=
|
33
|
+
ConfigBuilders::PeriodicGauge.new(yaml).build
|
34
|
+
end
|
35
|
+
|
36
|
+
def logger
|
37
|
+
@logger ||= ConfigBuilders::Logger.new(yaml).build
|
38
|
+
end
|
39
|
+
|
40
|
+
def yaml
|
41
|
+
@yaml ||= ConfigBuilders::Yaml.new(config_path, env).build
|
42
|
+
end
|
43
|
+
|
44
|
+
def commons
|
45
|
+
@commons ||= ConfigBuilders::Common.new(yaml).build
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
attr_reader :config_path
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Connections
|
3
|
+
class InfluxDb
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegator :client, :write_point
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@client = ::InfluxDB::Client.new(config.influx)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :client
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Connections
|
3
|
+
class Null
|
4
|
+
include Metrux::Loggable
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
@logger = config.logger
|
8
|
+
end
|
9
|
+
|
10
|
+
(InfluxDb.public_instance_methods - public_instance_methods)
|
11
|
+
.each do |method|
|
12
|
+
define_method(method) do |*args, &block|
|
13
|
+
log("Calling #{method} with #{args}. Block given? #{block.present?}")
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Metrux
|
2
|
+
HOST = Socket.gethostname.freeze
|
3
|
+
MAIN_PROGRAM_NAME = $PROGRAM_NAME
|
4
|
+
.split('/').last
|
5
|
+
.split(' ').first.gsub(/\W/, '')
|
6
|
+
.freeze
|
7
|
+
PUMA_WORKER =
|
8
|
+
$PROGRAM_NAME
|
9
|
+
.split('/').last
|
10
|
+
.scan(/^puma: cluster worker (\d+): */)
|
11
|
+
.flatten.last
|
12
|
+
.freeze
|
13
|
+
|
14
|
+
PROGRAM_NAME = if PUMA_WORKER.present?
|
15
|
+
"#{MAIN_PROGRAM_NAME}-#{PUMA_WORKER}"
|
16
|
+
else
|
17
|
+
MAIN_PROGRAM_NAME
|
18
|
+
end.freeze
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Loggable
|
3
|
+
PREFIX_PROGRAM_NAME = 'metrux'.freeze
|
4
|
+
private_constant :PREFIX_PROGRAM_NAME
|
5
|
+
|
6
|
+
LOG_PROGRAM_NAME = "#{PREFIX_PROGRAM_NAME}/#{Metrux::PROGRAM_NAME}".freeze
|
7
|
+
private_constant :LOG_PROGRAM_NAME
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def log(message, severity = :debug)
|
12
|
+
return if __logger__.blank?
|
13
|
+
|
14
|
+
__logger__.public_send(severity, LOG_PROGRAM_NAME) do
|
15
|
+
"[#{self.class}][thread=#{Thread.current.object_id.to_s(16)}] " \
|
16
|
+
"#{message}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def __logger__
|
21
|
+
@logger || Metrux.logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Metrux
|
2
|
+
class PluginRegister
|
3
|
+
include Loggable
|
4
|
+
|
5
|
+
# Registered plugins
|
6
|
+
attr_reader :plugins
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@logger = config.logger
|
11
|
+
@plugins = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Register a plugin
|
15
|
+
#
|
16
|
+
# == Arguments
|
17
|
+
#
|
18
|
+
# * +klass+ - The plugin class
|
19
|
+
# * +options+ - Any option that you might use on plugin
|
20
|
+
# * +block+ - If you don't have a class, you can pass a block that receives
|
21
|
+
# the `config` and `options` as arguments.
|
22
|
+
#
|
23
|
+
# === Examples
|
24
|
+
#
|
25
|
+
# * Passing a class as plugin
|
26
|
+
#
|
27
|
+
# plugin_register.register(
|
28
|
+
# Metrux::Plugins::MyPlugin, tags: { a: 'tag' }
|
29
|
+
# ) # => true
|
30
|
+
#
|
31
|
+
# * Passing a block as plugin
|
32
|
+
#
|
33
|
+
# plugin_register.register(tags: { a: 'tag' }) do |config, options|
|
34
|
+
# # do something
|
35
|
+
# end # => true
|
36
|
+
#
|
37
|
+
def register(klass = nil, **options)
|
38
|
+
plugin = if block_given?
|
39
|
+
-> () { yield(config, options) }
|
40
|
+
else
|
41
|
+
klass.new(config, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
log("Registering plugin #{plugin.class}")
|
45
|
+
|
46
|
+
plugin.call
|
47
|
+
|
48
|
+
@plugins << plugin
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :config, :logger
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Plugins
|
3
|
+
class Gc < PeriodicGauge
|
4
|
+
def data
|
5
|
+
{
|
6
|
+
count: count, major_count: major_count, minor_count: minor_count,
|
7
|
+
total_allocated_objects: total_allocated_objects,
|
8
|
+
heap_live: heap_live, heap_free: heap_free
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def key
|
13
|
+
'gc'.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def count
|
19
|
+
::GC.count
|
20
|
+
end
|
21
|
+
|
22
|
+
def major_count
|
23
|
+
gc_stats[:major_gc_count]
|
24
|
+
end
|
25
|
+
|
26
|
+
def minor_count
|
27
|
+
gc_stats[:minor_gc_count]
|
28
|
+
end
|
29
|
+
|
30
|
+
def total_allocated_objects
|
31
|
+
gc_stats[:total_allocated_objects] ||
|
32
|
+
gc_stats[:total_allocated_object]
|
33
|
+
end
|
34
|
+
|
35
|
+
def heap_live
|
36
|
+
gc_stats[:heap_live_slots] ||
|
37
|
+
gc_stats[:heap_live_slot] ||
|
38
|
+
gc_stats[:heap_live_num]
|
39
|
+
end
|
40
|
+
|
41
|
+
def heap_free
|
42
|
+
gc_stats[:heap_free_slots] ||
|
43
|
+
gc_stats[:heap_free_slot] ||
|
44
|
+
gc_stats[:heap_free_num]
|
45
|
+
end
|
46
|
+
|
47
|
+
def gc_stats
|
48
|
+
::GC.stat
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Plugins
|
3
|
+
class PeriodicGauge
|
4
|
+
def initialize(config, options = {})
|
5
|
+
@config = config
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
Metrux.periodic_gauge(key, options) { data }
|
11
|
+
end
|
12
|
+
|
13
|
+
def key
|
14
|
+
not_implemented
|
15
|
+
end
|
16
|
+
|
17
|
+
def data
|
18
|
+
not_implemented
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
attr_reader :config, :options
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def not_implemented
|
28
|
+
raise NotImplementedError, 'This is a base plugin'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Metrux
|
2
|
+
module Plugins
|
3
|
+
class Process < PeriodicGauge
|
4
|
+
def initialize(*)
|
5
|
+
super
|
6
|
+
@pid = ::Process.pid
|
7
|
+
end
|
8
|
+
|
9
|
+
def data
|
10
|
+
{ rss: rss }
|
11
|
+
end
|
12
|
+
|
13
|
+
def key
|
14
|
+
'process'.freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :pid
|
20
|
+
|
21
|
+
def rss
|
22
|
+
case ::RbConfig::CONFIG['host_os']
|
23
|
+
when /darwin|mac os/
|
24
|
+
default_rss
|
25
|
+
when /linux/
|
26
|
+
linux_rss
|
27
|
+
else
|
28
|
+
0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def linux_rss
|
33
|
+
statm? ? (fetch_statm_rss * kernel_page_size) / 1_024 : default_rss
|
34
|
+
rescue
|
35
|
+
0
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_rss
|
39
|
+
exec("ps -o rss= -p #{pid}").chomp.to_i
|
40
|
+
rescue
|
41
|
+
0
|
42
|
+
end
|
43
|
+
|
44
|
+
def kernel_page_size
|
45
|
+
@kernel_page_size ||= fetch_pagesize
|
46
|
+
end
|
47
|
+
|
48
|
+
def statm_path
|
49
|
+
@statm_path ||= "/proc/#{pid}/statm".freeze
|
50
|
+
end
|
51
|
+
|
52
|
+
def statm?
|
53
|
+
@statm_found ||= ::File.exist?(statm_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_statm_rss
|
57
|
+
::File.read(statm_path).split(' ')[1].to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_pagesize
|
61
|
+
exec('getconf PAGESIZE').chomp.to_i
|
62
|
+
rescue
|
63
|
+
4_096
|
64
|
+
end
|
65
|
+
|
66
|
+
def exec(cmd)
|
67
|
+
::Kernel.public_send(:`, cmd)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|