prometheus-client-mmap 0.20.3-x86_64-linux
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/README.md +253 -0
 - data/ext/fast_mmaped_file/extconf.rb +30 -0
 - data/ext/fast_mmaped_file/fast_mmaped_file.c +122 -0
 - data/ext/fast_mmaped_file/file_format.c +5 -0
 - data/ext/fast_mmaped_file/file_format.h +11 -0
 - data/ext/fast_mmaped_file/file_parsing.c +195 -0
 - data/ext/fast_mmaped_file/file_parsing.h +27 -0
 - data/ext/fast_mmaped_file/file_reading.c +102 -0
 - data/ext/fast_mmaped_file/file_reading.h +30 -0
 - data/ext/fast_mmaped_file/globals.h +14 -0
 - data/ext/fast_mmaped_file/mmap.c +427 -0
 - data/ext/fast_mmaped_file/mmap.h +61 -0
 - data/ext/fast_mmaped_file/rendering.c +199 -0
 - data/ext/fast_mmaped_file/rendering.h +8 -0
 - data/ext/fast_mmaped_file/utils.c +56 -0
 - data/ext/fast_mmaped_file/utils.h +22 -0
 - data/ext/fast_mmaped_file/value_access.c +242 -0
 - data/ext/fast_mmaped_file/value_access.h +15 -0
 - data/ext/fast_mmaped_file_rs/.cargo/config.toml +23 -0
 - data/ext/fast_mmaped_file_rs/Cargo.lock +790 -0
 - data/ext/fast_mmaped_file_rs/Cargo.toml +30 -0
 - data/ext/fast_mmaped_file_rs/README.md +52 -0
 - data/ext/fast_mmaped_file_rs/extconf.rb +30 -0
 - data/ext/fast_mmaped_file_rs/src/error.rs +174 -0
 - data/ext/fast_mmaped_file_rs/src/file_entry.rs +579 -0
 - data/ext/fast_mmaped_file_rs/src/file_info.rs +190 -0
 - data/ext/fast_mmaped_file_rs/src/lib.rs +79 -0
 - data/ext/fast_mmaped_file_rs/src/macros.rs +14 -0
 - data/ext/fast_mmaped_file_rs/src/map.rs +492 -0
 - data/ext/fast_mmaped_file_rs/src/mmap.rs +151 -0
 - data/ext/fast_mmaped_file_rs/src/parser.rs +346 -0
 - data/ext/fast_mmaped_file_rs/src/raw_entry.rs +473 -0
 - data/ext/fast_mmaped_file_rs/src/testhelper.rs +222 -0
 - data/ext/fast_mmaped_file_rs/src/util.rs +121 -0
 - data/lib/2.7/fast_mmaped_file.so +0 -0
 - data/lib/2.7/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.0/fast_mmaped_file.so +0 -0
 - data/lib/3.0/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.1/fast_mmaped_file.so +0 -0
 - data/lib/3.1/fast_mmaped_file_rs.so +0 -0
 - data/lib/3.2/fast_mmaped_file.so +0 -0
 - data/lib/3.2/fast_mmaped_file_rs.so +0 -0
 - data/lib/prometheus/client/configuration.rb +23 -0
 - data/lib/prometheus/client/counter.rb +27 -0
 - data/lib/prometheus/client/formats/text.rb +118 -0
 - data/lib/prometheus/client/gauge.rb +40 -0
 - data/lib/prometheus/client/helper/entry_parser.rb +132 -0
 - data/lib/prometheus/client/helper/file_locker.rb +50 -0
 - data/lib/prometheus/client/helper/json_parser.rb +23 -0
 - data/lib/prometheus/client/helper/metrics_processing.rb +45 -0
 - data/lib/prometheus/client/helper/metrics_representation.rb +51 -0
 - data/lib/prometheus/client/helper/mmaped_file.rb +64 -0
 - data/lib/prometheus/client/helper/plain_file.rb +29 -0
 - data/lib/prometheus/client/histogram.rb +80 -0
 - data/lib/prometheus/client/label_set_validator.rb +86 -0
 - data/lib/prometheus/client/metric.rb +80 -0
 - data/lib/prometheus/client/mmaped_dict.rb +79 -0
 - data/lib/prometheus/client/mmaped_value.rb +154 -0
 - data/lib/prometheus/client/page_size.rb +17 -0
 - data/lib/prometheus/client/push.rb +203 -0
 - data/lib/prometheus/client/rack/collector.rb +88 -0
 - data/lib/prometheus/client/rack/exporter.rb +96 -0
 - data/lib/prometheus/client/registry.rb +65 -0
 - data/lib/prometheus/client/simple_value.rb +31 -0
 - data/lib/prometheus/client/summary.rb +69 -0
 - data/lib/prometheus/client/support/unicorn.rb +35 -0
 - data/lib/prometheus/client/uses_value_type.rb +20 -0
 - data/lib/prometheus/client/version.rb +5 -0
 - data/lib/prometheus/client.rb +58 -0
 - data/lib/prometheus.rb +3 -0
 - data/vendor/c/hashmap/.gitignore +52 -0
 - data/vendor/c/hashmap/LICENSE +21 -0
 - data/vendor/c/hashmap/README.md +90 -0
 - data/vendor/c/hashmap/_config.yml +1 -0
 - data/vendor/c/hashmap/src/hashmap.c +692 -0
 - data/vendor/c/hashmap/src/hashmap.h +267 -0
 - data/vendor/c/hashmap/test/Makefile +22 -0
 - data/vendor/c/hashmap/test/hashmap_test.c +608 -0
 - data/vendor/c/jsmn/.travis.yml +4 -0
 - data/vendor/c/jsmn/LICENSE +20 -0
 - data/vendor/c/jsmn/Makefile +41 -0
 - data/vendor/c/jsmn/README.md +168 -0
 - data/vendor/c/jsmn/example/jsondump.c +126 -0
 - data/vendor/c/jsmn/example/simple.c +76 -0
 - data/vendor/c/jsmn/jsmn.c +314 -0
 - data/vendor/c/jsmn/jsmn.h +76 -0
 - data/vendor/c/jsmn/library.json +16 -0
 - data/vendor/c/jsmn/test/test.h +27 -0
 - data/vendor/c/jsmn/test/tests.c +407 -0
 - data/vendor/c/jsmn/test/testutil.h +94 -0
 - metadata +243 -0
 
