prometheus_exporter 0.1.6 → 0.1.7
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 +4 -4
 - data/README.md +50 -6
 - data/bin/prometheus_exporter +58 -0
 - data/lib/prometheus_exporter.rb +2 -0
 - data/lib/prometheus_exporter/client.rb +9 -1
 - data/lib/prometheus_exporter/instrumentation.rb +2 -0
 - data/lib/prometheus_exporter/instrumentation/method_profiler.rb +55 -0
 - data/lib/prometheus_exporter/instrumentation/process.rb +77 -0
 - data/lib/prometheus_exporter/middleware.rb +47 -0
 - data/lib/prometheus_exporter/server.rb +1 -0
 - data/lib/prometheus_exporter/server/collector.rb +117 -7
 - data/lib/prometheus_exporter/server/collector_base.rb +14 -0
 - data/lib/prometheus_exporter/server/web_server.rb +21 -4
 - data/lib/prometheus_exporter/version.rb +1 -1
 - data/prometheus_exporter.gemspec +2 -1
 - metadata +24 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 6957162d157420b2b8fb6d2a4b9e08483ce846497fe6bb21f1695b0e70460dc8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 9a74c21c97efad9ef3720df426d14a5e1d879eb34bebcafc625de8d45f3b232b
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 695472a08f8f9ede18d6f0fdaa8722d2e4efb01b20bf23269d26afae580cb559da4f409bb3499da7a80dcd5d47e6156a6e8c7e54f01c770485212e21369fb61d
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: af55f7e9d025a925f0add89a61628d236691df0a613d82d8c4e622fc9b83b7651187d9e78121113de872632459e95015772ed9af43a12b85ce4a1f820be9b7fc
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -61,20 +61,20 @@ In some cases, for example unicorn or puma clusters you may want to aggregate me 
     | 
|
| 
       61 
61 
     | 
    
         | 
| 
       62 
62 
     | 
    
         
             
            Simplest way to acheive this is use the built-in collector.
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
            First, run an exporter on your desired port:
         
     | 
| 
      
 64 
     | 
    
         
            +
            First, run an exporter on your desired port, we use the default port of 9394:
         
     | 
| 
       65 
65 
     | 
    
         | 
| 
       66 
66 
     | 
    
         
             
            ```
         
     | 
| 
       67 
     | 
    
         
            -
            # prometheus_exporter 
     | 
| 
      
 67 
     | 
    
         
            +
            # prometheus_exporter
         
     | 
| 
       68 
68 
     | 
    
         
             
            ```
         
     | 
| 
       69 
69 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
            At this point an exporter is running on port  
     | 
| 
      
 70 
     | 
    
         
            +
            At this point an exporter is running on port 9394
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
       72 
72 
     | 
    
         
             
            In your application:
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
74 
     | 
    
         
             
            ```ruby
         
     | 
| 
       75 
75 
     | 
    
         
             
            require 'prometheus_exporter/client'
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
            client = PrometheusExporter::Client. 
     | 
| 
      
 77 
     | 
    
         
            +
            client = PrometheusExporter::Client.default
         
     | 
| 
       78 
78 
     | 
    
         
             
            gauge = client.register(:gauge, "awesome", "amount of awesome")
         
     | 
| 
       79 
79 
     | 
    
         | 
| 
       80 
80 
     | 
    
         
             
            gauge.observe(10)
         
     | 
| 
         @@ -85,7 +85,7 @@ gauge.observe(99, day: "friday") 
     | 
|
| 
       85 
85 
     | 
    
         
             
            Then you will get the metrics:
         
     | 
| 
       86 
86 
     | 
    
         | 
| 
       87 
87 
     | 
    
         
             
            ```bash
         
     | 
| 
       88 
     | 
    
         
            -
            % curl localhost: 
     | 
| 
      
 88 
     | 
    
         
            +
            % curl localhost:9394/metrics
         
     | 
| 
       89 
89 
     | 
    
         
             
            # HELP collector_working Is the master process collector able to collect metrics
         
     | 
| 
       90 
90 
     | 
    
         
             
            # TYPE collector_working gauge
         
     | 
| 
       91 
91 
     | 
    
         
             
            collector_working 1
         
     | 
| 
         @@ -97,6 +97,50 @@ awesome 10 
     | 
|
| 
       97 
97 
     | 
    
         | 
| 
       98 
98 
     | 
    
         
             
            ```
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
      
 100 
     | 
    
         
            +
            ### Easy integration into Rails
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
            You can easily integrate into any Rack application:
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
            In your Gemfile:
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            ```
         
     | 
| 
      
 107 
     | 
    
         
            +
            gem 'prometheus_exporter'
         
     | 
| 
      
 108 
     | 
    
         
            +
            ```
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
            ```
         
     | 
| 
      
 112 
     | 
    
         
            +
            # in an initializer
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            unless Rails.env == "test"
         
     | 
| 
      
 115 
     | 
    
         
            +
              require 'prometheus_exporter/middleware'
         
     | 
| 
      
 116 
     | 
    
         
            +
              # insert in position 1
         
     | 
| 
      
 117 
     | 
    
         
            +
              # instrument means method profiler will be injected in Redis and PG
         
     | 
