fluent-plugin-node-exporter-metrics 0.1.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/.github/workflows/linux-test.yaml +42 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/LICENSE +202 -0
- data/README.md +167 -0
- data/Rakefile +13 -0
- data/docs/cpu.md +26 -0
- data/docs/cpufreq.md +43 -0
- data/docs/diskstats.md +27 -0
- data/docs/filefd.md +12 -0
- data/docs/loadavg.md +13 -0
- data/docs/meminfo.md +61 -0
- data/docs/netdev.md +26 -0
- data/docs/stat.md +17 -0
- data/docs/time.md +9 -0
- data/docs/uname.md +15 -0
- data/docs/vmstat.md +17 -0
- data/fluent-plugin-node-exporter-metrics.gemspec +31 -0
- data/lib/fluent/plugin/in_node_exporter_metrics.rb +138 -0
- data/lib/fluent/plugin/node_exporter/cmetrics_dataschema_parser.rb +84 -0
- data/lib/fluent/plugin/node_exporter/collector.rb +41 -0
- data/lib/fluent/plugin/node_exporter/cpu_collector.rb +130 -0
- data/lib/fluent/plugin/node_exporter/cpufreq_collector.rb +97 -0
- data/lib/fluent/plugin/node_exporter/diskstats_collector.rb +280 -0
- data/lib/fluent/plugin/node_exporter/filefd_collector.rb +60 -0
- data/lib/fluent/plugin/node_exporter/loadavg_collector.rb +64 -0
- data/lib/fluent/plugin/node_exporter/meminfo_collector.rb +61 -0
- data/lib/fluent/plugin/node_exporter/netdev_collector.rb +92 -0
- data/lib/fluent/plugin/node_exporter/stat_collector.rb +84 -0
- data/lib/fluent/plugin/node_exporter/time_collector.rb +50 -0
- data/lib/fluent/plugin/node_exporter/uname_collector.rb +64 -0
- data/lib/fluent/plugin/node_exporter/vmstat_collector.rb +56 -0
- data/lib/fluent/plugin/parser_node_exporter_metrics.rb +54 -0
- data/test/fixtures/cpu/with_thermal_throttle/proc/stat +3 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu0/thermal_throttle/core_throttle_count +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu0/thermal_throttle/package_throttle_count +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu0/topology/core_id +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu0/topology/physical_package_id +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu1/thermal_throttle/core_throttle_count +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu1/thermal_throttle/package_throttle_count +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu1/topology/core_id +1 -0
- data/test/fixtures/cpu/with_thermal_throttle/sys/devices/system/cpu/cpu1/topology/physical_package_id +1 -0
- data/test/fixtures/cpu/without_thermal_throttle/proc/stat +3 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_cur_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_max_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_min_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq +1 -0
- data/test/fixtures/cpufreq/with_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_max_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_min_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq +1 -0
- data/test/fixtures/cpufreq/without_cur_freq/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq +1 -0
- data/test/helper.rb +24 -0
- data/test/plugin/test_cmetrics_data_schema_parser.rb +83 -0
- data/test/plugin/test_cpu_collector.rb +84 -0
- data/test/plugin/test_cpufreq_collector.rb +75 -0
- data/test/plugin/test_diskstats_collector.rb +200 -0
- data/test/plugin/test_filefd_collector.rb +39 -0
- data/test/plugin/test_in_node_exporter_metrics.rb +583 -0
- data/test/plugin/test_loadavg_collector.rb +41 -0
- data/test/plugin/test_meminfo_collector.rb +47 -0
- data/test/plugin/test_netdev_collector.rb +35 -0
- data/test/plugin/test_stat_collector.rb +37 -0
- data/test/plugin/test_time_collector.rb +15 -0
- data/test/plugin/test_uname_collector.rb +47 -0
- data/test/plugin/test_vmstat_collector.rb +53 -0
- metadata +273 -0
data/docs/stat.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Stat Collector
|
|
2
|
+
|
|
3
|
+
## Metric and label naming
|
|
4
|
+
|
|
5
|
+
* node_boot_time_seconds
|
|
6
|
+
* node_context_switches_total
|
|
7
|
+
* node_forks_total
|
|
8
|
+
* node_intr_total
|
|
9
|
+
* node_procs_blocked
|
|
10
|
+
* node_procs_running
|
|
11
|
+
|
|
12
|
+
## Metric and its data sources
|
|
13
|
+
|
|
14
|
+
Stat collector access the following data sources.
|
|
15
|
+
|
|
16
|
+
* /proc/stat
|
|
17
|
+
|
data/docs/time.md
ADDED
data/docs/uname.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Uname Collector
|
|
2
|
+
|
|
3
|
+
## Metric and label naming
|
|
4
|
+
|
|
5
|
+
* node_uname_info {"sysname"=>..., "release"=>..., "version"=>..., "machine"=>..., "nodename"=>..., "domainname"=>...}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Metric and its data sources
|
|
9
|
+
|
|
10
|
+
Uname collector access the following data sources.
|
|
11
|
+
|
|
12
|
+
* Etc.uname
|
|
13
|
+
|
|
14
|
+
NOTE: `Etc.uname` returns at least sysname,release,version,machine,nodename
|
|
15
|
+
but it is not guaranteed to return domainname.
|
data/docs/vmstat.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Vmstat Collector
|
|
2
|
+
|
|
3
|
+
## Metric and label naming
|
|
4
|
+
|
|
5
|
+
* node_vmstat_oom_kill
|
|
6
|
+
* node_vmstat_pgfault
|
|
7
|
+
* node_vmstat_pgmajfault
|
|
8
|
+
* node_vmstat_pgpgin
|
|
9
|
+
* node_vmstat_pgpgout
|
|
10
|
+
* node_vmstat_pswpin
|
|
11
|
+
* node_vmstat_pswpout
|
|
12
|
+
|
|
13
|
+
## Metric and its data sources
|
|
14
|
+
|
|
15
|
+
Vmstat collector access the following data sources.
|
|
16
|
+
|
|
17
|
+
* /proc/vmstat
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |spec|
|
|
5
|
+
spec.name = "fluent-plugin-node-exporter-metrics"
|
|
6
|
+
spec.version = "0.1.0"
|
|
7
|
+
spec.authors = ["Kentaro Hayashi"]
|
|
8
|
+
spec.email = ["hayashi@clear-code.com"]
|
|
9
|
+
|
|
10
|
+
spec.summary = %q{Input plugin which collects metrics similar to Prometheus Node Exporter}
|
|
11
|
+
spec.description = %q{node exporter metrics input plugin implements 11 node exporter collectors}
|
|
12
|
+
spec.homepage = "https://github.com/fluent-plugins-nursery/fluent-plugin-node-exporter-metrics"
|
|
13
|
+
spec.license = "Apache-2.0"
|
|
14
|
+
|
|
15
|
+
test_files, files = `git ls-files -z`.split("\x0").partition do |f|
|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
|
17
|
+
end
|
|
18
|
+
spec.files = files
|
|
19
|
+
spec.executables = files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
20
|
+
spec.test_files = test_files
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
spec.add_runtime_dependency "cmetrics", "~> 0.2.3"
|
|
24
|
+
spec.add_runtime_dependency "capng_c", "~> 0.2.2"
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency "bundler", "~> 2.2.0"
|
|
27
|
+
spec.add_development_dependency "rake", "~> 13.0.6"
|
|
28
|
+
spec.add_development_dependency "test-unit", "~> 3.4.4"
|
|
29
|
+
spec.add_development_dependency "test-unit-rr", "~> 1.0.5"
|
|
30
|
+
spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
|
|
31
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2021- Kentaro Hayashi
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require "cmetrics"
|
|
17
|
+
require "etc"
|
|
18
|
+
require "fluent/env"
|
|
19
|
+
require "fluent/capability"
|
|
20
|
+
require "fluent/plugin/input"
|
|
21
|
+
require "fluent/plugin/node_exporter/cpu_collector"
|
|
22
|
+
require "fluent/plugin/node_exporter/cpufreq_collector"
|
|
23
|
+
require "fluent/plugin/node_exporter/diskstats_collector"
|
|
24
|
+
require "fluent/plugin/node_exporter/filefd_collector"
|
|
25
|
+
require "fluent/plugin/node_exporter/loadavg_collector"
|
|
26
|
+
require "fluent/plugin/node_exporter/meminfo_collector"
|
|
27
|
+
require "fluent/plugin/node_exporter/netdev_collector"
|
|
28
|
+
require "fluent/plugin/node_exporter/stat_collector"
|
|
29
|
+
require "fluent/plugin/node_exporter/time_collector"
|
|
30
|
+
require "fluent/plugin/node_exporter/uname_collector"
|
|
31
|
+
require "fluent/plugin/node_exporter/vmstat_collector"
|
|
32
|
+
|
|
33
|
+
module Fluent
|
|
34
|
+
module Plugin
|
|
35
|
+
class NodeExporterMetricsInput < Fluent::Plugin::Input
|
|
36
|
+
Fluent::Plugin.register_input("node_exporter_metrics", self)
|
|
37
|
+
|
|
38
|
+
helpers :timer
|
|
39
|
+
|
|
40
|
+
desc "Interval to scrape metrics from node"
|
|
41
|
+
config_param :scrape_interval, :time, default: 5
|
|
42
|
+
desc "Path to mount point to collect process information and metrics"
|
|
43
|
+
config_param :procfs_path, :string, default: "/proc"
|
|
44
|
+
desc "Path to file-system to collect system metrics"
|
|
45
|
+
config_param :sysfs_path, :string, default: "/sys"
|
|
46
|
+
desc "Tag string"
|
|
47
|
+
config_param :tag, :string, default: nil
|
|
48
|
+
desc "Enable cpu collector"
|
|
49
|
+
config_param :cpu, :bool, default: true
|
|
50
|
+
desc "Enable cpufreq collector"
|
|
51
|
+
config_param :cpufreq, :bool, default: true
|
|
52
|
+
desc "Enable diskstats collector"
|
|
53
|
+
config_param :diskstats, :bool, default: true
|
|
54
|
+
desc "Enable filefd collector"
|
|
55
|
+
config_param :filefd, :bool, default: true
|
|
56
|
+
desc "Enable loadavg collector"
|
|
57
|
+
config_param :loadavg, :bool, default: true
|
|
58
|
+
desc "Enable meminfo collector"
|
|
59
|
+
config_param :meminfo, :bool, default: true
|
|
60
|
+
desc "Enable netdev collector"
|
|
61
|
+
config_param :netdev, :bool, default: true
|
|
62
|
+
desc "Enable stat collector"
|
|
63
|
+
config_param :stat, :bool, default: true
|
|
64
|
+
desc "Enable time collector"
|
|
65
|
+
config_param :time, :bool, default: true
|
|
66
|
+
desc "Enable uname collector"
|
|
67
|
+
config_param :uname, :bool, default: true
|
|
68
|
+
desc "Enable vmstat collector"
|
|
69
|
+
config_param :vmstat, :bool, default: true
|
|
70
|
+
|
|
71
|
+
def configure(conf)
|
|
72
|
+
super
|
|
73
|
+
@collectors = []
|
|
74
|
+
config = {
|
|
75
|
+
procfs_path: @procfs_path,
|
|
76
|
+
sysfs_path: @sysfs_path
|
|
77
|
+
}
|
|
78
|
+
@collectors << NodeExporter::CpuMetricsCollector.new(config) if @cpu
|
|
79
|
+
@collectors << NodeExporter::DiskstatsMetricsCollector.new(config) if @diskstats
|
|
80
|
+
@collectors << NodeExporter::FilefdMetricsCollector.new(config) if @filefd
|
|
81
|
+
@collectors << NodeExporter::LoadavgMetricsCollector.new(config) if @loadavg
|
|
82
|
+
@collectors << NodeExporter::MeminfoMetricsCollector.new(config) if @meminfo
|
|
83
|
+
@collectors << NodeExporter::NetdevMetricsCollector.new(config) if @netdev
|
|
84
|
+
@collectors << NodeExporter::StatMetricsCollector.new(config) if @stat
|
|
85
|
+
@collectors << NodeExporter::TimeMetricsCollector.new(config) if @time
|
|
86
|
+
@collectors << NodeExporter::UnameMetricsCollector.new(config) if @uname
|
|
87
|
+
@collectors << NodeExporter::VmstatMetricsCollector.new(config) if @vmstat
|
|
88
|
+
|
|
89
|
+
if Fluent.linux?
|
|
90
|
+
if @cpufreq
|
|
91
|
+
@capability = Fluent::Capability.new(:current_process)
|
|
92
|
+
unless Etc.getpwuid.uid != 0 and @capability.have_capability?(:effective, :dac_read_search)
|
|
93
|
+
raise ConfigError, "Linux capability CAP_DAC_READ_SEARCH must be enabled"
|
|
94
|
+
end
|
|
95
|
+
@collectors << NodeExporter::CpufreqMetricsCollector.new(config) if @cpufreq
|
|
96
|
+
end
|
|
97
|
+
elsif Fluent.windows?
|
|
98
|
+
raise ConfigError, "node_exporter_metrics is not supported"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if @collectors.empty?
|
|
102
|
+
raise ConfigError, "all collectors are disabled. Enable at least one collector."
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def start
|
|
107
|
+
super
|
|
108
|
+
timer_execute(:execute_node_exporter_metrics, @scrape_interval, &method(:refresh_watchers))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def refresh_watchers
|
|
112
|
+
begin
|
|
113
|
+
@serde = CMetrics::Serde.new
|
|
114
|
+
@collectors.each do |collector|
|
|
115
|
+
begin
|
|
116
|
+
collector.run
|
|
117
|
+
collector.cmetrics.each do |key, cmetric|
|
|
118
|
+
@serde.concat(cmetric) if cmetric
|
|
119
|
+
end
|
|
120
|
+
rescue => e
|
|
121
|
+
$log.error(e.message)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
record = {
|
|
125
|
+
"cmetrics" => @serde.to_msgpack
|
|
126
|
+
}
|
|
127
|
+
es = OneEventStream.new(Fluent::EventTime.now, record)
|
|
128
|
+
router.emit_stream(@tag, es)
|
|
129
|
+
end
|
|
130
|
+
rescue => e
|
|
131
|
+
$log.error(e.message)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def shutdown
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2021- Kentaro Hayashi
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require "cmetrics"
|
|
17
|
+
require "fluent/plugin/input"
|
|
18
|
+
|
|
19
|
+
module Fluent
|
|
20
|
+
module Plugin
|
|
21
|
+
module NodeExporter
|
|
22
|
+
class CMetricsDataSchemaParser
|
|
23
|
+
def parse(metrics)
|
|
24
|
+
data = []
|
|
25
|
+
begin
|
|
26
|
+
metrics.each do |metric|
|
|
27
|
+
next if metric["values"].empty?
|
|
28
|
+
data << to_readable_hash(metric)
|
|
29
|
+
end
|
|
30
|
+
rescue => e
|
|
31
|
+
raise Fluent::ParserError.new(e.message)
|
|
32
|
+
end
|
|
33
|
+
data.flatten
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Parsed CMetrics Data Schema Format
|
|
37
|
+
# {
|
|
38
|
+
# "name" => metrics name
|
|
39
|
+
# "time" => Fluent::EventTime
|
|
40
|
+
# "labels" => {"key" => value, ...}
|
|
41
|
+
# "value" => ...
|
|
42
|
+
# }
|
|
43
|
+
#
|
|
44
|
+
# "labels" field is optional. It is available when {"meta"=>"label_dictionary"...} and
|
|
45
|
+
# "labels" in "values" => [{"ts"=>..., "labels"=>[...]}]..
|
|
46
|
+
#
|
|
47
|
+
def to_readable_hash(metrics)
|
|
48
|
+
opts = metrics["meta"]["opts"]
|
|
49
|
+
|
|
50
|
+
metric_name = if opts["ss"].size.zero?
|
|
51
|
+
"#{opts['ns']}_#{opts['name']}"
|
|
52
|
+
else
|
|
53
|
+
"#{opts['ns']}_#{opts['ss']}_#{opts['name']}"
|
|
54
|
+
end
|
|
55
|
+
cmetrics = []
|
|
56
|
+
labels = []
|
|
57
|
+
unless metrics["meta"]["labels"].empty?
|
|
58
|
+
metrics["meta"]["labels"].each do |v|
|
|
59
|
+
labels << metrics["meta"]["label_dictionary"][v]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
metrics["values"].each do |entry|
|
|
63
|
+
cmetric = {
|
|
64
|
+
"name" => metric_name,
|
|
65
|
+
"value" => entry["value"],
|
|
66
|
+
"desc" => opts["desc"],
|
|
67
|
+
"ts" => entry["ts"]
|
|
68
|
+
}
|
|
69
|
+
unless metrics["meta"]["labels"].empty?
|
|
70
|
+
params = {}
|
|
71
|
+
entry["labels"].each_with_index do |v, index|
|
|
72
|
+
label = labels[index]
|
|
73
|
+
params[label] = metrics["meta"]["label_dictionary"][v]
|
|
74
|
+
end
|
|
75
|
+
cmetric["labels"] = params
|
|
76
|
+
end
|
|
77
|
+
cmetrics << cmetric
|
|
78
|
+
end
|
|
79
|
+
cmetrics
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2021- Kentaro Hayashi
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require "cmetrics"
|
|
17
|
+
require "fluent/plugin/input"
|
|
18
|
+
|
|
19
|
+
module Fluent
|
|
20
|
+
module Plugin
|
|
21
|
+
module NodeExporter
|
|
22
|
+
class MetricsCollector
|
|
23
|
+
def initialize(config={})
|
|
24
|
+
@scrape_interval = config[:scrape_interval] || 5
|
|
25
|
+
@procfs_path = config[:procfs_path] || "/proc"
|
|
26
|
+
@sysfs_path = config[:sysfs_path] || "/sys"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def scan_sysfs_path(pattern)
|
|
30
|
+
Dir.glob(File.join(@sysfs_path, pattern)).sort do |a, b|
|
|
31
|
+
File.basename(a).delete("a-z").to_i <=> File.basename(b).delete("a-z").to_i
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def cmetrics
|
|
36
|
+
raise NotImplementedError
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2021- Kentaro Hayashi
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require "cmetrics"
|
|
17
|
+
require "etc"
|
|
18
|
+
require "fluent/plugin/input"
|
|
19
|
+
require "fluent/plugin/node_exporter/collector"
|
|
20
|
+
|
|
21
|
+
module Fluent
|
|
22
|
+
module Plugin
|
|
23
|
+
module NodeExporter
|
|
24
|
+
class CpuMetricsCollector < MetricsCollector
|
|
25
|
+
def initialize(config={})
|
|
26
|
+
super(config)
|
|
27
|
+
|
|
28
|
+
# It varies whether /sys/devices/system/cpu/cpuN/thermal_throttle exists or not
|
|
29
|
+
@thermal_throttle_path = File.join(@sysfs_path, "devices/system/cpu/cpu0/thermal_throttle")
|
|
30
|
+
|
|
31
|
+
@core_throttles_total = if File.exist?(File.join(@thermal_throttle_path, "core_throttle_count"))
|
|
32
|
+
CMetrics::Counter.new
|
|
33
|
+
else
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
@core_throttles_total.create("node", "cpu", "core_throttles_total",
|
|
37
|
+
"Number of times this CPU core has been throttled.",
|
|
38
|
+
["core", "package"]) if @core_throttles_total
|
|
39
|
+
|
|
40
|
+
@package_throttles_total = if File.exist?(File.join(@thermal_throttle_path, "package_throttle_count"))
|
|
41
|
+
CMetrics::Counter.new
|
|
42
|
+
else
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
@package_throttles_total.create("node", "cpu", "package_throttles_total",
|
|
46
|
+
"Number of times this CPU package has been throttled.",
|
|
47
|
+
["package"]) if @package_throttles_total
|
|
48
|
+
|
|
49
|
+
@seconds_total = CMetrics::Counter.new
|
|
50
|
+
@seconds_total.create("node", "cpu", "seconds_total",
|
|
51
|
+
"Seconds the CPUs spent in each mode.",
|
|
52
|
+
["cpu", "mode"])
|
|
53
|
+
|
|
54
|
+
@guest_seconds_total = CMetrics::Counter.new
|
|
55
|
+
@guest_seconds_total.create("node", "cpu", "guest_seconds_total",
|
|
56
|
+
"Seconds the CPUs spent in guests (VMs) for each mode.",
|
|
57
|
+
["cpu", "mode"])
|
|
58
|
+
|
|
59
|
+
@core_throttles_set = {}
|
|
60
|
+
@package_throttles_set = {}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def run
|
|
64
|
+
cpu_thermal_update
|
|
65
|
+
cpu_stat_update
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def cpu_thermal_update
|
|
69
|
+
scan_sysfs_path("devices/system/cpu/cpu[0-9]*").each do |path|
|
|
70
|
+
next unless @core_throttles_total
|
|
71
|
+
next unless @package_throttles_total
|
|
72
|
+
|
|
73
|
+
core_id_path = File.join(path, "topology", "core_id")
|
|
74
|
+
physical_package_path = File.join(path, "topology", "physical_package_id")
|
|
75
|
+
core_id = File.read(core_id_path).strip
|
|
76
|
+
physical_package_id = File.read(physical_package_path).strip
|
|
77
|
+
next if @core_throttles_set[{physical_package_id: physical_package_id, core_id: core_id}]
|
|
78
|
+
@core_throttles_set[{physical_package_id: physical_package_id, core_id: core_id}] = true
|
|
79
|
+
|
|
80
|
+
core_throttle_count = File.read(File.join(path, "thermal_throttle", "core_throttle_count")).to_i
|
|
81
|
+
@core_throttles_total.set(core_throttle_count, [core_id, physical_package_id])
|
|
82
|
+
|
|
83
|
+
next if @package_throttles_set[physical_package_id]
|
|
84
|
+
@package_throttles_set[physical_package_id] = true
|
|
85
|
+
|
|
86
|
+
package_throttle_count = File.read(File.join(path, "thermal_throttle", "package_throttle_count")).to_i
|
|
87
|
+
@package_throttles_total.set(package_throttle_count, [physical_package_id])
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
STAT_CPU_PATTERN = /^cpu(?<cpuid>\d+)\s(?<user>\d+)\s(?<nice>\d+)\s(?<system>\d+)\s(?<idle>\d+)\s(?<iowait>\d+)\s(?<irq>\d+)\s(?<softirq>\d+)\s(?<steal>\d+)\s(?<guest>\d+)\s(?<guest_nice>\d+)/
|
|
93
|
+
|
|
94
|
+
def cpu_stat_update
|
|
95
|
+
stat_path = File.join(@procfs_path, "stat")
|
|
96
|
+
File.readlines(stat_path).each do |line|
|
|
97
|
+
if line.start_with?("cpu ")
|
|
98
|
+
# Ignore CPU total
|
|
99
|
+
next
|
|
100
|
+
elsif line.start_with?("cpu")
|
|
101
|
+
user_hz = Etc.sysconf(Etc::SC_CLK_TCK)
|
|
102
|
+
line.match(STAT_CPU_PATTERN) do |m|
|
|
103
|
+
@seconds_total.set(m[:idle].to_f / user_hz, [m[:cpuid], "idle"])
|
|
104
|
+
@seconds_total.set(m[:iowait].to_f / user_hz, [m[:cpuid], "iowait"])
|
|
105
|
+
@seconds_total.set(m[:irq].to_f / user_hz, [m[:cpuid], "irq"])
|
|
106
|
+
@seconds_total.set(m[:nice].to_f / user_hz, [m[:cpuid], "nice"])
|
|
107
|
+
@seconds_total.set(m[:softirq].to_f / user_hz, [m[:cpuid], "softirq"])
|
|
108
|
+
@seconds_total.set(m[:steal].to_f / user_hz, [m[:cpuid], "steal"])
|
|
109
|
+
@seconds_total.set(m[:system].to_f / user_hz, [m[:cpuid], "system"])
|
|
110
|
+
@seconds_total.set(m[:user].to_f / user_hz, [m[:cpuid], "user"])
|
|
111
|
+
|
|
112
|
+
@guest_seconds_total.set(m[:guest].to_f / user_hz, [m[:cpuid], "user"])
|
|
113
|
+
@guest_seconds_total.set(m[:guest_nice].to_f / user_hz, [m[:cpuid], "nice"])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def cmetrics
|
|
120
|
+
{
|
|
121
|
+
core_throttles_total: @core_throttles_total,
|
|
122
|
+
package_throttles_total: @package_throttles_total,
|
|
123
|
+
seconds_total: @seconds_total,
|
|
124
|
+
guest_seconds_total: @guest_seconds_total
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2021- Kentaro Hayashi
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require "cmetrics"
|
|
17
|
+
require "etc"
|
|
18
|
+
require "fluent/plugin/input"
|
|
19
|
+
require "fluent/plugin/node_exporter/collector"
|
|
20
|
+
|
|
21
|
+
module Fluent
|
|
22
|
+
module Plugin
|
|
23
|
+
module NodeExporter
|
|
24
|
+
class CpufreqMetricsCollector < MetricsCollector
|
|
25
|
+
def initialize(config={})
|
|
26
|
+
super(config)
|
|
27
|
+
|
|
28
|
+
if Fluent.linux?
|
|
29
|
+
@frequency_hertz = CMetrics::Gauge.new
|
|
30
|
+
@frequency_hertz.create("node", "cpu", "frequency_hertz",
|
|
31
|
+
"Current cpu thread frequency in hertz.", ["cpu"])
|
|
32
|
+
|
|
33
|
+
@frequency_max_hertz = CMetrics::Gauge.new
|
|
34
|
+
@frequency_max_hertz.create("node", "cpu", "frequency_max_hertz",
|
|
35
|
+
"Maximum cpu thread frequency in hertz.", ["cpu"])
|
|
36
|
+
|
|
37
|
+
@frequency_min_hertz = CMetrics::Gauge.new
|
|
38
|
+
@frequency_min_hertz.create("node", "cpu", "frequency_min_hertz",
|
|
39
|
+
"Minimum cpu thread frequency in hertz.", ["cpu"])
|
|
40
|
+
|
|
41
|
+
@scaling_frequency_hertz = CMetrics::Gauge.new
|
|
42
|
+
@scaling_frequency_hertz.create("node", "cpu", "scaling_frequency_hertz",
|
|
43
|
+
"Current scaled CPU thread frequency in hertz.", ["cpu"])
|
|
44
|
+
|
|
45
|
+
@scaling_frequency_max_hertz = CMetrics::Gauge.new
|
|
46
|
+
@scaling_frequency_max_hertz.create("node", "cpu", "scaling_frequency_max_hertz",
|
|
47
|
+
"Maximum scaled CPU thread frequency in hertz.", ["cpu"])
|
|
48
|
+
|
|
49
|
+
@scaling_frequency_min_hertz = CMetrics::Gauge.new
|
|
50
|
+
@scaling_frequency_min_hertz.create("node", "cpu", "scaling_frequency_min_hertz",
|
|
51
|
+
"Minimum scaled CPU thread frequency in hertz.", ["cpu"])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def run
|
|
56
|
+
cpufreq_update
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def cpuinfo_cur_freq_exist?
|
|
60
|
+
path = File.join(@sysfs_path, "devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq")
|
|
61
|
+
File.exist?(path)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def cpufreq_update
|
|
65
|
+
scan_sysfs_path("devices/system/cpu/cpu[0-9]*").each do |path|
|
|
66
|
+
next unless Dir.exist?(File.join(path, "cpufreq"))
|
|
67
|
+
|
|
68
|
+
cpuinfo_cur_freq_path = File.join(path, "cpufreq", "cpuinfo_cur_freq")
|
|
69
|
+
cpuinfo_max_freq_path = File.join(path, "cpufreq", "cpuinfo_max_freq")
|
|
70
|
+
cpuinfo_min_freq_path = File.join(path, "cpufreq", "cpuinfo_min_freq")
|
|
71
|
+
scaling_cur_freq_path = File.join(path, "cpufreq", "scaling_cur_freq")
|
|
72
|
+
scaling_max_freq_path = File.join(path, "cpufreq", "scaling_max_freq")
|
|
73
|
+
scaling_min_freq_path = File.join(path, "cpufreq", "scaling_min_freq")
|
|
74
|
+
cpu_id = File.basename(path).sub(/cpu(\d+)/, "\\1")
|
|
75
|
+
@frequency_hertz.set(File.read(cpuinfo_cur_freq_path).to_f, [cpu_id]) if File.exist?(cpuinfo_cur_freq_path)
|
|
76
|
+
@frequency_max_hertz.set(File.read(cpuinfo_max_freq_path).to_f, [cpu_id]) if File.exist?(cpuinfo_max_freq_path)
|
|
77
|
+
@frequency_min_hertz.set(File.read(cpuinfo_min_freq_path).to_f, [cpu_id]) if File.exist?(cpuinfo_min_freq_path)
|
|
78
|
+
@scaling_frequency_hertz.set(File.read(scaling_cur_freq_path).to_f, [cpu_id]) if File.exist?(scaling_cur_freq_path)
|
|
79
|
+
@scaling_frequency_max_hertz.set(File.read(scaling_max_freq_path).to_f, [cpu_id]) if File.exist?(scaling_max_freq_path)
|
|
80
|
+
@scaling_frequency_min_hertz.set(File.read(scaling_min_freq_path).to_f, [cpu_id]) if File.exist?(scaling_min_freq_path)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def cmetrics
|
|
85
|
+
{
|
|
86
|
+
frequency_hertz: @frequency_hertz,
|
|
87
|
+
frequency_max_hertz: @frequency_max_hertz,
|
|
88
|
+
frequency_min_hertz: @frequency_min_hertz,
|
|
89
|
+
scaling_frequency_hertz: @scaling_frequency_hertz,
|
|
90
|
+
scaling_frequency_max_hertz: @scaling_frequency_max_hertz,
|
|
91
|
+
scaling_frequency_min_hertz: @scaling_frequency_min_hertz
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|