| 
         @@ -0,0 +1,121 @@ 
     | 
|
| 
      
 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::Result;
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            /// Wrapper around `checked_add()` that converts failures
         
     | 
| 
      
 11 
     | 
    
         
            +
            /// to `MmapError::Overflow`.
         
     | 
| 
      
 12 
     | 
    
         
            +
            pub trait CheckedOps: Sized {
         
     | 
| 
      
 13 
     | 
    
         
            +
                fn add_chk(self, rhs: Self) -> Result<Self>;
         
     | 
| 
      
 14 
     | 
    
         
            +
                fn mul_chk(self, rhs: Self) -> Result<Self>;
         
     | 
| 
      
 15 
     | 
    
         
            +
            }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            impl CheckedOps for usize {
         
     | 
| 
      
 18 
     | 
    
         
            +
                fn add_chk(self, rhs: Self) -> Result<Self> {
         
     | 
| 
      
 19 
     | 
    
         
            +
                    self.checked_add(rhs)
         
     | 
| 
      
 20 
     | 
    
         
            +
                        .ok_or_else(|| MmapError::overflowed(self, rhs, "adding"))
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                fn mul_chk(self, rhs: Self) -> Result<Self> {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    self.checked_mul(rhs)
         
     | 
| 
      
 25 
     | 
    
         
            +
                        .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying"))
         
     | 
| 
      
 26 
     | 
    
         
            +
                }
         
     | 
| 
      
 27 
     | 
    
         
            +
            }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            impl CheckedOps for c_long {
         
     | 
| 
      
 30 
     | 
    
         
            +
                fn add_chk(self, rhs: Self) -> Result<Self> {
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.checked_add(rhs)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        .ok_or_else(|| MmapError::overflowed(self, rhs, "adding"))
         
     | 
| 
      
 33 
     | 
    
         
            +
                }
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                fn mul_chk(self, rhs: Self) -> Result<Self> {
         
     | 
| 
      
 36 
     | 
    
         
            +
                    self.checked_mul(rhs)
         
     | 
| 
      
 37 
     | 
    
         
            +
                        .ok_or_else(|| MmapError::overflowed(self, rhs, "multiplying"))
         
     | 
| 
      
 38 
     | 
    
         
            +
                }
         
     | 
| 
      
 39 
     | 
    
         
            +
            }
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            /// A wrapper around `TryFrom`, returning `MmapError::FailedCast` on error.
         
     | 
| 
      
 42 
     | 
    
         
            +
            pub fn cast_chk<T, U>(val: T, name: &str) -> Result<U>
         
     | 
| 
      
 43 
     | 
    
         
            +
            where
         
     | 
| 
      
 44 
     | 
    
         
            +
                T: Copy + Display,
         
     | 
| 
      
 45 
     | 
    
         
            +
                U: std::convert::TryFrom<T>,
         
     | 
| 
      
 46 
     | 
    
         
            +
            {
         
     | 
| 
      
 47 
     | 
    
         
            +
                U::try_from(val).map_err(|_| MmapError::failed_cast::<T, U>(val, name))
         
     | 
| 
      
 48 
     | 
    
         
            +
            }
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            /// Retrieve errno(3).
         
     | 
| 
      
 51 
     | 
    
         
            +
            pub fn errno() -> i32 {
         
     | 
| 
      
 52 
     | 
    
         
            +
                // UNWRAP: This will always return `Some` when called from `last_os_error()`.
         
     | 
| 
      
 53 
     | 
    
         
            +
                io::Error::last_os_error().raw_os_error().unwrap()
         
     | 
| 
      
 54 
     | 
    
         
            +
            }
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            /// Get the error string associated with errno(3).
         
     | 
| 
      
 57 
     | 
    
         
            +
            /// Equivalent to strerror(3).
         
     | 
| 
      
 58 
     | 
    
         
            +
            pub fn strerror(errno: i32) -> &'static str {
         
     | 
| 
      
 59 
     | 
    
         
            +
                Errno::from_i32(errno).desc()
         
     | 
| 
      
 60 
     | 
    
         
            +
            }
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            /// Read a `u32` value from a byte slice starting from `offset`.
         
     | 
