Last active
August 27, 2019 17:07
-
-
Save electron0zero/78c58b1ef7d61020d656a2a2e6c96524 to your computer and use it in GitHub Desktop.
Ruby covert Prometheus metrics collected to graphite format (hash with series and value)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# Module to covert Prometheus metrics collected to graphite | |
# format (hash with series and value) | |
# This is used to ship ruby-kafka metrics to graphite | |
# | |
# usage: | |
# require 'prometheus/client' | |
# registry = Prometheus::Client.registry | |
# data = PromToGraphite.marshal(registry) | |
# | |
# # drop all gauge type metrics | |
# data = PromToGraphite.marshal(registry, skip_types: [:gauge]) | |
module PromToGraphite | |
FORMAT = '%s.%s' | |
# @param [Prometheus::Client.registry] registry | |
# @param [Array] array of metric types to skip | |
def self.marshal(registry, skip_types: []) | |
# ensure we always have symbols | |
skip_types.map!(&:to_sym) | |
values = {} | |
registry.metrics.each do |metric| | |
next if skip_types.include?(metric.type) | |
metric.values.each do |label_set, value| | |
representation(metric, label_set, value) do |series, metric_value| | |
values[series] = metric_value | |
end | |
end | |
end | |
return values | |
end | |
class << self | |
private | |
# take a metric and transform it into graphite representation | |
def representation(metric, label_set, value, &block) | |
set = metric.base_labels.merge(label_set) | |
l_str = labels(set) | |
if metric.type == :summary | |
summary(metric.name, l_str, value, &block) | |
elsif metric.type == :histogram | |
histogram(metric.name, l_str, value, &block) | |
else | |
yield metric(metric.name, l_str, value) | |
end | |
end | |
# transform a Prometheus summery metric into graphite series | |
def summary(name, l_str, value) | |
value.each do |q, v| | |
yield metric("#{name}.quantile.#{sanitize_value(q)}", l_str, v) | |
end | |
yield metric("#{name}.sum", l_str, value.sum) | |
yield metric("#{name}.count", l_str, value.total) | |
end | |
# transform a Prometheus histogram metric into graphite series | |
def histogram(name, l_str, value) | |
bucket = "#{name}.bucket" | |
value.each do |q, v| | |
yield metric("#{bucket}.le.#{sanitize_value(q)}", l_str, v) | |
end | |
yield metric("#{bucket}.le.Inf", l_str, value.total) | |
yield metric("#{name}.sum", l_str, value.sum) | |
yield metric("#{name}.count", l_str, value.total) | |
end | |
# format a Prometheus metric into graphite series | |
# @return series, value | |
def metric(name, labels, value) | |
# labels string is prefixed because they usually have low cardinality | |
return format(FORMAT, labels, name), value | |
end | |
# convert labels into labels string | |
def labels(set) | |
return if set.empty? | |
strings = [] | |
# NOTE: sort by keys makes sure we always send data with same series name | |
set.keys.sort.each do |key| | |
strings << format(FORMAT, key, sanitize_value(set[key])) | |
end | |
return strings.join('.').to_s | |
end | |
# sanitize label value | |
def sanitize_value(item) | |
item.to_s.gsub(/[^a-zA-Z0-9\-_]/, '_') | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment