vinted-prometheus-client-mmap 1.5.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +5 -0
  3. data/ext/fast_mmaped_file_rs/Cargo.toml +40 -0
  4. data/ext/fast_mmaped_file_rs/README.md +52 -0
  5. data/ext/fast_mmaped_file_rs/build.rs +7 -0
  6. data/ext/fast_mmaped_file_rs/extconf.rb +28 -0
  7. data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
  8. data/ext/fast_mmaped_file_rs/src/exemplars.rs +25 -0
  9. data/ext/fast_mmaped_file_rs/src/file_entry.rs +1252 -0
  10. data/ext/fast_mmaped_file_rs/src/file_info.rs +240 -0
  11. data/ext/fast_mmaped_file_rs/src/lib.rs +89 -0
  12. data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
  13. data/ext/fast_mmaped_file_rs/src/map.rs +519 -0
  14. data/ext/fast_mmaped_file_rs/src/metrics.proto +153 -0
  15. data/ext/fast_mmaped_file_rs/src/mmap/inner.rs +775 -0
  16. data/ext/fast_mmaped_file_rs/src/mmap.rs +977 -0
  17. data/ext/fast_mmaped_file_rs/src/raw_entry.rs +547 -0
  18. data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
  19. data/ext/fast_mmaped_file_rs/src/util.rs +140 -0
  20. data/lib/.DS_Store +0 -0
  21. data/lib/2.7/fast_mmaped_file_rs.so +0 -0
  22. data/lib/3.0/fast_mmaped_file_rs.so +0 -0
  23. data/lib/3.1/fast_mmaped_file_rs.so +0 -0
  24. data/lib/3.2/fast_mmaped_file_rs.so +0 -0
  25. data/lib/3.3/fast_mmaped_file_rs.so +0 -0
  26. data/lib/prometheus/.DS_Store +0 -0
  27. data/lib/prometheus/client/configuration.rb +24 -0
  28. data/lib/prometheus/client/counter.rb +27 -0
  29. data/lib/prometheus/client/formats/protobuf.rb +93 -0
  30. data/lib/prometheus/client/formats/text.rb +85 -0
  31. data/lib/prometheus/client/gauge.rb +40 -0
  32. data/lib/prometheus/client/helper/entry_parser.rb +132 -0
  33. data/lib/prometheus/client/helper/file_locker.rb +50 -0
  34. data/lib/prometheus/client/helper/json_parser.rb +23 -0
  35. data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
  36. data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
  37. data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
  38. data/lib/prometheus/client/helper/plain_file.rb +29 -0
  39. data/lib/prometheus/client/histogram.rb +80 -0
  40. data/lib/prometheus/client/label_set_validator.rb +85 -0
  41. data/lib/prometheus/client/metric.rb +80 -0
  42. data/lib/prometheus/client/mmaped_dict.rb +83 -0
  43. data/lib/prometheus/client/mmaped_value.rb +164 -0
  44. data/lib/prometheus/client/page_size.rb +17 -0
  45. data/lib/prometheus/client/push.rb +203 -0
  46. data/lib/prometheus/client/rack/collector.rb +88 -0
  47. data/lib/prometheus/client/rack/exporter.rb +102 -0
  48. data/lib/prometheus/client/registry.rb +65 -0
  49. data/lib/prometheus/client/simple_value.rb +31 -0
  50. data/lib/prometheus/client/summary.rb +69 -0
  51. data/lib/prometheus/client/support/puma.rb +44 -0
  52. data/lib/prometheus/client/support/unicorn.rb +35 -0
  53. data/lib/prometheus/client/uses_value_type.rb +20 -0
  54. data/lib/prometheus/client/version.rb +5 -0
  55. data/lib/prometheus/client.rb +58 -0
  56. data/lib/prometheus.rb +3 -0
  57. metadata +210 -0