| 
      
 118 
     | 
    
         
            +
              Rails.application.middleware.unshift PrometheusExporter::Middleware
         
     | 
| 
      
 119 
     | 
    
         
            +
            end
         
     | 
| 
      
 120 
     | 
    
         
            +
            ```
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            You may also be interested in per-process stats, this collects memory and GC stats
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
            ```
         
     | 
| 
      
 125 
     | 
    
         
            +
            # in an initializer
         
     | 
| 
      
 126 
     | 
    
         
            +
            unless Rails.env == "test"
         
     | 
| 
      
 127 
     | 
    
         
            +
              require 'prometheus_exporter/instrumentation'
         
     | 
| 
      
 128 
     | 
    
         
            +
              PrometheusExporter::Instrumentation::Process.start(type: "master")
         
     | 
| 
      
 129 
     | 
    
         
            +
            end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
            after_fork do
         
     | 
| 
      
 132 
     | 
    
         
            +
              require 'prometheus_exporter/instrumentation'
         
     | 
| 
      
 133 
     | 
    
         
            +
              PrometheusExporter::Instrumentation::Process.start(type:"web")
         
     | 
| 
      
 134 
     | 
    
         
            +
            end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
            ```
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
            Ensure you run the exporter via
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
            ```
         
     | 
| 
      
 141 
     | 
    
         
            +
            % bundle exec prometheus_exporter
         
     | 
| 
      
 142 
     | 
    
         
            +
            ```
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
       100 
144 
     | 
    
         
             
            ### Multi process mode with custom collector
         
     | 
| 
       101 
145 
     | 
    
         | 
| 
       102 
146 
     | 
    
         
             
            You can opt for custom collector logic in a multi process environment.
         
     | 
| 
         @@ -108,7 +152,7 @@ The standard collector ships "help", "type" and "name" for every metric, in some 
     | 
|
| 
       108 
152 
     | 
    
         
             
            First, define a custom collector, it is critical you inherit off `PrometheusExporter::Server::Collector`, also it is critical you have custom implementations for #process and #prometheus_metrics_text
         
     | 
| 
       109 
153 
     | 
    
         | 
| 
       110 
154 
     | 
    
         
             
            ```ruby
         
     | 
| 
       111 
     | 
    
         
            -
            class MyCustomCollector < PrometheusExporter::Server:: 
     | 
| 
      
 155 
     | 
    
         
            +
            class MyCustomCollector < PrometheusExporter::Server::CollectorBase
         
     | 
| 
       112 
156 
     | 
    
         
             
              def initialize
         
     | 
| 
       113 
157 
     | 
    
         
             
                @gauge1 = PrometheusExporter::Metric::Gauge.new("thing1", "I am thing 1")
         
     | 
| 
       114 
158 
     | 
    
         
             
                @gauge2 = PrometheusExporter::Metric::Gauge.new("thing2", "I am thing 2")
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative "./../lib/prometheus_exporter"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require_relative "./../lib/prometheus_exporter/server"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            def run
         
     | 
| 
      
 9 
     | 
    
         
            +
              port = PrometheusExporter::DEFAULT_PORT
         
     | 
| 
      
 10 
     | 
    
         
            +
              prefix = "ruby_"
         
     | 
| 
      
 11 
     | 
    
         
            +
              collector = nil
         
     | 
| 
      
 12 
     | 
    
         
            +
              verbose = false
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              OptionParser.new do |opt|
         
     | 
| 
      
 15 
     | 
    
         
            +
                opt.on('-p',
         
     | 
| 
      
 16 
     | 
    
         
            +
                       '--port INTEGER',
         
     | 
| 
      
 17 
     | 
    
         
            +
                       Integer,
         
     | 
| 
      
 18 
     | 
    
         
            +
                       "Port exporter should listen on (default: #{port})") do |o|
         
     | 
| 
      
 19 
     | 
    
         
            +
                  port = o.to_i
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                opt.on('--prefix METRIC_PREFIX', String, "Prefix to apply to all metrics (default: #{prefix})") do |o|
         
     | 
| 
      
 22 
     | 
    
         
            +
                  prefix = o.to_s
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
                opt.on('-c', '--collector CUSTOM_COLLECTOR', String, "(optional) Custom collector to run") do |o|
         
     | 
| 
      
 25 
     | 
    
         
            +
                  collector = o.to_s
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
                opt.on('-v', '--verbose') do |o|
         
     | 
| 
      
 28 
     | 
    
         
            +
                  verbose = true
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              end.parse!
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              PrometheusExporter::Metric::Base.default_prefix = prefix
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              if collector
         
     | 
| 
      
 36 
     | 
    
         
            +
                eval File.read(collector)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                ObjectSpace.each_object(Class) do |klass|
         
     | 
| 
      
 39 
     | 
    
         
            +
                  if klass < PrometheusExporter::Server::CollectorBase
         
     | 
| 
      
 40 
     | 
    
         
            +
                    collector = klass
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                if !collector
         
     | 
| 
      
 45 
     | 
    
         
            +
                  STDERR.puts "Can not find a class inheriting off PrometheusExporter::Server::Collector"
         
     | 