| 
      
 63 
     | 
    
         
            +
            #[inline]
         
     | 
| 
      
 64 
     | 
    
         
            +
            pub fn read_u32(buf: &[u8], offset: usize) -> Result<u32> {
         
     | 
| 
      
 65 
     | 
    
         
            +
                if let Some(slice) = buf.get(offset..offset + size_of::<u32>()) {
         
     | 
| 
      
 66 
     | 
    
         
            +
                    // UNWRAP: We can safely unwrap the conversion from slice to array as we
         
     | 
| 
      
 67 
     | 
    
         
            +
                    // the source and targets are constructed here with the same length.
         
     | 
| 
      
 68 
     | 
    
         
            +
                    let out: &[u8; size_of::<u32>()] = slice.try_into().unwrap();
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    return Ok(u32::from_ne_bytes(*out));
         
     | 
| 
      
 71 
     | 
    
         
            +
                }
         
     | 
| 
      
 72 
     | 
    
         
            +
                Err(MmapError::out_of_bounds(offset, buf.len()))
         
     | 
| 
      
 73 
     | 
    
         
            +
            }
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            /// Read an `f64` value from a byte slice starting from `offset`.
         
     | 
| 
      
 76 
     | 
    
         
            +
            #[inline]
         
     | 
| 
      
 77 
     | 
    
         
            +
            pub fn read_f64(buf: &[u8], offset: usize) -> Result<f64> {
         
     | 
| 
      
 78 
     | 
    
         
            +
                if let Some(slice) = buf.get(offset..offset + size_of::<f64>()) {
         
     | 
| 
      
 79 
     | 
    
         
            +
                    // UNWRAP: We can safely unwrap the conversion from slice to array as we
         
     | 
| 
      
 80 
     | 
    
         
            +
                    // can be sure the target array has same length as the source slice.
         
     | 
| 
      
 81 
     | 
    
         
            +
                    let out: &[u8; size_of::<f64>()] = slice.try_into().unwrap();
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    return Ok(f64::from_ne_bytes(*out));
         
     | 
| 
      
 84 
     | 
    
         
            +
                }
         
     | 
| 
      
 85 
     | 
    
         
            +
                Err(MmapError::out_of_bounds(
         
     | 
| 
      
 86 
     | 
    
         
            +
                    offset + size_of::<f64>(),
         
     | 
| 
      
 87 
     | 
    
         
            +
                    buf.len(),
         
     | 
| 
      
 88 
     | 
    
         
            +
                ))
         
     | 
| 
      
 89 
     | 
    
         
            +
            }
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
            #[cfg(test)]
         
     | 
