metricfuncs.inc.php 3.69 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');

require __DIR__ . '/../../vendor/autoload.php';
include_once("cachefuncs.inc.php");

use \Prometheus\Storage\InMemory;
use \Prometheus\CollectorRegistry;
use \Prometheus\RenderTextFormat;

// We always pass through an InMemory cache. This means that
// metrics are only good while the php-fpm service is running
// and will start again at 0 if it's restarted.
$registry = new CollectorRegistry(new InMemory());

function add_metric($anchor, $method, $path, $type = null) {

	global $registry;
	// We keep track of which routes we're interested in by storing
	// a JSON-encoded list into the "prometheus_metrics" key,
	// with each item being a JSON-encoded associative array
	// in the form: {'path': <route>, 'query_string': <query_string>}.
	$metrics = get_cache_value("prometheus_metrics");
	$metrics = $metrics ? json_decode($metrics) : array();

	$key = "$path:$type";

	// If the current request $path isn't yet in $metrics create
	// a new assoc array for it and push it into $metrics.
	if (!in_array($key, $metrics)) {
		$data = array(
			'anchor' => $anchor,
			'method' => $method,
			'path' => $path,
			'type' => $type
		);
		array_push($metrics, json_encode($data));
	}

	// Cache-wise, we also store the count values of each route
	// through the "prometheus:<route>" key. Grab the cache value
	// representing the current $path we're on (defaulted to 1).
	$count = get_cache_value("prometheus:$key");
	$count = $count ? $count + 1 : 1;

	$labels = ["method", "route"];
	if ($type)
		array_push($labels, "type");

	$gauge = $registry->getOrRegisterGauge(
		'aurweb',
		$anchor,
		'A metric count for the aurweb platform.',
		$labels
	);

	$label_values = [$data['method'], $data['path']];
	if ($type)
		array_push($label_values, $type);

	$gauge->set($count, $label_values);

	// Update cache values.
	set_cache_value("prometheus:$key", $count, 0);
	set_cache_value("prometheus_metrics", json_encode($metrics), 0);

}

function render_metrics() {
	if (!defined('EXTENSION_LOADED_APC') && !defined('EXTENSION_LOADED_MEMCACHE')) {
		error_log("The /metrics route requires a valid 'options.cache' "
			. "configuration; no cache is configured.");
		return http_response_code(417); // EXPECTATION_FAILED
	}

	global $registry;

	// First, we grab the set of metrics we're interested in in the
	// form of a cached JSON list, if we can.
	$metrics = get_cache_value("prometheus_metrics");
	if (!$metrics)
		$metrics = array();
	else
		$metrics = json_decode($metrics);

	// Now, we walk through each of those list values one by one,
	// which happen to be JSON-serialized associative arrays,
	// and process each metric via its associative array's contents:
	// The route path and the query string.
	// See web/html/index.php for the creation of such metrics.
	foreach ($metrics as $metric) {
		$data = json_decode($metric, true);

		$anchor = $data['anchor'];
		$path = $data['path'];
		$type = $data['type'];
		$key = "$path:$type";

		$labels = ["method", "route"];
		if ($type)
			array_push($labels, "type");

		$count = get_cache_value("prometheus:$key");
		$gauge = $registry->getOrRegisterGauge(
			'aurweb',
			$anchor,
			'A metric count for the aurweb platform.',
			$labels
		);

		$label_values = [$data['method'], $data['path']];
		if ($type)
			array_push($label_values, $type);

		$gauge->set($count, $label_values);
	}

	// Construct the results from RenderTextFormat renderer and
	// registry's samples.
	$renderer = new RenderTextFormat();
	$result = $renderer->render($registry->getMetricFamilySamples());

	// Output the results with the right content type header.
	http_response_code(200); // OK
	header('Content-Type: ' . RenderTextFormat::MIME_TYPE);
	echo $result;
}

?>