| 
      
 46 
     | 
    
         
            +
                  usage
         
     | 
| 
      
 47 
     | 
    
         
            +
                  exit 1
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              puts "#{Time.now} Starting prometheus exporter on port #{port}"
         
     | 
| 
      
 52 
     | 
    
         
            +
              server = PrometheusExporter::Server::WebServer.new port: port, collector: collector&.new, verbose: verbose
         
     | 
| 
      
 53 
     | 
    
         
            +
              server.start
         
     | 
| 
      
 54 
     | 
    
         
            +
              sleep
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            run
         
     | 
    
        data/lib/prometheus_exporter.rb
    CHANGED
    
    
| 
         @@ -24,10 +24,18 @@ class PrometheusExporter::Client 
     | 
|
| 
       24 
24 
     | 
    
         
             
                end
         
     | 
| 
       25 
25 
     | 
    
         
             
              end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
              def self.default
         
     | 
| 
      
 28 
     | 
    
         
            +
                @default ||= new
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def self.default=(client)
         
     | 
| 
      
 32 
     | 
    
         
            +
                @default = client
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       27 
35 
     | 
    
         
             
              MAX_SOCKET_AGE = 25
         
     | 
| 
       28 
36 
     | 
    
         
             
              MAX_QUEUE_SIZE = 10_000
         
     | 
| 
       29 