| 
      
 92 
     | 
    
         
            +
            mod test {
         
     | 
| 
      
 93 
     | 
    
         
            +
                use super::*;
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 96 
     | 
    
         
            +
                fn test_read_u32() {
         
     | 
| 
      
 97 
     | 
    
         
            +
                    let buf = 1u32.to_ne_bytes();
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    assert!(matches!(read_u32(&buf, 0), Ok(1)), "index ok");
         
     | 
| 
      
 100 
     | 
    
         
            +
                    assert!(read_u32(&buf, 10).is_err(), "index out of range");
         
     | 
| 
      
 101 
     | 
    
         
            +
                    assert!(
         
     | 
| 
      
 102 
     | 
    
         
            +
                        read_u32(&buf, 1).is_err(),
         
     | 
| 
      
 103 
     | 
    
         
            +
                        "index in range but end out of range"
         
     | 
| 
      
 104 
     | 
    
         
            +
                    );
         
     | 
| 
      
 105 
     | 
    
         
            +
                }
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                #[test]
         
     | 
| 
      
 108 
     | 
    
         
            +
                fn test_read_f64() {
         
     | 
| 
      
 109 
     | 
    
         
            +
                    let buf = 1.00f64.to_ne_bytes();
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    let ok = read_f64(&buf, 0);
         
     | 
| 
      
 112 
     | 
    
         
            +
                    assert!(ok.is_ok());
         
     | 
| 
      
 113 
     | 
    
         
            +
                    assert_eq!(ok.unwrap(), 1.00);
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    assert!(read_f64(&buf, 10).is_err(), "index out of range");
         
     | 
| 
      
 116 
     | 
    
         
            +
                    assert!(
         
     | 
| 
      
 117 
     | 
    
         
            +
                        read_f64(&buf, 1).is_err(),
         
     | 
| 
      
 118 
     | 
    
         
            +
                        "index in range but end out of range"
         
     | 
| 
      
 119 
     | 
    
         
            +
                    );
         
     | 
| 
      
 120 
     | 
    
         
            +
                }
         
     | 
| 
      
 121 
     | 
    
         
            +
            }
         
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         Binary file 
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 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
         
     | 
| 
      
 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 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            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)
         
     | 
| 
      
 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) }
         
     | 
| 
      
 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,118 @@ 
     | 
|
| 
      
 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, use_rust: false)
         
     | 
| 
      
 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 
     | 
    
         
            +
                        if use_rust && rust_impl_available?
         
     | 
| 
      
 35 
     | 
    
         
            +
                          FastMmapedFileRs.to_metrics(file_list.to_a)
         
     | 
| 
      
 36 
     | 
    
         
            +
                        else
         
     | 
| 
      
 37 
     | 
    
         
            +
                          FastMmapedFile.to_metrics(file_list.to_a)
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      def rust_impl_available?
         
     | 
| 
      
 42 
     | 
    
         
            +
                        return @rust_available unless @rust_available.nil?
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                        check_for_rust
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      private
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                      def load_rust_extension
         
     | 
| 
      
 50 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 51 
     | 
    
         
            +
                          ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
         
     | 
| 
      
 52 
     | 
    
         
            +
                          require_relative "../../../#{ruby_version}/fast_mmaped_file_rs"
         
     | 
| 
      
 53 
     | 
    
         
            +
                        rescue LoadError
         
     | 
| 
      
 54 
     | 
    
         
            +
                          require 'fast_mmaped_file_rs'
         
     | 
| 
      
 55 
     | 
    
         
            +
                        end
         
     | 
| 
      
 56 
     | 
    
         
            +
                      end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                      def check_for_rust
         
     | 
| 
      
 59 
     | 
    
         
            +
                        # This will be evaluated on each invocation even with `||=` if
         
     | 
| 
      
 60 
     | 
    
         
            +
                        # `@rust_available` if false. Running a `require` statement is slow,
         
     | 
| 
      
 61 
     | 
    
         
            +
                        # so the `rust_impl_available?` method memoizes the result, external
         
     | 
| 
      
 62 
     | 
    
         
            +
                        # callers can only trigger this method a single time.
         
     | 
| 
      
 63 
     | 
    
         
            +
                        @rust_available = begin
         
     | 
| 
      
 64 
     | 
    
         
            +
                          load_rust_extension
         
     | 
| 
      
 65 
     | 
    
         
            +
                          true
         
     | 
| 
      
 66 
     | 
    
         
            +
                        rescue LoadError
         
     | 
| 
      
 67 
     | 
    
         
            +
                          Prometheus::Client.logger.info('FastMmapedFileRs unavailable')
         
     | 
| 
      
 68 
     | 
    
         
            +
                          false
         
     | 
| 
      
 69 
     | 
    
         
            +
                        end
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      def load_metrics(path)
         
     | 
| 
      
 73 
     | 
    
         
            +
                        metrics = {}
         
     | 
| 
      
 74 
     | 
    
         
            +
                        Dir.glob(File.join(path, '*.db')).sort.each do |f|
         
     | 
| 
      
 75 
     | 
    
         
            +
                          Helper::PlainFile.new(f).to_metrics(metrics)
         
     | 
| 
      
 76 
     | 
    
         
            +
                        end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                        metrics
         
     | 
| 
      
 79 
     | 
    
         
            +
                      end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                      def representation(metric, label_set, value)
         
     | 
| 
      
 82 
     | 
    
         
            +
                        labels = metric.base_labels.merge(label_set)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        if metric.type == :summary
         
     | 
| 
      
 85 
     | 
    
         
            +
                          summary(metric.name, labels, value)
         
     | 
| 
      
 86 
     | 
    
         
            +
                        elsif metric.type == :histogram
         
     | 
| 
      
 87 
     | 
    
         
            +
                          histogram(metric.name, labels, value)
         
     | 
