注册指标

require "yabeda/prometheus"时,继承Yabeda::BaseAdapterYabeda::Prometheus::Adapter会实现好各种register_xxx!方法,并将自身注册到Yabeda@adapters

(可参考[[yabeda浅析]])

# yabeda-prometheus-0.8.0/lib/yabeda/prometheus/adapter.rb
module Yabeda
  class Prometheus::Adapter < BaseAdapter

    def registry
      @registry ||= ::Prometheus::Client.registry
    end

    def register_counter!(metric)
      validate_metric!(metric)
      registry.counter(
        build_name(metric),
        docstring: metric.comment,
        labels: Array(metric.tags),
        store_settings: store_settings(metric),
      )
    end

    def register_gauge!(metric)
      # ...
    end

    def register_histogram!(metric)
      # ...
    end

    Yabeda.register_adapter(:prometheus, new)
  end
end

暴露metrics接口

一般使用Yabeda::Prometheus::Exporter.start_metrics_server!启动服务器来提供/metrics接口

可以通过环境变量PROMETHEUS_EXPORTER_PORT改监听端口

这个服务器自身就是::Prometheus::Middleware::Exporter的子类

# yabeda-prometheus-0.8.0/lib/yabeda/prometheus/exporter.rb
module Yabeda
  module Prometheus
    class Exporter < ::Prometheus::Middleware::Exporter
      NOT_FOUND_HANDLER = lambda do |_env|
        [404, { "Content-Type" => "text/plain" }, ["Not Found\n"]]
      end.freeze

      class << self
        def call(env)
          @app ||= new(NOT_FOUND_HANDLER, path: "/")
          @app.call(env)
        end

        def start_metrics_server!(**rack_app_options)
          Thread.new do
            default_port = ENV.fetch("PORT", 9394)
            ::Rack::Handler::WEBrick.run(
              rack_app(**rack_app_options),
              Port: ENV.fetch("PROMETHEUS_EXPORTER_PORT", default_port),
              # ...
            )
          end
        end

        def rack_app(exporter = self, path: "/metrics", logger: Logger.new(IO::NULL))
          ::Rack::Builder.new do
            # ...
            use exporter, path: path
            run NOT_FOUND_HANDLER
          end
        end
      end

      def initialize(app, options = {})
        super(app, options.merge(registry: Yabeda::Prometheus.registry))
      end

      def call(env)
        ::Yabeda.collect! if env["PATH_INFO"] == path
        # ...
        super
      end
    end
  end
end