Thanks to visit codestin.com
Credit goes to github.com

Skip to content

ToddG/Themis

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Themis

Prometheus client in pure Gleam!

Please remember that Themis is still in early development.

Only Erlang target supported currently.

Package Version Hex Docs

gleam add themis

Quick Start

import gleam/dict
import gleam/io
import gleam/set
import themis
import themis/counter
import themis/gauge
import themis/histogram
import themis/number

pub fn main() {
  // initialize the metrics store
  themis.init()

  // ------------------------------------------------------------------
  // Gauge
  // ------------------------------------------------------------------

  // NOTES:
  // This can fail if the metric name is invalid
  // See discussion [here](https://github.com/guillheu/Themis/issues/5)
  // Summary: 'gauge' is blacklisted for a metric name
  let assert Ok(_) = gauge.new("gge_01", "My first Prometheus 'gauge' metric")

  let labels = dict.from_list([#("foo", "bar")])
  let value = number.integer(10)
  let assert Ok(_) = gauge.observe("gge_01", labels, value)

  // ------------------------------------------------------------------
  // Counter
  // ------------------------------------------------------------------
  // NOTES:
  // This can fail if the metric name is invalid
  // See discussion [here](https://github.com/guillheu/Themis/issues/5)
  // Summary: 'counter' is blacklisted for a metric name
  // Summary: counter metric names must end with '_total'
  let assert Ok(_) =
    counter.new("cntr_01_total", "My first Prometheus 'counter' metric")
  let labels = dict.from_list([#("wibble", "wobble")])
  let other_labels = dict.from_list([#("wii", "woo")])
  let assert Ok(_) =
    counter.new("cntr_02_total", "My second Prometheus 'counter' metric")
  let assert Ok(_) = counter.increment("cntr_02_total", labels)
  let assert Ok(_) =
    counter.increment_by("cntr_02_total", other_labels, number.decimal(1.2))

  // ------------------------------------------------------------------
  // Histogram
  // ------------------------------------------------------------------
  //
  // NOTES:
  // Histograms work with buckets. Each bucket needs an upper boundary.
  // Read more about histograms here https://prometheus.io/docs/practices/histograms/
  // This can fail if the metric name is invalid
  // See discussion [here](https://github.com/guillheu/Themis/issues/5)
  // Summary: 'histogram' is blacklisted for a metric name
  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("hgm_01", "My first Prometheus 'histogram' 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("hgm_01", labels, value)
    // When incrementing a histogram with new labels, a new histogram will automatically be initialized
    let assert Ok(_) =
      histogram.observe(
        "hgm_01",
        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:

# TYPE gge_01 gauge
# HELP gge_01 My first Prometheus 'gauge' metric
gge_01{foo="bar"} 10


# TYPE cntr_02_total counter
# HELP cntr_02_total My second Prometheus 'counter' metric
cntr_02_total{wibble="wobble"} 1
cntr_02_total{wii="woo"} 1.2

# TYPE cntr_01_total counter
# HELP cntr_01_total My first Prometheus 'counter' metric


# TYPE hgm_01 histogram
# HELP hgm_01 My first Prometheus 'histogram' metric

hgm_01_count{toto="titi"} 1
hgm_01_sum{toto="titi"} 1.5
hgm_01_bucket{le="+Inf",toto="titi"} 1
hgm_01_bucket{le="0.05",toto="titi"} 0
hgm_01_bucket{le="0.1",toto="titi"} 0
hgm_01_bucket{le="0.25",toto="titi"} 0
hgm_01_bucket{le="0.5",toto="titi"} 0
hgm_01_bucket{le="1",toto="titi"} 0

hgm_01_count{toto="tata"} 1
hgm_01_sum{toto="tata"} 20
hgm_01_bucket{le="+Inf",toto="tata"} 1
hgm_01_bucket{le="0.05",toto="tata"} 0
hgm_01_bucket{le="0.1",toto="tata"} 0
hgm_01_bucket{le="0.25",toto="tata"} 0
hgm_01_bucket{le="0.5",toto="tata"} 0
hgm_01_bucket{le="1",toto="tata"} 0

Further documentation can be found at https://hexdocs.pm/themis.

Usage

Working with Different Numeric Types

Themis metric values are set using the dedicated Number type. There are 5 number types available:

import themis/number

// Integer values
let integer = number.integer(1_234_567)

// Decimal (float) values
let decimal = number.decimal(23.5)

// Special values
let positive_infinity = number.positive_infinity()
let negative_infinity = number.negative_infinity()
let not_a_number = number.not_a_number()

Metric Types

Gauges

Gauges 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.

import themis/gauge

// Create a new gauge metric
let assert Ok(_) = 
  gauge.new(
    "process_memory_bytes",
    "Current memory usage in bytes",
  )

// Set a gauge value with labels
let labels = dict.from_list([#("process", "web_server")])
let value = number.integer(52_428_800)  // 50MB in bytes
let assert Ok(_) =
  gauge.observe("process_memory_bytes", labels, value)

Counters

Counters 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.

import themis/counter

// Create a new counter metric
let 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 1
let assert Ok(_) =
  counter.increment("http_requests_total", labels)

// Increment counter by specific amount
let assert Ok(_) =
  counter.increment_by(
    "http_requests_total",
    labels,
    number.decimal(5.0),
  )

Histograms

Histograms 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.

import 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 metric
let assert Ok(_) =
  histogram.new(
    "http_request_duration_seconds",
    "HTTP request duration in seconds",
    buckets,
  )

// Record an observation
let labels = dict.from_list([#("method", "POST"), #("path", "/api/users")])
let duration = number.decimal(0.157)  // 157ms
let 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.

Summaries

Summaries 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).
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.

License

MIT

About

Prometheus client in pure Gleam!

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Gleam 98.5%
  • Erlang 1.5%