| 
      
 88 
     | 
    
         
            +
                        else
         
     | 
| 
      
 89 
     | 
    
         
            +
                          [[metric.name, labels, value.get]]
         
     | 
| 
      
 90 
     | 
    
         
            +
                        end
         
     | 
| 
      
 91 
     | 
    
         
            +
                      end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                      def summary(name, set, value)
         
     | 
| 
      
 94 
     | 
    
         
            +
                        rv = value.get.map do |q, v|
         
     | 
| 
      
 95 
     | 
    
         
            +
                          [name, set.merge(quantile: q), v]
         
     | 
| 
      
 96 
     | 
    
         
            +
                        end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        rv << ["#{name}_sum", set, value.get.sum]
         
     | 
| 
      
 99 
     | 
    
         
            +
                        rv << ["#{name}_count", set, value.get.total]
         
     | 
| 
      
 100 
     | 
    
         
            +
                        rv
         
     | 
| 
      
 101 
     | 
    
         
            +
                      end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                      def histogram(name, set, value)
         
     | 
| 
      
 104 
     | 
    
         
            +
                        # |metric_name, labels, value|
         
     | 
| 
      
 105 
     | 
    
         
            +
                        rv = value.get.map do |q, v|
         
     | 
| 
      
 106 
     | 
    
         
            +
                          [name, set.merge(le: q), v]
         
     | 
| 
      
 107 
     | 
    
         
            +
                        end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                        rv << [name, set.merge(le: '+Inf'), value.get.total]
         
     | 
| 
      
 110 
     | 
    
         
            +
                        rv << ["#{name}_sum", set, value.get.sum]
         
     | 
| 
      
 111 
     | 
    
         
            +
                        rv << ["#{name}_count", set, value.get.total]
         
     | 
| 
      
 112 
     | 
    
         
            +
                        rv
         
     | 
| 
      
 113 
     | 
    
         
            +
                      end
         
     | 
| 
      
 114 
     | 
    
         
            +
                    end
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
              end
         
     | 
| 
      
 118 
     | 
    
         
            +
            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
         
     | 
| 
         @@ -0,0 +1,132 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prometheus/client/helper/json_parser'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 6 
     | 
    
         
            +
                  module EntryParser
         
     | 
| 
      
 7 
     | 
    
         
            +
                    class ParsingError < RuntimeError;
         
     | 
| 
      
 8 
     | 
    
         
            +
                    end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    MINIMUM_SIZE = 8
         
     | 
| 
      
 11 
     | 
    
         
            +
                    START_POSITION = 8
         
     | 
| 
      
 12 
     | 
    
         
            +
                    VALUE_BYTES = 8
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ENCODED_LENGTH_BYTES = 4
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    def used
         
     | 
| 
      
 16 
     | 
    
         
            +
                      slice(0..3).unpack('l')[0]
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    def parts
         
     | 
| 
      
 20 
     | 
    
         
            +
                      @parts ||= File.basename(filepath, '.db')
         
     | 
| 
      
 21 
     | 
    
         
            +
                                   .split('_')
         
     | 
| 
      
 22 
     | 
    
         
            +
                                   .map { |e| e.gsub(/-\d+$/, '') } # remove trailing -number
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    def type
         
     | 
| 
      
 26 
     | 
    
         
            +
                      parts[0].to_sym
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    def pid
         
     | 
| 
      
 30 
     | 
    
         
            +
                      (parts[2..-1] || []).join('_')
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    def multiprocess_mode
         
     | 
| 
      
 34 
     | 
    
         
            +
                      parts[1]
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    def empty?
         
     | 
| 
      
 38 
     | 
    
         
            +
                      size < MINIMUM_SIZE || used.zero?
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    def entries(ignore_errors = false)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      return Enumerator.new {} if empty?
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                      Enumerator.new do |yielder|
         
     | 
| 
      
 45 
     | 
    
         
            +
                        used_ = used # cache used to avoid unnecessary unpack operations
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                        pos = START_POSITION # used + padding offset
         
     | 
| 
      
 48 
     | 
    
         
            +
                        while pos < used_ && pos < size && pos > 0
         
     | 
| 
      
 49 
     | 
    
         
            +
                          data = slice(pos..-1)
         
     | 
| 
      
 50 
     | 
    
         
            +
                          unless data
         
     | 
| 
      
 51 
     | 
    
         
            +
                            raise ParsingError, "data slice is nil at pos #{pos}" unless ignore_errors
         
     | 
| 
      
 52 
     | 
    
         
            +
                            pos += 8
         
     | 
| 
      
 53 
     | 
    
         
            +
                            next
         
     | 
| 
      
 54 
     | 
    
         
            +
                          end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                          encoded_len, first_encoded_bytes = data.unpack('LL')
         
     | 
