hermes

module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2021 License: MIT

README

Hermes: Push Gateway for Prometheus Metrics

UDP Push gateway for prometheus metrics

The Hermes server acts as a push-gateway to allow worker processes to push Prometheus Metrics to an intermediate metric store, which is then scraped by some Prometheus instance. This avoids the need to run a Prometheus server instance within each worker process.

Communicate is handled entirely via UDP to provide a low-overhead interface that will not stop/break any worker processes if the Hermes server stops working (as a HTTP/TCP connection would). The metrics that can be pushed to a Hermes server instance are defined in a JSON file that is mounted into the container of the service. The JSON file contains definitions for Gauges and Counters, which are then created at the server start time. JSON payloads containing the metric name and JSON payload can then be pushed to the Hermes server within the client code via the UDP interface (see example/send_metric.py) for an example of how to push UDP JSON packets in Python

A typical JSON configuration can be defined as follows

{
    "service_name": "testing-service",
    "gauges": [
        {
            "metric_name": "sample_gauge",
            "metric_description": "gauges are awesome",
            "labels": ["label_1", "label_2"]
        }
    ],
    "counters": [
        {
            "metric_name": "sample_counter",
            "metric_description": "counters are awesome too",
            "labels": ["label_1"]
        }
    ]
}

Note that currently only one service name is supported per Hermes instance, as Hermes is designed to be a side-cart application for applications deployed on Docker Swarm, Kubernetes and similar container orchestration platforms.

The following Dockerfile illustrates how to use the Hermes image

FROM psauerborn/hermes

COPY ./hermes_config.json ./

ENV CONFIG_FILE_PATH=./hermes_config.json

The UDP interface by default listens on port 7789, which can be configured in the environment variables of the container, while the Prometheus interface listens on port 8080. The UDP packets send to the Hermes server must have the following format

Counters
{
    "metric_name": "sample_counter",
    "payload": {
        "labels": {
            "label_1": "testing label 1"
        }
    }
}
Gauges
{
    "metric_name": "sample_gauge",
    "payload": {
        "labels": {
            "label_1": "testing label 1",
            "label_2": "testing label 2"
        },
        "operation": "increment"
    }
}
{
    "metric_name": "sample_gauge",
    "payload": {
        "labels": {
            "label_1": "testing label 1",
            "label_2": "testing label 2"
        },
        "operation": "decrement"
    }
}
{
    "metric_name": "sample_gauge",
    "payload": {
        "labels": {
            "label_1": "testing label 1",
            "label_2": "testing label 2"
        },
        "value": 65.4,
        "operation": "set"
    }
}
Histograms
{
    "metric_name": "sample_histogram",
    "payload": {
        "labels": {
            "label_1": "testing label 1"
        },
        "observation": 65.4
    }
}
Summaries
{
    "metric_name": "sample_summary",
    "payload": {
        "labels": {
            "label_1": "testing label 1"
        },
        "observation": 65.4
    }
}

Note that the labels defined in the JSON packets must match the labels defined in the Hermes configuration file

Python Client Library

Hermes has a client library written in python (Go version coming soon). The package can be installed with

pip install python-hermes

Two high-level utility functions are provided, along with context managers and decorators that can be used to automatically manage gauge and counter interfaces. See the examples in example/python directory for implementation examples

Counters
import time
from hermes import set_hermes_config, increment_counter, counter_wrapper


def example_job():
    """Example job used to demonstrate counters
    set manually using the provided functions"""
    increment_counter('sample_counter', {'label_1': 'demo label 1'})
    time.sleep(5)

@counter_wrapper('sample_counter', {'label_1': 'demo label 1'})
def example_job_wrapper_pre():
    """Example job used to demonstrate counters
    set with the decorator"""
    print('this counter has been incremented on start')
    time.sleep(5)

@counter_wrapper('sample_counter', {'label_1': 'demo label 1'}, pre_execution=False)
def example_job_wrapper_post():
    """Example job used to demonstrate counters
    set with the decorator"""
    print('this counter will be incremented when im finished')
    time.sleep(5)


if __name__ == '__main__':

    set_hermes_config('localhost', 7789)
    example_job_wrapper_post()
Gauges
from hermes import set_gauge, increment_gauge, decrement_gauge, \
    hermes_gauge, gauge_wrapper, set_hermes_config


def example_job():
    """Example job used to demonstrate gauges
    set manually using the provided functions"""
    increment_gauge('sample_gauge', {'label_1': 'demo label 1', 'label_2': 'demo label 2'})
    time.sleep(5)
    decrement_gauge('sample_gauge', {'label_1': 'demo label 1', 'label_2': 'demo label 2'})

def example_job_context():
    """Example job used to demonstrate gauges set
    with the context manager provided"""
    labels = {'label_1': 'demo label 1', 'label_2': 'demo label 2'}
    with hermes_gauge('sample_gauge', labels=labels):
        print('this gauge has been incremented on start and will decrement when Im done')
        time.sleep(5)

@gauge_wrapper('sample_gauge', {'label_1': 'demo label 1', 'label_2': 'demo label 2'})
def example_job_wrapper():
    """Example job used to demonstrate gauges
    set with the decorator"""
    print('this gauge has been incremented on start and will decrement when Im done')
    time.sleep(5)


if __name__ == '__main__':

    set_hermes_config('localhost', 7789)
    example_job_wrapper()
Histograms
from hermes import observe_histogram

if __name__ == '__main__':

    set_hermes_config('localhost', 7789)

    labels = {'label_1': 'test-label'}
    observe_histogram('sample_histogram', labels=labels, observation=6.54)
Summaries
from hermes import observe_summary

if __name__ == '__main__':

    set_hermes_config('localhost', 7789)

    labels = {'label_1': 'test-label'}
    observe_summary('sample_summary', labels=labels, observation=20.84)

Go Client Library

The Hermes package also ships with a Go client library. The following code snippet demonstrates how the Go Client can be used

package main

import (
    "github.com/PSauerborn/hermes/pkg/client"
)

func main() {
    // set configuration for Hermes client and increment sample counter
    client := hermes_client.New("192.168.99.100", 7789)

    // increment counter metric over hermes server
    client.IncrementCounter("sample_counter",
        map[string]string{"label_1": "test-label"})

    // increment gauge
    client.IncrementGauge("sample_gauge",
        map[string]string{"label_1": "test-label", "label_2": "test-label-2"})

    // decrement gauge
    client.DecrementGauge("sample_gauge",
        map[string]string{"label_1": "test-label", "label_2": "test-label-2"})

    // set value of gauge
    client.SetGauge("sample_gauge",
        map[string]string{"label_1": "test-label", "label_2": "test-label-random"}, 54)

    // make observation on histogram
    client.ObserveHistogram("sample_histogram",
        map[string]string{"label_1": "test-label"}, 1233)

    // make observation on summary
    client.ObserveSummary("sample_summary",
        map[string]string{"label_1": "test-label"}, 5)
}

Directories

Path Synopsis
cmd
example
go
pkg

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL