# encoding: UTF-8 module Prometheus module Client module Formats # Text format is human readable mainly used for manual inspection. module Text MEDIA_TYPE = 'text/plain'.freeze VERSION = '0.0.4'.freeze CONTENT_TYPE = "#{MEDIA_TYPE}; version=#{VERSION}".freeze METRIC_LINE = '%s%s %s'.freeze TYPE_LINE = '# TYPE %s %s'.freeze HELP_LINE = '# HELP %s %s'.freeze LABEL = '%s="%s"'.freeze SEPARATOR = ','.freeze DELIMITER = "\n".freeze REGEX = { doc: /[\n\\]/, label: /[\n\\"]/ }.freeze REPLACE = { "\n" => '\n', '\\' => '\\\\', '"' => '\"' }.freeze def self.marshal(registry) lines = [] registry.metrics.each do |metric| lines << format(TYPE_LINE, metric.name, metric.type) lines << format(HELP_LINE, metric.name, escape(metric.docstring)) metric.values.each do |label_set, value| representation(metric, label_set, value) { |l| lines << l } end end # there must be a trailing delimiter (lines << nil).join(DELIMITER) end class << self private def representation(metric, label_set, value, &block) set = metric.base_labels.merge(label_set) if metric.type == :summary summary(metric.name, set, value, &block) elsif metric.type == :histogram histogram(metric.name, set, value, &block) else yield metric(metric.name, labels(set), value) end end def summary(name, set, value) value.each do |q, v| yield metric(name, labels(set.merge(quantile: q)), v) end l = labels(set) yield metric("#{name}_sum", l, value.sum) yield metric("#{name}_count", l, value.total) end def histogram(name, set, value) bucket = "#{name}_bucket" value.each do |q, v| yield metric(bucket, labels(set.merge(le: q)), v) end yield metric(bucket, labels(set.merge(le: '+Inf')), value.total) l = labels(set) yield metric("#{name}_sum", l, value.sum) yield metric("#{name}_count", l, value.total) end def metric(name, labels, value) format(METRIC_LINE, name, labels, value) end def labels(set) return if set.empty? strings = set.each_with_object([]) do |(key, value), memo| memo << format(LABEL, key, escape(value, :label)) end "{#{strings.join(SEPARATOR)}}" end def escape(string, format = :doc) string.to_s.gsub(REGEX[format], REPLACE) end end end end end end