| 
      
 57 
     | 
    
         
            +
                          if encoded_len.nil? || encoded_len.zero? || first_encoded_bytes.nil? || first_encoded_bytes.zero?
         
     | 
| 
      
 58 
     | 
    
         
            +
                            # do not parse empty data
         
     | 
| 
      
 59 
     | 
    
         
            +
                            pos += 8
         
     | 
| 
      
 60 
     | 
    
         
            +
                            next
         
     | 
| 
      
 61 
     | 
    
         
            +
                          end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                          entry_len = ENCODED_LENGTH_BYTES + encoded_len
         
     | 
| 
      
 64 
     | 
    
         
            +
                          padding_len = 8 - entry_len % 8
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                          value_offset = entry_len + padding_len # align to 8 bytes
         
     | 
| 
      
 67 
     | 
    
         
            +
                          pos += value_offset
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                          if value_offset > 0 && (pos + VALUE_BYTES) <= size # if positions are safe
         
     | 
| 
      
 70 
     | 
    
         
            +
                            yielder.yield data, encoded_len, value_offset, pos
         
     | 
| 
      
 71 
     | 
    
         
            +
                          else
         
     | 
| 
      
 72 
     | 
    
         
            +
                            raise ParsingError, "data slice is nil at pos #{pos}" unless ignore_errors
         
     | 
| 
      
 73 
     | 
    
         
            +
                          end
         
     | 
| 
      
 74 
     | 
    
         
            +
                          pos += VALUE_BYTES
         
     | 
| 
      
 75 
     | 
    
         
            +
                        end
         
     | 
| 
      
 76 
     | 
    
         
            +
                      end
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    def parsed_entries(ignore_errors = false)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      result = entries(ignore_errors).map do |data, encoded_len, value_offset, _|
         
     | 
| 
      
 81 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 82 
     | 
    
         
            +
                          encoded, value = data.unpack(format('@4A%d@%dd', encoded_len, value_offset))
         
     | 
| 
      
 83 
     | 
    
         
            +
                          [encoded, value]
         
     | 
| 
      
 84 
     | 
    
         
            +
                        rescue ArgumentError => e
         
     | 
| 
      
 85 
     | 
    
         
            +
                          Prometheus::Client.logger.debug("Error processing data: #{bin_to_hex(data[0, 7])} len: #{encoded_len} value_offset: #{value_offset}")
         
     | 
| 
      
 86 
     | 
    
         
            +
                          raise ParsingError, e unless ignore_errors
         
     | 
| 
      
 87 
     | 
    
         
            +
                        end
         
     | 
| 
      
 88 
     | 
    
         
            +
                      end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                      result.reject!(&:nil?) if ignore_errors
         
     | 
| 
      
 91 
     | 
    
         
            +
                      result
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    def to_metrics(metrics = {}, ignore_errors = false)
         
     | 
| 
      
 95 
     | 
    
         
            +
                      parsed_entries(ignore_errors).each do |key, value|
         
     | 
| 
      
 96 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 97 
     | 
    
         
            +
                          metric_name, name, labelnames, labelvalues = JsonParser.load(key)
         
     | 
| 
      
 98 
     | 
    
         
            +
                          labelnames ||= []
         
     | 
| 
      
 99 
     | 
    
         
            +
                          labelvalues ||= []
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                          metric = metrics.fetch(metric_name,
         
     | 
| 
      
 102 
     | 
    
         
            +
                                                 metric_name: metric_name,
         
     | 
| 
      
 103 
     | 
    
         
            +
                                                 help: 'Multiprocess metric',
         
     | 
| 
      
 104 
     | 
    
         
            +
                                                 type: type,
         
     | 
| 
      
 105 
     | 
    
         
            +
                                                 samples: [])
         
     | 
| 
      
 106 
     | 
    
         
            +
                          if type == :gauge
         
     | 
| 
      
 107 
     | 
    
         
            +
                            metric[:multiprocess_mode] = multiprocess_mode
         
     | 
| 
      
 108 
     | 
    
         
            +
                            metric[:samples] += [[name, labelnames.zip(labelvalues) + [['pid', pid]], value]]
         
     | 
| 
      
 109 
     | 
    
         
            +
                          else
         
     | 
| 
      
 110 
     | 
    
         
            +
                            # The duplicates and labels are fixed in the next for.
         
     | 
| 
      
 111 
     | 
    
         
            +
                            metric[:samples] += [[name, labelnames.zip(labelvalues), value]]
         
     | 
| 
      
 112 
     | 
    
         
            +
                          end
         
     | 
| 
      
 113 
     | 
    
         
            +
                          metrics[metric_name] = metric
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                        rescue JSON::ParserError => e
         
     | 
