Skip to content

Instantly share code, notes, and snippets.

@electron0zero
Last active August 27, 2019 17:07
Show Gist options
  • Save electron0zero/78c58b1ef7d61020d656a2a2e6c96524 to your computer and use it in GitHub Desktop.
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)
# 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