Current section
Files
Jump to
Current section
Files
README.md
# ThemisPrometheus client in pure Gleam!Please remember that Themis is still in early development.Only Erlang target supported currently.[](https://hex.pm/packages/themis)[](https://hexdocs.pm/themis/)```shgleam add themis```## Quick Start```gleamimport gleam/dictimport gleam/ioimport gleam/setimport themisimport themis/counterimport themis/gaugeimport themis/histogramimport themis/numberpub fn main() { // initialize the metrics store themis.init() // Gauge // This can fail if the metric name is invalid let assert Ok(_) = gauge.new( "my_first_metric", "A gauge Prometheus metric", ) let labels = dict.from_list([#("foo", "bar")]) let value = number.integer(10) let assert Ok(_) = gauge.observe("my_first_metric", labels, value) // Counter let assert Ok(_) = counter.new( "my_second_metric", "A counter Prometheus metric", ) let labels = dict.from_list([#("wibble", "wobble")]) let other_labels = dict.from_list([#("wii", "woo")]) let assert Ok(_) = counter.new("my_second_metric", "A counter Prometheus metric") let assert Ok(_) = counter.increment("my_second_metric", labels) let assert Ok(_) = counter.increment_by( "my_second_metric", other_labels, number.decimal(1.2), ) // Histogram // Histograms work with buckets. Each bucket needs an upper boundary. // Read more about histograms here https://prometheus.io/docs/practices/histograms/ let buckets = set.from_list([ number.decimal(0.05), number.decimal(0.1), number.decimal(0.25), number.decimal(0.5), number.integer(1), ]) let assert Ok(_) = histogram.new( "my_third_metric", "A histogram Prometheus metric", buckets, ) let value = number.integer(20) let other_value = number.decimal(1.5) let labels = dict.from_list([#("toto", "tata")]) let other_labels = dict.from_list([#("toto", "titi")]) let assert Ok(_) = histogram.observe("my_third_metric", labels, value) // When incrementing a histogram with new labels, a new histogram will automatically be initialized let assert Ok(_) = histogram.observe( "my_third_metric", other_labels, other_value, ) // Printing all the metrics as a Prometheus-scrapable String let assert Ok(prometheus_string) = themis.print() io.println(prometheus_string)}```This code will print the following prometheus-compatible metrics:```# HELP my_first_metric A gauge Prometheus metric# TYPE my_first_metric gaugemy_first_metric{foo="bar"} 10# HELP my_second_metric_total A counter Prometheus metric# TYPE my_second_metric_total countermy_second_metric_total{wibble="wobble"} 1my_second_metric_total{wii="woo"} 1.2# HELP my_third_metric A histogram Prometheus metric# TYPE my_third_metric histogrammy_third_metric_bucket{le="0.05",toto="tata"} 0my_third_metric_bucket{le="0.1",toto="tata"} 0my_third_metric_bucket{le="0.25",toto="tata"} 0my_third_metric_bucket{le="0.5",toto="tata"} 0my_third_metric_bucket{le="1",toto="tata"} 0my_third_metric_bucket{le="+Inf",toto="tata"} 1my_third_metric_sum{toto="tata"} 1my_third_metric_count{toto="tata"} 1my_third_metric_bucket{le="0.05",toto="titi"} 0my_third_metric_bucket{le="0.1",toto="titi"} 0my_third_metric_bucket{le="0.25",toto="titi"} 0my_third_metric_bucket{le="0.5",toto="titi"} 0my_third_metric_bucket{le="1",toto="titi"} 0my_third_metric_bucket{le="+Inf",toto="titi"} 1my_third_metric_sum{toto="titi"} 1my_third_metric_count{toto="titi"} 1```Further documentation can be found at <https://hexdocs.pm/themis>.## Usage### Working with Different Numeric TypesThemis metric values are set using the dedicated `Number` type. There are 5 number types available:```gleamimport themis/number// Integer valueslet integer = number.integer(1_234_567)// Decimal (float) valueslet decimal = number.decimal(23.5)// Special valueslet positive_infinity = number.positive_infinity()let negative_infinity = number.negative_infinity()let not_a_number = number.not_a_number()```### Metric Types#### GaugesGauges are metrics that represent a single numerical value that can arbitrarily go up and down. They are typically used for measured values like temperatures, current memory usage, or number of active connections.```gleamimport themis/gauge// Create a new gauge metriclet assert Ok(_) = gauge.new( "process_memory_bytes", "Current memory usage in bytes", )// Set a gauge value with labelslet labels = dict.from_list([#("process", "web_server")])let value = number.integer(52_428_800) // 50MB in byteslet assert Ok(_) = gauge.observe("process_memory_bytes", labels, value)```#### CountersCounters are cumulative metrics that can only increase or be reset to zero. They are typically used to count requests served, tasks completed, errors occurred, or other countable occurrences.```gleamimport themis/counter// Create a new counter metriclet assert Ok(_) = counter.new( "http_requests_total", "Total number of HTTP requests made", )// Currently, counters cannot be initialized.// To have a counter of value 0, you must increment it by 0:let labels = dict.from_list([#("method", "GET"), #("path", "/api/users")])let assert Ok(_) = counter.increment_by("http_requests_total", labels, number.integer(0))// Increment counter by 1let assert Ok(_) = counter.increment("http_requests_total", labels)// Increment counter by specific amountlet assert Ok(_) = counter.increment_by( "http_requests_total", labels, number.decimal(5.0), )```#### HistogramsHistograms sample observations (usually duration or response size) and count them in configurable buckets. They also provide a sum of all observed values and a count of observations.```gleamimport themis/histogram// Define histogram buckets (upper bounds of observation buckets in seconds)let buckets = set.from_list([ number.decimal(0.005), // 5ms number.decimal(0.01), // 10ms number.decimal(0.025), // 25ms number.decimal(0.05), // 50ms number.decimal(0.1), // 100ms number.decimal(0.25), // 250ms number.decimal(0.5), // 500ms number.decimal(1.0), // 1s ])// Create a new histogram metriclet assert Ok(_) = histogram.new( "http_request_duration_seconds", "HTTP request duration in seconds", buckets, )// Record an observationlet labels = dict.from_list([#("method", "POST"), #("path", "/api/users")])let duration = number.decimal(0.157) // 157mslet assert Ok(_) = histogram.observe( "http_request_duration_seconds", labels, duration, )```Each histogram observation is counted in all buckets with upper bounds greater than the observation value. The `+Inf` bucket is automatically added and counts all observations. Additionally, histograms track the sum of all observed values and the total count of observations.#### SummariesSummaries have not yet been implemented, because at first glance it seems an accurate summary must keep a complete history of all the observed values, which will be a huge memory hog. This means I would have to implement some algorithm that goes way above my head to instead derive an approximation. I ain't doin' that (maybe one day if I need summaries but don't hold your breath).<br>If you're feeling adventurous, feel free to open a PR.## Known issues- Javascript target is not supported. While it is not *impossible* to implement, I am not a Javascript developer. To make Themis compatible with the Javascript target, a replacement for the Erlang ETS tables (which store all the metrics) must be used. If you're interested in implementing a Javascript-compatible metrics store for Themis, please open an issue.## LicenseMIT