| 
      
 116 
     | 
    
         
            +
                          raise ParsingError(e) unless ignore_errors
         
     | 
| 
      
 117 
     | 
    
         
            +
                        end
         
     | 
| 
      
 118 
     | 
    
         
            +
                      end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                      metrics.reject! { |e| e.nil? } if ignore_errors
         
     | 
| 
      
 121 
     | 
    
         
            +
                      metrics
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    private
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    def bin_to_hex(s)
         
     | 
| 
      
 127 
     | 
    
         
            +
                      s.each_byte.map { |b| b.to_s(16) }.join
         
     | 
| 
      
 128 
     | 
    
         
            +
                    end
         
     | 
| 
      
 129 
     | 
    
         
            +
                  end
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
              end
         
     | 
| 
      
 132 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class FileLocker
         
     | 
| 
      
 5 
     | 
    
         
            +
                    class << self
         
     | 
| 
      
 6 
     | 
    
         
            +
                      LOCK_FILE_MUTEX = Mutex.new
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                      def lock_to_process(filepath)
         
     | 
| 
      
 9 
     | 
    
         
            +
                        LOCK_FILE_MUTEX.synchronize do
         
     | 
| 
      
 10 
     | 
    
         
            +
                          @file_locks ||= {}
         
     | 
| 
      
 11 
     | 
    
         
            +
                          return false if @file_locks[filepath]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                          file = File.open(filepath, 'ab')
         
     | 
| 
      
 14 
     | 
    
         
            +
                          if file.flock(File::LOCK_NB | File::LOCK_EX)
         
     | 
| 
      
 15 
     | 
    
         
            +
                            @file_locks[filepath] = file
         
     | 
| 
      
 16 
     | 
    
         
            +
                            return true
         
     | 
| 
      
 17 
     | 
    
         
            +
                          else
         
     | 
| 
      
 18 
     | 
    
         
            +
                            return false
         
     | 
| 
      
 19 
     | 
    
         
            +
                          end
         
     | 
| 
      
 20 
     | 
    
         
            +
                        end
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      def unlock(filepath)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        LOCK_FILE_MUTEX.synchronize do
         
     | 
| 
      
 25 
     | 
    
         
            +
                          @file_locks ||= {}
         
     | 
| 
      
 26 
     | 
    
         
            +
                          return false unless @file_locks[filepath]
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                          file = @file_locks[filepath]
         
     | 
| 
      
 29 
     | 
    
         
            +
                          file.flock(File::LOCK_UN)
         
     | 
| 
      
 30 
     | 
    
         
            +
                          file.close
         
     | 
| 
      
 31 
     | 
    
         
            +
                          @file_locks.delete(filepath)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        end
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      def unlock_all
         
     | 
| 
      
 36 
     | 
    
         
            +
                        LOCK_FILE_MUTEX.synchronize do
         
     | 
| 
      
 37 
     | 
    
         
            +
                          @file_locks ||= {}
         
     | 
| 
      
 38 
     | 
    
         
            +
                          @file_locks.values.each do |file|
         
     | 
| 
      
 39 
     | 
    
         
            +
                            file.flock(File::LOCK_UN)
         
     | 
| 
      
 40 
     | 
    
         
            +
                            file.close
         
     | 
| 
      
 41 
     | 
    
         
            +
                          end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                          @file_locks = {}
         
     | 
| 
      
 44 
     | 
    
         
            +
                        end
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 6 
     | 
    
         
            +
                  module JsonParser
         
     | 
| 
      
 7 
     | 
    
         
            +
                    class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                      if defined?(Oj)
         
     | 
| 
      
 9 
     | 
    
         
            +
                        def load(s)
         
     | 
| 
      
 10 
     | 
    
         
            +
                          Oj.load(s)
         
     | 
| 
      
 11 
     | 
    
         
            +
                        rescue Oj::ParseError, EncodingError => e
         
     | 
| 
      
 12 
     | 
    
         
            +
                          raise JSON::ParserError.new(e.message)
         
     | 
| 
      
 13 
     | 
    
         
            +
                        end
         
     | 
| 
      
 14 
     | 
    
         
            +
                      else
         
     | 
| 
      
 15 
     | 
    
         
            +
                        def load(s)
         
     | 
| 
      
 16 
     | 
    
         
            +
                          JSON.parse(s)
         
     | 
| 
      
 17 
     | 
    
         
            +
                        end
         
     | 
| 
      
 18 
     | 
    
         
            +
                      end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 4 
     | 
    
         
            +
                  module MetricsProcessing
         
     | 
| 
      
 5 
     | 
    
         
            +
                    def self.merge_metrics(metrics)
         
     | 
| 
      
 6 
     | 
    
         
            +
                      metrics.each_value do |metric|
         
     | 