@@ -0,0 +1,222 @@
1
+ use bstr::{BString, B};
2
+ use std::fs::File;
3
+ use std::io::{Read, Seek, Write};
4
+ use std::path::PathBuf;
5
+ use tempfile::{tempdir, TempDir};
6
+
7
+ use crate::raw_entry::RawEntry;
8
+ use crate::HEADER_SIZE;
9
+
10
+ #[derive(PartialEq, Default, Debug)]
11
+ pub struct TestEntry {
12
+ pub header: u32,
13
+ pub json: &'static str,
14
+ pub padding_len: usize,
15
+ pub value: f64,
16
+ }
17
+
18
+ impl TestEntry {
19
+ pub fn new(json: &'static str, value: f64) -> Self {
20
+ TestEntry {
21
+ header: json.len() as u32,
22
+ json,
23
+ padding_len: RawEntry::padding_len(json.len()),
24
+ value,
25
+ }
26
+ }
27
+
28
+ pub fn as_bytes(&self) -> Vec<u8> {
29
+ [
30
+ B(&self.header.to_ne_bytes()),
31
+ self.json.as_bytes(),
32
+ &vec![b' '; self.padding_len],
33
+ B(&self.value.to_ne_bytes()),
34
+ ]
35
+ .concat()
36
+ }
37
+ pub fn as_bstring(&self) -> BString {
38
+ [
39
+ B(&self.header.to_ne_bytes()),
40
+ self.json.as_bytes(),
41
+ &vec![b' '; self.padding_len],
42
+ B(&self.value.to_ne_bytes()),
43
+ ]
44
+ .concat()
45
+ .into()
46
+ }
47
+
48
+ pub fn as_bytes_no_header(&self) -> BString {
49
+ [
50
+ self.json.as_bytes(),
51
+ &vec![b' '; self.padding_len],
52
+ B(&self.value.to_ne_bytes()),
53
+ ]
54
+ .concat()
55
+ .into()
56
+ }
57
+ }
58
+
59
+ /// Format the data for a `.db` file.
60
+ /// Optional header value can be used to set an invalid `used` size.
61
+ pub fn entries_to_db(entries: &[&'static str], values: &[f64], header: Option<u32>) -> Vec<u8> {
62
+ let mut out = Vec::new();
63
+
64
+ let entry_bytes: Vec<_> = entries
65
+ .iter()
66
+ .zip(values)
67
+ .flat_map(|(e, val)| TestEntry::new(e, *val).as_bytes())
68
+ .collect();
69
+
70
+ let used = match header {
71
+ Some(u) => u,
72
+ None => (entry_bytes.len() + HEADER_SIZE) as u32,
73
+ };
74
+
75
+ out.extend(used.to_ne_bytes());
76
+ out.extend([0x0u8; 4]); // Padding.
77
+ out.extend(entry_bytes);
78
+
79
+ out
80
+ }
81
+
82
+ /// A temporary file, path, and dir for use with testing.
83
+ #[derive(Debug)]
84
+ pub struct TestFile {
85
+ pub file: File,
86
+ pub path: PathBuf,
87
+ pub dir: TempDir,
88
+ }
89
+
90
+ impl TestFile {
91
+ pub fn new(file_data: &[u8]) -> TestFile {
92
+ let dir = tempdir().unwrap();
93
+ let path = dir.path().join("test.db");
94
+ let mut file = File::options()
95
+ .create(true)
96
+ .read(true)
97
+ .write(true)
98
+ .open(&path)
99
+ .unwrap();
100
+
101
+ file.write_all(file_data).unwrap();
102
+ file.sync_all().unwrap();
103
+ file.rewind().unwrap();
104
+
105
+ // We need to keep `dir` in scope so it doesn't drop before the files it
106
+ // contains, which may prevent cleanup.
107
+ TestFile { file, path, dir }
108
+ }
109
+ }
110
+
111
+ mod test {
112
+ use super::*;
113
+
114
+ #[test]
115
+ fn test_entry_new() {
116
+ let json = "foobar";
117
+ let value = 1.0f64;
118
+ let expected = TestEntry {
119
+ header: 6,
120
+ json,
121
+ padding_len: 6,
122
+ value,
123
+ };
124
+
125
+ let actual = TestEntry::new(json, value);
126
+ assert_eq!(expected, actual);
127
+ }
128
+
129
+ #[test]
130
+ fn test_entry_bytes() {
131
+ let json = "foobar";
132
+ let value = 1.0f64;
133
+ let expected = [
134
+ &6u32.to_ne_bytes(),
135
+ B(json),
136
+ &[b' '; 6],
137
+ &value.to_ne_bytes(),
138
+ ]
139
+ .concat();
140
+
141
+ let actual = TestEntry::new(json, value).as_bstring();
142
+ assert_eq!(expected, actual);
143
+ }
144
+
145
+ #[test]
146
+ fn test_entry_bytes_no_header() {
147
+ let json = "foobar";
148
+ let value = 1.0f64;
149
+ let expected = [B(json), &[b' '; 6], &value.to_ne_bytes()].concat();
150
+
151
+ let actual = TestEntry::new(json, value).as_bytes_no_header();
152
+ assert_eq!(expected, actual);
153
+ }
154
+
155
+ #[test]
156
+ fn test_entries_to_db_header_correct() {
157
+ let json = &["foobar", "qux"];
158
+ let values = &[1.0, 2.0];
159
+
160
+ let out = entries_to_db(json, values, None);
161
+
162
+ assert_eq!(48u32.to_ne_bytes(), out[0..4], "used set correctly");
163
+ assert_eq!([0u8; 4], out[4..8], "padding set");
164
+ assert_eq!(
165
+ TestEntry::new(json[0], values[0]).as_bytes(),
166
+ out[8..32],
167
+ "first entry matches"
168
+ );
169
+ assert_eq!(
170
+ TestEntry::new(json[1], values[1]).as_bytes(),
171
+ out[32..48],
172
+ "second entry matches"
173
+ );
174
+ }
175
+
176
+ #[test]
177
+ fn test_entries_to_db_header_wrong() {
178
+ let json = &["foobar", "qux"];
179
+ let values = &[1.0, 2.0];
180
+
181
+ const WRONG_USED: u32 = 1000;
182
+ let out = entries_to_db(json, values, Some(WRONG_USED));
183
+
184
+ assert_eq!(
185
+ WRONG_USED.to_ne_bytes(),
186
+ out[0..4],
187
+ "used set to value requested"
188
+ );
189
+ assert_eq!([0u8; 4], out[4..8], "padding set");
190
+ assert_eq!(
191
+ TestEntry::new(json[0], values[0]).as_bytes(),
192
+ out[8..32],
193
+ "first entry matches"
194
+ );
195
+ assert_eq!(
196
+ TestEntry::new(json[1], values[1]).as_bytes(),
197
+ out[32..48],
198
+ "second entry matches"
199
+ );
200
+ }
201
+
202
+ #[test]
203
+ fn test_file() {
204
+ let mut test_file = TestFile::new(b"foobar");
205
+ let stat = test_file.file.metadata().unwrap();
206
+
207
+ assert_eq!(6, stat.len(), "file length");
208
+ assert_eq!(
209
+ 0,
210
+ test_file.file.stream_position().unwrap(),
211
+ "at start of file"
212
+ );
213
+ let mut out_buf = vec![0u8; 256];
214
+ let read_result = test_file.file.read(&mut out_buf);
215
+ assert!(read_result.is_ok());
216
+ assert_eq!(6, read_result.unwrap(), "file is readable");
217
+
218
+ let write_result = test_file.file.write(b"qux");
219
+ assert!(write_result.is_ok());
220
+ assert_eq!(3, write_result.unwrap(), "file is writable");
221
+ }
222
+ }
@@ -0,0 +1,140 @@
1
+ use nix::errno::Errno;
2
+ use nix::libc::c_long;
3
+ use std::fmt::Display;
4
+ use std::io;
5
+ use std::mem::size_of;
6
+
7
+ use crate::error::MmapError;
8
+ use crate::exemplars::{Exemplar, EXEMPLAR_ENTRY_MAX_SIZE_BYTES};
9
+ use crate::Result;
10
+
11
+ /// Wrapper around `checked_add()` that converts failures
12
+ /// to `MmapError::Overflow`.
13
+ pub trait CheckedOps: Sized {
14
+ fn add_chk(self, rhs: Self) -> Result<Self>;
15
+ fn mul_chk(self, rhs: Self) -> Result<Self>;
16
+ }
17
+
18
+ impl CheckedOps for usize {
19
+ fn add_chk(self, rhs: Self) -> Result<Self> {
20
+ self.checked_add(rhs)
21
+ .ok_or_else(|| MmapError::overflowed(self, rhs, "adding"))
22
+ }
23
+
24
+ fn mul_chk(self, rhs: Self) -> Result<Self> {
25
+ self.checked_mul(rhs)
26
+ .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying"))
27
+ }
28
+ }
29
+
30
+ impl CheckedOps for c_long {
31
+ fn add_chk(self, rhs: Self) -> Result<Self> {
32
+ self.checked_add(rhs)
33
+ .ok_or_else(|| MmapError::overflowed(self, rhs, "adding"))
34
+ }
35
+
36
+ fn mul_chk(self, rhs: Self) -> Result<Self> {
37
+ self.checked_mul(rhs)
38
+ .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying"))
39
+ }
40
+ }
41
+
42
+ /// A wrapper around `TryFrom`, returning `MmapError::FailedCast` on error.
43
+ pub fn cast_chk<T, U>(val: T, name: &str) -> Result<U>
44
+ where
45
+ T: Copy + Display,
46
+ U: std::convert::TryFrom<T>,
47
+ {
48
+ U::try_from(val).map_err(|_| MmapError::failed_cast::<T, U>(val, name))
49
+ }
50
+
51
+ /// Retrieve errno(3).
52
+ pub fn errno() -> i32 {
53
+ // UNWRAP: This will always return `Some` when called from `last_os_error()`.
54
+ io::Error::last_os_error().raw_os_error().unwrap()
55
+ }
56
+
57
+ /// Get the error string associated with errno(3).
58
+ /// Equivalent to strerror(3).
59
+ pub fn strerror(errno: i32) -> &'static str {
60
+ Errno::from_i32(errno).desc()
61
+ }
62
+
63
+ /// Read a `u32` value from a byte slice starting from `offset`.
64
+ #[inline]
65
+ pub fn read_u32(buf: &[u8], offset: usize) -> Result<u32> {
66
+ if let Some(slice) = buf.get(offset..offset + size_of::<u32>()) {
67
+ // UNWRAP: We can safely unwrap the conversion from slice to array as we
68
+ // the source and targets are constructed here with the same length.
69
+ let out: &[u8; size_of::<u32>()] = slice.try_into().unwrap();
70
+
71
+ return Ok(u32::from_ne_bytes(*out));
72
+ }
73
+ Err(MmapError::out_of_bounds(offset, buf.len()))
74
+ }
75
+
76
+ /// Read an `f64` value from a byte slice starting from `offset`.
77
+ #[inline]
78
+ pub fn read_f64(buf: &[u8], offset: usize) -> Result<f64> {
79
+ if let Some(slice) = buf.get(offset..offset + size_of::<f64>()) {
80
+ // UNWRAP: We can safely unwrap the conversion from slice to array as we
81
+ // can be sure the target array has same length as the source slice.
82
+ let out: &[u8; size_of::<f64>()] = slice.try_into().unwrap();
83
+
84
+ return Ok(f64::from_ne_bytes(*out));
85
+ }
86
+ Err(MmapError::out_of_bounds(
87
+ offset + size_of::<f64>(),
88
+ buf.len(),
89
+ ))
90
+ }
91
+
92
+ pub fn read_exemplar(buf: &[u8], offset: usize) -> Result<Exemplar> {
93
+ if let Some(slice) = buf.get(offset..offset + EXEMPLAR_ENTRY_MAX_SIZE_BYTES) {
94
+ // UNWRAP: We can safely unwrap the conversion from slice to array as we
95
+ // can be sure the target array has same length as the source slice.
96
+ let out: &[u8; EXEMPLAR_ENTRY_MAX_SIZE_BYTES] = slice.try_into().expect("failed to convert slice to array");
97
+
98
+ let res: Vec<u8> = out.iter().cloned().filter(|&x| x != 0).collect();
99
+
100
+ let v: Exemplar = serde_json::from_slice(&res).expect("failed to convert string to Exemplar");
101
+
102
+ return Ok(v)
103
+ }
104
+ Err(MmapError::out_of_bounds(
105
+ offset + EXEMPLAR_ENTRY_MAX_SIZE_BYTES,
106
+ buf.len(),
107
+ ))
108
+ }
109
+
110
+ #[cfg(test)]
111
+ mod test {
112
+ use super::*;
113
+
114
+ #[test]
115
+ fn test_read_u32() {
116
+ let buf = 1u32.to_ne_bytes();
117
+
118
+ assert!(matches!(read_u32(&buf, 0), Ok(1)), "index ok");
119
+ assert!(read_u32(&buf, 10).is_err(), "index out of range");
120
+ assert!(
121
+ read_u32(&buf, 1).is_err(),
122
+ "index in range but end out of range"
123
+ );
124
+ }
125
+
126
+ #[test]
127
+ fn test_read_f64() {
128
+ let buf = 1.00f64.to_ne_bytes();
129
+
130
+ let ok = read_f64(&buf, 0);
131
+ assert!(ok.is_ok());
132
+ assert_eq!(ok.unwrap(), 1.00);
133
+
134
+ assert!(read_f64(&buf, 10).is_err(), "index out of range");
135
+ assert!(
136
+ read_f64(&buf, 1).is_err(),
137
+ "index in range but end out of range"
138
+ );
139
+ }
140
+ }
data/lib/.DS_Store ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ require 'prometheus/client/registry'
2
+ require 'prometheus/client/mmaped_value'
3
+ require 'prometheus/client/page_size'
4
+ require 'logger'
5
+ require 'tmpdir'
6
+
7
+ module Prometheus
8
+ module Client
9
+ class Configuration
10
+ attr_accessor :value_class, :multiprocess_files_dir, :initial_mmap_file_size, :logger, :pid_provider, :enable_protobuf
11
+
12
+ def initialize
13
+ @value_class = ::Prometheus::Client::MmapedValue
14
+ @initial_mmap_file_size = ::Prometheus::Client::PageSize.page_size(fallback_page_size: 4096)
15
+ @logger = Logger.new($stdout)
16
+ @pid_provider = Process.method(:pid)
17
+ @multiprocess_files_dir = ENV.fetch('prometheus_multiproc_dir') do
18
+ Dir.mktmpdir("prometheus-mmap")
19
+ end
20
+ @enable_protobuf = false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'prometheus/client/metric'
4
+
5
+ module Prometheus
6
+ module Client
7
+ # Counter is a metric that exposes merely a sum or tally of things.
8
+ class Counter < Metric
9
+ def type
10
+ :counter
11
+ end
12
+
13
+ def increment(labels = {}, by = 1, exemplar_name = '', exemplar_value = '')
14
+ raise ArgumentError, 'increment must be a non-negative number' if by < 0
15
+
16
+ label_set = label_set_for(labels)
17
+ synchronize { @values[label_set].increment(by, exemplar_name, exemplar_value) }
18
+ end
19
+
20
+ private
21
+
22
+ def default(labels)
23
+ value_object(type, @name, @name, labels)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,93 @@
1
+ require 'prometheus/client/uses_value_type'
2
+ require 'prometheus/client/helper/json_parser'
3
+ require 'prometheus/client/helper/plain_file'
4
+ require 'prometheus/client/helper/metrics_processing'
5
+ require 'prometheus/client/helper/metrics_representation'
6
+
7
+ module Prometheus
8
+ module Client
9
+ module Formats
10
+ # Protobuf format supports the same metric types as the text format plus
11
+ # native histograms. It is only available with the Rust extension.
12
+ module Protobuf
13
+ MEDIA_TYPE = 'application/vnd.google.protobuf'.freeze
14
+ PROTO = 'io.prometheus.client.MetricFamily'.freeze
15
+ ENCODING = 'delimited'.freeze
16
+ CONTENT_TYPE = "#{MEDIA_TYPE}; proto=#{PROTO}; encoding=#{ENCODING}".freeze
17
+
18
+ class << self
19
+ def marshal(registry)
20
+ metrics = registry.metrics.map do |metric|
21
+ samples = metric.values.flat_map do |label_set, value|
22
+ representation(metric, label_set, value)
23
+ end
24
+
25
+ [metric.name, { type: metric.type, help: metric.docstring, samples: samples }]
26
+ end
27
+
28
+ Helper::MetricsRepresentation.to_metrics(metrics)
29
+ end
30
+
31
+ def marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir, use_rust: true)
32
+ # NOTE(GiedriusS): need to ensure exemplar files go at the end because they add extra data.
33
+ file_list = Dir.glob(File.join(path, '*.db')).sort_by { |f| [f.include?('exemplar') ? 1 : 0, f] }
34
+ .map {|f| Helper::PlainFile.new(f) }
35
+ .map {|f| [f.filepath, f.multiprocess_mode.to_sym, f.type.to_sym, f.pid] }
36
+
37
+ FastMmapedFileRs.to_protobuf(file_list.to_a)
38
+ end
39
+
40
+ def rust_impl_available?
41
+ return @rust_available unless @rust_available.nil?
42
+
43
+ check_for_rust
44
+ end
45
+
46
+ private
47
+
48
+ def load_metrics(path)
49
+ metrics = {}
50
+ Dir.glob(File.join(path, '*.db')).sort.each do |f|
51
+ Helper::PlainFile.new(f).to_metrics(metrics)
52
+ end
53
+
54
+ metrics
55
+ end
56
+
57
+ def representation(metric, label_set, value)
58
+ labels = metric.base_labels.merge(label_set)
59
+
60
+ if metric.type == :summary
61
+ summary(metric.name, labels, value)
62
+ elsif metric.type == :histogram
63
+ histogram(metric.name, labels, value)
64
+ else
65
+ [[metric.name, labels, value.get]]
66
+ end
67
+ end
68
+
69
+ def summary(name, set, value)
70
+ rv = value.get.map do |q, v|
71
+ [name, set.merge(quantile: q), v]
72
+ end
73
+
74
+ rv << ["#{name}_sum", set, value.get.sum]
75
+ rv << ["#{name}_count", set, value.get.total]
76
+ rv
77
+ end
78
+
79
+ def histogram(name, set, value)
80
+ # |metric_name, labels, value|
81
+ rv = value.get.map do |q, v|
82
+ [name, set.merge(le: q), v]
83
+ end
84
+
85
+ rv << ["#{name}_sum", set, value.get.sum]
86
+ rv << ["#{name}_count", set, value.get.total]
87
+ rv
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,85 @@
1
+ require 'prometheus/client/uses_value_type'
2
+ require 'prometheus/client/helper/json_parser'
3
+ require 'prometheus/client/helper/plain_file'
4
+ require 'prometheus/client/helper/metrics_processing'
5
+ require 'prometheus/client/helper/metrics_representation'
6
+
7
+ module Prometheus
8
+ module Client
9
+ module Formats
10
+ # Text format is human readable mainly used for manual inspection.
11
+ module Text
12
+ MEDIA_TYPE = 'text/plain'.freeze
13
+ VERSION = '0.0.4'.freeze
14
+ CONTENT_TYPE = "#{MEDIA_TYPE}; version=#{VERSION}".freeze
15
+
16
+ class << self
17
+ def marshal(registry)
18
+ metrics = registry.metrics.map do |metric|
19
+ samples = metric.values.flat_map do |label_set, value|
20
+ representation(metric, label_set, value)
21
+ end
22
+
23
+ [metric.name, { type: metric.type, help: metric.docstring, samples: samples }]
24
+ end
25
+
26
+ Helper::MetricsRepresentation.to_text(metrics)
27
+ end
28
+
29
+ def marshal_multiprocess(path = Prometheus::Client.configuration.multiprocess_files_dir)
30
+ file_list = Dir.glob(File.join(path, '*.db')).sort
31
+ .map {|f| Helper::PlainFile.new(f) }
32
+ .map {|f| [f.filepath, f.multiprocess_mode.to_sym, f.type.to_sym, f.pid] }
33
+
34
+ FastMmapedFileRs.to_metrics(file_list.to_a)
35
+ end
36
+
37
+ private
38
+
39
+ def load_metrics(path)
40
+ metrics = {}
41
+ Dir.glob(File.join(path, '*.db')).sort.each do |f|
42
+ Helper::PlainFile.new(f).to_metrics(metrics)
43
+ end
44
+
45
+ metrics
46
+ end
47
+
48
+ def representation(metric, label_set, value)
49
+ labels = metric.base_labels.merge(label_set)
50
+
51
+ if metric.type == :summary
52
+ summary(metric.name, labels, value)
53
+ elsif metric.type == :histogram
54
+ histogram(metric.name, labels, value)
55
+ else
56
+ [[metric.name, labels, value.get]]
57
+ end
58
+ end
59
+
60
+ def summary(name, set, value)
61
+ rv = value.get.map do |q, v|
62
+ [name, set.merge(quantile: q), v]
63
+ end
64
+
65
+ rv << ["#{name}_sum", set, value.get.sum]
66
+ rv << ["#{name}_count", set, value.get.total]
67
+ rv
68
+ end
69
+
70
+ def histogram(name, set, value)
71
+ # |metric_name, labels, value|
72
+ rv = value.get.map do |q, v|
73
+ [name, set.merge(le: q), v]
74
+ end
75
+
76
+ rv << [name, set.merge(le: '+Inf'), value.get.total]
77
+ rv << ["#{name}_sum", set, value.get.sum]
78
+ rv << ["#{name}_count", set, value.get.total]
79
+ rv
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'prometheus/client/metric'
4
+
5
+ module Prometheus
6
+ module Client
7
+ # A Gauge is a metric that exposes merely an instantaneous value or some
8
+ # snapshot thereof.
9
+ class Gauge < Metric
10
+ def initialize(name, docstring, base_labels = {}, multiprocess_mode=:all)
11
+ super(name, docstring, base_labels)
12
+ if value_class.multiprocess and ![:min, :max, :livesum, :liveall, :all].include?(multiprocess_mode)
13
+ raise ArgumentError, 'Invalid multiprocess mode: ' + multiprocess_mode
14
+ end
15
+ @multiprocess_mode = multiprocess_mode
16
+ end
17
+
18
+ def type
19
+ :gauge
20
+ end
21
+
22
+ def default(labels)
23
+ value_object(type, @name, @name, labels, @multiprocess_mode)
24
+ end
25
+
26
+ # Sets the value for the given label set
27
+ def set(labels, value)
28
+ @values[label_set_for(labels)].set(value)
29
+ end
30
+
31
+ def increment(labels, value)
32
+ @values[label_set_for(labels)].increment(value)
33
+ end
34
+
35
+ def decrement(labels, value)
36
+ @values[label_set_for(labels)].decrement(value)
37
+ end
38
+ end
39
+ end
40
+ end