37 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
              def initialize(host: 'localhost', port 
     | 
| 
      
 38 
     | 
    
         
            +
              def initialize(host: 'localhost', port: PrometheusExporter::DEFAULT_PORT, max_queue_size: nil, thread_sleep: 0.5)
         
     | 
| 
       31 
39 
     | 
    
         
             
                @metrics = []
         
     | 
| 
       32 
40 
     | 
    
         | 
| 
       33 
41 
     | 
    
         
             
                @queue = Queue.new
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # see https://samsaffron.com/archive/2017/10/18/fastest-way-to-profile-a-method-in-ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            module PrometheusExporter::Instrumentation; end
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class PrometheusExporter::Instrumentation::MethodProfiler
         
     | 
| 
      
 5 
     | 
    
         
            +
              def self.patch(klass, methods, name)
         
     | 
| 
      
 6 
     | 
    
         
            +
                patches = methods.map do |method_name|
         
     | 
| 
      
 7 
     | 
    
         
            +
                  <<~RUBY
         
     | 
| 
      
 8 
     | 
    
         
            +
                  unless defined?(#{method_name}__mp_unpatched)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    alias_method :#{method_name}__mp_unpatched, :#{method_name}
         
     | 
| 
      
 10 
     | 
    
         
            +
                    def #{method_name}(*args, &blk)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      unless prof = Thread.current[:_method_profiler]
         
     | 
| 
      
 12 
     | 
    
         
            +
                        return #{method_name}__mp_unpatched(*args, &blk)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      end
         
     | 
| 
      
 14 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 15 
     | 
    
         
            +
                        start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 16 
     | 
    
         
            +
                        #{method_name}__mp_unpatched(*args, &blk)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 18 
     | 
    
         
            +
                        data = (prof[:#{name}] ||= {duration: 0.0, calls: 0})
         
     | 
| 
      
 19 
     | 
    
         
            +
                        data[:duration] += Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
         
     | 
| 
      
 20 
     | 
    
         
            +
                        data[:calls] += 1
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  RUBY
         
     | 
| 
      
 25 
     | 
    
         
            +
                end.join("\n")
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                klass.class_eval patches
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def self.transfer
         
     | 
| 
      
 31 
     | 
    
         
            +
                result = Thread.current[:_method_profiler]
         
     | 
| 
      
 32 
     | 
    
         
            +
                Thread.current[:_method_profiler] = nil
         
     | 
| 
      
 33 
     | 
    
         
            +
                result
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              def self.start(transfer = nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                Thread.current[:_method_profiler] = transfer || {
         
     | 
| 
      
 38 
     | 
    
         
            +
                  __start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 39 
     | 
    
         
            +
                }
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def self.clear
         
     | 
| 
      
 43 
     | 
    
         
            +
                Thread.current[:_method_profiler] = nil
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def self.stop
         
     | 
| 
      
 47 
     | 
    
         
            +
                finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 48 
     | 
    
         
            +
                if data = Thread.current[:_method_profiler]
         
     | 
| 
      
 49 
     | 
    
         
            +
                  Thread.current[:_method_profiler] = nil
         
     | 
| 
      
 50 
     | 
    
         
            +
                  start = data.delete(:__start)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  data[:total_duration] = finish - start
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
                data
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,77 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # collects stats from currently running process
         
     | 
| 
      
 2 
     | 
    
         
            +
            module PrometheusExporter::Instrumentation
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Process
         
     | 
| 
      
 4 
     | 
    
         
            +
                def self.start(client: nil, type: "ruby", frequency: 30)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  process_collector = new(type)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  client ||= PrometheusExporter::Client.default
         
     | 
| 
      
 7 
     | 
    
         
            +
                  Thread.new do
         
     | 
| 
      
 8 
     | 
    
         
            +
                    while true
         
     | 
| 
      
 9 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 10 
     | 
    
         
            +
                        metric = process_collector.collect
         
     | 
| 
      
 11 
     | 
    
         
            +
                        client.send_json metric
         
     | 
| 
      
 12 
     | 
    
         
            +
                      rescue => e
         
     | 
| 
      
 13 
     | 
    
         
            +
                        STDERR.puts("Prometheus Discoruse Failed To Collect Process Stats #{e}")
         
     | 
| 
      
 14 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 15 
     | 
    
         
            +
                        sleep frequency
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def initialize(type)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @type = type
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def collect
         
     | 
| 
      
 26 
     | 
    
         
            +
                  metric = {}
         
     | 
| 
      
 27 
     | 
    
         
            +
                  metric[:type] = "process"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  metric[:process_type] = @type
         
     | 
| 
      
 29 
     | 
    
         
            +
                  collect_gc_stats(metric)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  collect_v8_stats(metric)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  collect_process_stats(metric)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  metric
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def pid
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @pid = ::Process.pid
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def rss
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @pagesize ||= `getconf PAGESIZE`.to_i rescue 4096
         
     | 
| 
      
 41 
     | 
    
         
            +
                  File.read("/proc/#{pid}/statm").split(' ')[1].to_i * @pagesize rescue 0
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def collect_process_stats(metric)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  metric[:pid] = pid
         
     | 
| 
      
 46 
     | 
    
         
            +
                  metric[:rss] = rss
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def collect_gc_stats(metric)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  stat = GC.stat
         
     | 
| 
      
 52 
     | 
    
         
            +
                  metric[:heap_live_slots] = stat[:heap_live_slots]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  metric[:heap_free_slots] = stat[:heap_free_slots]
         
     | 
| 
      
 54 
     | 
    
         
            +
                  metric[:major_gc_count] = stat[:major_gc_count]
         
     | 
| 
      
 55 
     | 
    
         
            +
                  metric[:minor_gc_count] = stat[:minor_gc_count]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  metric[:total_allocated_objects] = stat[:total_allocated_objects]
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def collect_v8_stats(metric)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  return if !defined? MiniRacer
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  metric[:v8_heap_count] = metric[:v8_heap_size] = 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                  metric[:v8_heap_size] = metric[:v8_physical_size] = 0
         
     | 
| 
      
 64 
     | 
    
         
            +
                  metric[:v8_used_heap_size] = 0
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  ObjectSpace.each_object(MiniRacer::Context) do |context|
         
     | 
| 
      
 67 
     | 
    
         
            +
                    stats = context.heap_stats
         
     | 
| 
      
 68 
     | 
    
         
            +
                    if stats
         
     | 
| 
      
 69 
     | 
    
         
            +
                      metric[:v8_heap_count] += 1
         
     | 
| 
      
 70 
     | 
    
         
            +
                      metric[:v8_heap_size] += stats[:total_heap_size].to_i
         
     | 
| 
      
 71 
     | 
    
         
            +
                      metric[:v8_used_heap_size] += stats[:used_heap_size].to_i
         
     | 
| 
      
 72 
     | 
    
         
            +
                      metric[:v8_physical_size] += stats[:total_physical_size].to_i
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'prometheus_exporter/instrumentation/method_profiler'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'prometheus_exporter/client'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class PrometheusExporter::Middleware
         
     | 
| 
      
 7 
     | 
    
         
            +
              MethodProfiler = PrometheusExporter::Instrumentation::MethodProfiler
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def initialize(app, config = { instrument: true, client: nil })
         
     | 
| 
      
 10 
     | 
    
         
            +
                @app = app
         
     | 
| 
      
 11 
     | 
    
         
            +
                @client = config[:client] || PrometheusExporter::Client.default
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                if config[:instrument]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  if defined? Redis::Client
         
     | 
| 
      
 15 
     | 
    
         
            +
                    MethodProfiler.patch(Redis::Client, [:call, :call_pipeline], :redis)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  if defined? PG::Connection
         
     | 
| 
      
 18 
     | 
    
         
            +
                    MethodProfiler.patch(PG::Connection, [
         
     | 
| 
      
 19 
     | 
    
         
            +
                      :exec, :async_exec, :exec_prepared, :send_query_prepared, :query
         
     | 
| 
      
 20 
     | 
    
         
            +
                    ], :sql)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def call(env)
         
     | 
| 
      
 26 
     | 
    
         
            +
                MethodProfiler.start
         
     | 
| 
      
 27 
     | 
    
         
            +
                result = @app.call(env)
         
     | 
| 
      
 28 
     | 
    
         
            +
                info = MethodProfiler.stop
         
     | 
| 
      
 29 
     | 
    
         
            +
                result
         
     | 
| 
      
 30 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 31 
     | 
    
         
            +
                status = (result && result[0]) || -1
         
     | 
| 
      
 32 
     | 
    
         
            +
                params = env["action_dispatch.request.parameters"]
         
     | 
| 
      
 33 
     | 
    
         
            +
                action, controller = nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                if params
         
     | 
| 
      
 35 
     | 
    
         
            +
                  action = params["action"]
         
     | 
| 
      
 36 
     | 
    
         
            +
                  controller = params["controller"]
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                @client.send_json(
         
     | 
| 
      
 40 
     | 
    
         
            +
                  type: "web",
         
     | 
| 
      
 41 
     | 
    
         
            +
                  timings: info,
         
     | 
| 
      
 42 
     | 
    
         
            +
                  action: action,
         
     | 
| 
      
 43 
     | 
    
         
            +
                  controller: controller,
         
     | 
| 
      
 44 
     | 
    
         
            +
                  status: status
         
     | 
| 
      
 45 
     | 
    
         
            +
                )
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -2,9 +2,26 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module PrometheusExporter::Server
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
              class Collector
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Collector < CollectorBase
         
     | 
| 
      
 6 
     | 
    
         
            +
                MAX_PROCESS_METRIC_AGE = 60
         
     | 
| 
      
 7 
     | 
    
         
            +
                PROCESS_GAUGES = {
         
     | 
| 
      
 8 
     | 
    
         
            +
                  heap_free_slots: "Free ruby heap slots",
         
     | 
| 
      
 9 
     | 
    
         
            +
                  heap_live_slots: "Used ruby heap slots",
         
     | 
| 
      
 10 
     | 
    
         
            +
                  v8_heap_size: "Total JavaScript V8 heap size (bytes)",
         
     | 
| 
      
 11 
     | 
    
         
            +
                  v8_used_heap_size: "Total used JavaScript V8 heap size (bytes)",
         
     | 
| 
      
 12 
     | 
    
         
            +
                  v8_physical_size: "Physical size consumed by V8 heaps",
         
     | 
| 
      
 13 
     | 
    
         
            +
                  v8_heap_count: "Number of V8 contexts running",
         
     | 
| 
      
 14 
     | 
    
         
            +
                  rss: "Total RSS used by process",
         
     | 
| 
      
 15 
     | 
    
         
            +
                }
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                PROCESS_COUNTERS = {
         
     | 
| 
      
 18 
     | 
    
         
            +
                  major_gc_count: "Major GC operations by process",
         
     | 
| 
      
 19 
     | 
    
         
            +
                  minor_gc_count: "Minor GC operations by process",
         
     | 
| 
      
 20 
     | 
    
         
            +
                  total_allocated_objects: "Total number of allocateds objects by process",
         
     | 
| 
      
 21 
     | 
    
         
            +
                }
         
     | 
| 
       6 
22 
     | 
    
         | 
| 
       7 
23 
     | 
    
         
             
                def initialize
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @process_metrics = []
         
     | 
| 
       8 
25 
     | 
    
         
             
                  @metrics = {}
         
     | 
| 
       9 
26 
     | 
    
         
             
                  @buffer = []
         
     | 
| 
       10 
27 
     | 
    
         
             
                  @mutex = Mutex.new
         
     | 
| 
         @@ -13,27 +30,120 @@ module PrometheusExporter::Server 
     | 
|
| 
       13 
30 
     | 
    
         
             
                def process(str)
         
     | 
| 
       14 
31 
     | 
    
         
             
                  obj = JSON.parse(str)
         
     | 
| 
       15 
32 
     | 
    
         
             
                  @mutex.synchronize do
         
     | 
| 
       16 
     | 
    
         
            -
                     
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
                    if obj["type"] == "web"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      observe_web(obj)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    elsif obj["type"] == "process"
         
     | 
| 
      
 36 
     | 
    
         
            +
                      observe_process(obj)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    else
         
     | 
| 
      
 38 
     | 
    
         
            +
                      metric = @metrics[obj["name"]]
         
     | 
| 
      
 39 
     | 
    
         
            +
                      if !metric
         
     | 
| 
      
 40 
     | 
    
         
            +
                        metric = register_metric_unsafe(obj)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
                      metric.observe(obj["value"], obj["keys"])
         
     | 
| 
       19 
43 
     | 
    
         
             
                    end
         
     | 
| 
       20 
     | 
    
         
            -
                    metric.observe(obj["value"], obj["keys"])
         
     | 
| 
       21 
44 
     | 
    
         
             
                  end
         
     | 
| 
       22 
45 
     | 
    
         
             
                end
         
     | 
| 
       23 
46 
     | 
    
         | 
| 
       24 
47 
     | 
    
         
             
                def prometheus_metrics_text
         
     | 
| 
       25 
48 
     | 
    
         
             
                  @mutex.synchronize do
         
     | 
| 
       26 
     | 
    
         
            -
                    @metrics.values.map(&:to_prometheus_text).join("\n")
         
     | 
| 
      
 49 
     | 
    
         
            +
                    val = @metrics.values.map(&:to_prometheus_text).join("\n")
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    metrics = {}
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    if @process_metrics.length > 0
         
     | 
| 
      
 54 
     | 
    
         
            +
                      val << "\n"
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      @process_metrics.map do |m|
         
     | 
| 
      
 57 
     | 
    
         
            +
                        metric_key = { pid: m["pid"], type: m["process_type"] }
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                        PROCESS_GAUGES.map do |k, help|
         
     | 
| 
      
 60 
     | 
    
         
            +
                          k = k.to_s
         
     | 
| 
      
 61 
     | 
    
         
            +
                          if v = m[k]
         
     | 
| 
      
 62 
     | 
    
         
            +
                            g = metrics[k] ||= PrometheusExporter::Metric::Gauge.new(k, help)
         
     | 
| 
      
 63 
     | 
    
         
            +
                            g.observe(v, metric_key)
         
     | 
| 
      
 64 
     | 
    
         
            +
                          end
         
     | 
| 
      
 65 
     | 
    
         
            +
                        end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                        PROCESS_COUNTERS.map do |k, help|
         
     | 
| 
      
 68 
     | 
    
         
            +
                          k = k.to_s
         
     | 
| 
      
 69 
     | 
    
         
            +
                          if v = m[k]
         
     | 
| 
      
 70 
     | 
    
         
            +
                            c = metrics[k] ||= PrometheusExporter::Metric::Counter.new(k, help)
         
     | 
| 
      
 71 
     | 
    
         
            +
                            c.observe(v, metric_key)
         
     | 
| 
      
 72 
     | 
    
         
            +
                          end
         
     | 
| 
      
 73 
     | 
    
         
            +
                        end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                      end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                      val << metrics.values.map(&:to_prometheus_text).join("\n")
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    val
         
     | 
| 
       27 
81 
     | 
    
         
             
                  end
         
     | 
| 
       28 
82 
     | 
    
         
             
                end
         
     | 
| 
       29 
83 
     | 
    
         | 
| 
      
 84 
     | 
    
         
            +
                protected
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
       30 
86 
     | 
    
         
             
                def register_metric(metric)
         
     | 
| 
       31 
87 
     | 
    
         
             
                  @mutex.synchronize do
         
     | 
| 
       32 
88 
     | 
    
         
             
                    @metrics << metric
         
     | 
| 
       33 
89 
     | 
    
         
             
                  end
         
     | 
| 
       34 
90 
     | 
    
         
             
                end
         
     | 
| 
       35 
91 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
      
 92 
     | 
    
         
            +
                def ensure_web_metrics
         
     | 
| 
      
 93 
     | 
    
         
            +
                  unless @http_requests
         
     | 
| 
      
 94 
     | 
    
         
            +
                    @metrics["http_requests"] = @http_requests = PrometheusExporter::Metric::Counter.new(
         
     | 
| 
      
 95 
     | 
    
         
            +
                      "http_requests",
         
     | 
| 
      
 96 
     | 
    
         
            +
                      "Total HTTP requests from web app"
         
     | 
| 
      
 97 
     | 
    
         
            +
                    )
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    @metrics["http_duration_seconds"] = @http_duration_seconds = PrometheusExporter::Metric::Summary.new(
         
     | 
| 
      
 100 
     | 
    
         
            +
                      "http_duration_seconds",
         
     | 
| 
      
 101 
     | 
    
         
            +
                      "Time spent in HTTP reqs in seconds"
         
     | 
| 
      
 102 
     | 
    
         
            +
                    )
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    @metrics["http_redis_duration_seconds"] = @http_redis_duration_seconds = PrometheusExporter::Metric::Summary.new(
         
     | 
| 
      
 105 
     | 
    
         
            +
                      "http_redis_duration_seconds",
         
     | 
| 
      
 106 
     | 
    
         
            +
                      "Time spent in HTTP reqs in redis seconds"
         
     | 
| 
      
 107 
     | 
    
         
            +
                    )
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                    @metrics["http_sql_duration_seconds"] = @http_sql_duration_seconds = PrometheusExporter::Metric::Summary.new(
         
     | 
| 
      
 110 
     | 
    
         
            +
                      "http_sql_duration_seconds",
         
     | 
| 
      
 111 
     | 
    
         
            +
                      "Time spent in HTTP reqs in SQL in seconds"
         
     | 
| 
      
 112 
     | 
    
         
            +
                    )
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                def observe_web(obj)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  ensure_web_metrics
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  labels = {
         
     | 
| 
      
 120 
     | 
    
         
            +
                    controller: obj["controller"] || "other",
         
     | 
| 
      
 121 
     | 
    
         
            +
                    action: obj["action"] || "other"
         
     | 
| 
      
 122 
     | 
    
         
            +
                  }
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                  @http_requests.observe(1, labels.merge(status: obj["status"]))
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                  if timings = obj["timings"]
         
     | 
| 
      
 127 
     | 
    
         
            +
                    @http_duration_seconds.observe(timings["total_duration"], labels)
         
     | 
| 
      
 128 
     | 
    
         
            +
                    if redis = timings["redis"]
         
     | 
| 
      
 129 
     | 
    
         
            +
                      @http_redis_duration_seconds.observe(redis["duration"], labels)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                    if sql = timings["sql"]
         
     | 
| 
      
 132 
     | 
    
         
            +
                      @http_sql_duration_seconds.observe(sql["duration"], labels)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    end
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                def observe_process(obj)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  obj["created_at"] = now
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  @process_metrics.delete_if do |current|
         
     | 
| 
      
 143 
     | 
    
         
            +
                    obj["pid"] == current["pid"] || (current["created_at"] + MAX_PROCESS_METRIC_AGE < now)
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
                  @process_metrics << obj
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
       37 
147 
     | 
    
         | 
| 
       38 
148 
     | 
    
         
             
                def register_metric_unsafe(obj)
         
     | 
| 
       39 
149 
     | 
    
         
             
                  name = obj["name"]
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module PrometheusExporter::Server
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
              # minimal interface to implement a customer collector
         
     | 
| 
      
 4 
     | 
    
         
            +
              class CollectorBase
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # called each time a string is delivered from the web
         
     | 
| 
      
 7 
     | 
    
         
            +
                def process(str)
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # a string denoting the metrics
         
     | 
| 
      
 11 
     | 
    
         
            +
                def prometheus_metrics_text(str)
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -9,7 +9,9 @@ module PrometheusExporter::Server 
     | 
|
| 
       9 
9 
     | 
    
         
             
              class WebServer
         
     | 
| 
       10 
10 
     | 
    
         
             
                attr_reader :collector
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                def initialize(port: , collector: nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(port: , collector: nil, verbose: false)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  @verbose = verbose
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                  @total_metrics = PrometheusExporter::Metric::Counter.new("total_collector_metrics", "total metrics processed by exporter web")
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
         @@ -21,10 +23,19 @@ module PrometheusExporter::Server 
     | 
|
| 
       21 
23 
     | 
    
         
             
                  @total_sessions.observe(0)
         
     | 
| 
       22 
24 
     | 
    
         
             
                  @total_bad_metrics.observe(0)
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                  access_log = []
         
     | 
| 
      
 27 
     | 
    
         
            +
                  if verbose
         
     | 
| 
      
 28 
     | 
    
         
            +
                    access_log = [
         
     | 
| 
      
 29 
     | 
    
         
            +
                      [$stderr, WEBrick::AccessLog::COMMON_LOG_FORMAT],
         
     | 
| 
      
 30 
     | 
    
         
            +
                      [$stderr, WEBrick::AccessLog::REFERER_LOG_FORMAT],
         
     | 
| 
      
 31 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    logger = WEBrick::Log.new($stderr)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       24 
35 
     | 
    
         
             
                  @server = WEBrick::HTTPServer.new(
         
     | 
| 
       25 
36 
     | 
    
         
             
                    Port: port,
         
     | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
       27 
     | 
    
         
            -
                     
     | 
| 
      
 37 
     | 
    
         
            +
                    Logger: logger,
         
     | 
| 
      
 38 
     | 
    
         
            +
                    AccessLog: access_log
         
     | 
| 
       28 
39 
     | 
    
         
             
                  )
         
     | 
| 
       29 
40 
     | 
    
         | 
| 
       30 
41 
     | 
    
         
             
                  @collector = collector || Collector.new
         
     | 
| 
         @@ -52,7 +63,7 @@ module PrometheusExporter::Server 
     | 
|
| 
       52 
63 
     | 
    
         
             
                      handle_metrics(req, res)
         
     | 
| 
       53 
64 
     | 
    
         
             
                    else
         
     | 
| 
       54 
65 
     | 
    
         
             
                      res.status = 404
         
     | 
| 
       55 
     | 
    
         
            -
                      res.body = "Not Found! The Prometheus  
     | 
| 
      
 66 
     | 
    
         
            +
                      res.body = "Not Found! The Prometheus Ruby Exporter only listens on /metrics and /send-metrics"
         
     | 
| 
       56 
67 
     | 
    
         
             
                    end
         
     | 
| 
       57 
68 
     | 
    
         
             
                  end
         
     | 
| 
       58 
69 
     | 
    
         
             
                end
         
     | 
| 
         @@ -64,6 +75,12 @@ module PrometheusExporter::Server 
     | 
|
| 
       64 
75 
     | 
    
         
             
                      @total_metrics.observe
         
     | 
| 
       65 
76 
     | 
    
         
             
                      @collector.process(block)
         
     | 
| 
       66 
77 
     | 
    
         
             
                    rescue => e
         
     | 
| 
      
 78 
     | 
    
         
            +
                      if @verbose
         
     | 
| 
      
 79 
     | 
    
         
            +
                        STDERR.puts
         
     | 
| 
      
 80 
     | 
    
         
            +
                        STDERR.puts e.inspect
         
     | 
| 
      
 81 
     | 
    
         
            +
                        STDERR.puts e.backtrace
         
     | 
| 
      
 82 
     | 
    
         
            +
                        STDERR.puts
         
     | 
| 
      
 83 
     | 
    
         
            +
                      end
         
     | 
| 
       67 
84 
     | 
    
         
             
                      @total_bad_metrics.observe
         
     | 
| 
       68 
85 
     | 
    
         
             
                      res.body = "Bad Metrics #{e}"
         
     | 
| 
       69 
86 
     | 
    
         
             
                      res.status = e.respond_to?(:status_code) ? e.status_code : 500
         
     | 
    
        data/prometheus_exporter.gemspec
    CHANGED
    
    | 
         @@ -18,12 +18,13 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       18 
18 
     | 
    
         
             
                f.match(%r{^(test|spec|features|bin)/})
         
     | 
| 
       19 
19 
     | 
    
         
             
              end
         
     | 
| 
       20 
20 
     | 
    
         
             
              spec.bindir        = "bin"
         
     | 
| 
       21 
     | 
    
         
            -
              spec.executables   =  
     | 
| 
      
 21 
     | 
    
         
            +
              spec.executables   = ["prometheus_exporter"]
         
     | 
| 
       22 
22 
     | 
    
         
             
              spec.require_paths = ["lib"]
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
              spec.add_development_dependency "bundler", "~> 1.16"
         
     | 
| 
       25 
25 
     | 
    
         
             
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
       26 
26 
     | 
    
         
             
              spec.add_development_dependency "minitest", "~> 5.0"
         
     | 
| 
       27 
27 
     | 
    
         
             
              spec.add_development_dependency "guard", "~> 2.0"
         
     | 
| 
      
 28 
     | 
    
         
            +
              spec.add_development_dependency "mini_racer", "~> 0.1"
         
     | 
| 
       28 
29 
     | 
    
         
             
              spec.add_development_dependency "guard-minitest", "~> 2.0"
         
     | 
| 
       29 
30 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: prometheus_exporter
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.7
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Sam Saffron
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2018-01- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2018-01-30 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: bundler
         
     | 
| 
         @@ -66,6 +66,20 @@ dependencies: 
     | 
|
| 
       66 
66 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       67 
67 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       68 
68 
     | 
    
         
             
                    version: '2.0'
         
     | 
| 
      
 69 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 70 
     | 
    
         
            +
              name: mini_racer
         
     | 
| 
      
 71 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 72 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 73 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '0.1'
         
     | 
| 
      
 76 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 77 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 78 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 79 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 80 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '0.1'
         
     | 
| 
       69 
83 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       70 
84 
     | 
    
         
             
              name: guard-minitest
         
     | 
| 
       71 
85 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -83,7 +97,8 @@ dependencies: 
     | 
|
| 
       83 
97 
     | 
    
         
             
            description: Prometheus metric collector and exporter for Ruby
         
     | 
| 
       84 
98 
     | 
    
         
             
            email:
         
     | 
| 
       85 
99 
     | 
    
         
             
            - sam.saffron@gmail.com
         
     | 
| 
       86 
     | 
    
         
            -
            executables: 
     | 
| 
      
 100 
     | 
    
         
            +
            executables:
         
     | 
| 
      
 101 
     | 
    
         
            +
            - prometheus_exporter
         
     | 
| 
       87 
102 
     | 
    
         
             
            extensions: []
         
     | 
| 
       88 
103 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       89 
104 
     | 
    
         
             
            files:
         
     | 
| 
         @@ -97,16 +112,22 @@ files: 
     | 
|
| 
       97 
112 
     | 
    
         
             
            - README.md
         
     | 
| 
       98 
113 
     | 
    
         
             
            - Rakefile
         
     | 
| 
       99 
114 
     | 
    
         
             
            - bench/bench.rb
         
     | 
| 
      
 115 
     | 
    
         
            +
            - bin/prometheus_exporter
         
     | 
| 
       100 
116 
     | 
    
         
             
            - examples/custom_collector.rb
         
     | 
| 
       101 
117 
     | 
    
         
             
            - lib/prometheus_exporter.rb
         
     | 
| 
       102 
118 
     | 
    
         
             
            - lib/prometheus_exporter/client.rb
         
     | 
| 
      
 119 
     | 
    
         
            +
            - lib/prometheus_exporter/instrumentation.rb
         
     | 
| 
      
 120 
     | 
    
         
            +
            - lib/prometheus_exporter/instrumentation/method_profiler.rb
         
     | 
| 
      
 121 
     | 
    
         
            +
            - lib/prometheus_exporter/instrumentation/process.rb
         
     | 
| 
       103 
122 
     | 
    
         
             
            - lib/prometheus_exporter/metric.rb
         
     | 
| 
       104 
123 
     | 
    
         
             
            - lib/prometheus_exporter/metric/base.rb
         
     | 
| 
       105 
124 
     | 
    
         
             
            - lib/prometheus_exporter/metric/counter.rb
         
     | 
| 
       106 
125 
     | 
    
         
             
            - lib/prometheus_exporter/metric/gauge.rb
         
     | 
| 
       107 
126 
     | 
    
         
             
            - lib/prometheus_exporter/metric/summary.rb
         
     | 
| 
      
 127 
     | 
    
         
            +
            - lib/prometheus_exporter/middleware.rb
         
     | 
| 
       108 
128 
     | 
    
         
             
            - lib/prometheus_exporter/server.rb
         
     | 
| 
       109 
129 
     | 
    
         
             
            - lib/prometheus_exporter/server/collector.rb
         
     | 
| 
      
 130 
     | 
    
         
            +
            - lib/prometheus_exporter/server/collector_base.rb
         
     | 
| 
       110 
131 
     | 
    
         
             
            - lib/prometheus_exporter/server/web_server.rb
         
     | 
| 
       111 
132 
     | 
    
         
             
            - lib/prometheus_exporter/version.rb
         
     | 
| 
       112 
133 
     | 
    
         
             
            - prometheus_exporter.gemspec
         
     |