| 
      
 7 
     | 
    
         
            +
                        metric[:samples] = merge_samples(metric[:samples], metric[:type], metric[:multiprocess_mode]).map do |(name, labels), value|
         
     | 
| 
      
 8 
     | 
    
         
            +
                          [name, labels.to_h, value]
         
     | 
| 
      
 9 
     | 
    
         
            +
                        end
         
     | 
| 
      
 10 
     | 
    
         
            +
                      end
         
     | 
| 
      
 11 
     | 
    
         
            +
                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    def self.merge_samples(raw_samples, metric_type, multiprocess_mode)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      samples = {}
         
     | 
| 
      
 15 
     | 
    
         
            +
                      raw_samples.each do |name, labels, value|
         
     | 
| 
      
 16 
     | 
    
         
            +
                        without_pid = labels.reject { |l| l[0] == 'pid' }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                        case metric_type
         
     | 
| 
      
 19 
     | 
    
         
            +
                          when :gauge
         
     | 
| 
      
 20 
     | 
    
         
            +
                            case multiprocess_mode
         
     | 
| 
      
 21 
     | 
    
         
            +
                              when 'min'
         
     | 
| 
      
 22 
     | 
    
         
            +
                                s = samples.fetch([name, without_pid], value)
         
     | 
| 
      
 23 
     | 
    
         
            +
                                samples[[name, without_pid]] = [s, value].min
         
     | 
| 
      
 24 
     | 
    
         
            +
                              when 'max'
         
     | 
| 
      
 25 
     | 
    
         
            +
                                s = samples.fetch([name, without_pid], value)
         
     | 
| 
      
 26 
     | 
    
         
            +
                                samples[[name, without_pid]] = [s, value].max
         
     | 
| 
      
 27 
     | 
    
         
            +
                              when 'livesum'
         
     | 
| 
      
 28 
     | 
    
         
            +
                                s = samples.fetch([name, without_pid], 0.0)
         
     | 
| 
      
 29 
     | 
    
         
            +
                                samples[[name, without_pid]] = s + value
         
     | 
| 
      
 30 
     | 
    
         
            +
                              else # all/liveall
         
     | 
| 
      
 31 
     | 
    
         
            +
                                samples[[name, labels]] = value
         
     | 
| 
      
 32 
     | 
    
         
            +
                            end
         
     | 
| 
      
 33 
     | 
    
         
            +
                          else
         
     | 
| 
      
 34 
     | 
    
         
            +
                            # Counter, Histogram and Summary.
         
     | 
| 
      
 35 
     | 
    
         
            +
                            s = samples.fetch([name, without_pid], 0.0)
         
     | 
| 
      
 36 
     | 
    
         
            +
                            samples[[name, without_pid]] = s + value
         
     | 
| 
      
 37 
     | 
    
         
            +
                        end
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      samples
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Prometheus
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Client
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Helper
         
     | 
| 
      
 4 
     | 
    
         
            +
                  module MetricsRepresentation
         
     | 
| 
      
 5 
     | 
    
         
            +
                    METRIC_LINE = '%s%s %s'.freeze
         
     | 
| 
      
 6 
     | 
    
         
            +
                    TYPE_LINE = '# TYPE %s %s'.freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                    HELP_LINE = '# HELP %s %s'.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    LABEL = '%s="%s"'.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                    SEPARATOR = ','.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
                    DELIMITER = "\n".freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
                    REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    def self.to_text(metrics)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      lines = []
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      metrics.each do |name, metric|
         
     | 
| 
      
 20 
     | 
    
         
            +
                        lines << format(HELP_LINE, name, escape(metric[:help]))
         
     | 
| 
      
 21 
     | 
    
         
            +
                        lines << format(TYPE_LINE, name, metric[:type])
         
     | 
| 
      
 22 
     | 
    
         
            +
                        metric[:samples].each do |metric_name, labels, value|
         
     | 
| 
      
 23 
     | 
    
         
            +
                          lines << metric(metric_name, format_labels(labels), value)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        end
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                      # there must be a trailing delimiter
         
     | 
| 
      
 28 
     | 
    
         
            +
                      (lines << nil).join(DELIMITER)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    def self.metric(name, labels, value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      format(METRIC_LINE, name, labels, value)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    def self.format_labels(set)
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return if set.empty?
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      strings = set.each_with_object([]) do |(key, value), memo|
         
     | 
| 
      
 39 
     | 
    
         
            +
                        memo << format(LABEL, key, escape(value, :label))
         
     | 
| 
      
 40 
     | 
    
         
            +
                      end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                      "{#{strings.join(SEPARATOR)}}"
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    def self.escape(string, format = :doc)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      string.to_s.gsub(REGEX[format], REPLACE)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     |