1 /*
2  * Copyright (c) 2024 Nordic Semiconductor
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(main, LOG_LEVEL_DBG);
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/net/tls_credentials.h>
12 #include <zephyr/net/http/server.h>
13 #include <zephyr/net/http/service.h>
14 #include <zephyr/net/net_if.h>
15 #include <zephyr/net/net_ip.h>
16 
17 #include <zephyr/net/prometheus/formatter.h>
18 #include <zephyr/net/prometheus/collector.h>
19 #include <zephyr/net/prometheus/counter.h>
20 #include <zephyr/net/prometheus/gauge.h>
21 #include <zephyr/net/prometheus/histogram.h>
22 #include <zephyr/net/prometheus/summary.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 extern struct http_service_desc test_http_service;
29 
30 static struct prometheus_counter *http_request_counter;
31 static struct prometheus_collector *stats_collector;
32 static struct prometheus_collector_walk_context walk_ctx;
33 
stats_handler(struct http_client_ctx * client,enum http_data_status status,const struct http_request_ctx * request_ctx,struct http_response_ctx * response_ctx,void * user_data)34 static int stats_handler(struct http_client_ctx *client, enum http_data_status status,
35 			 const struct http_request_ctx *request_ctx,
36 			 struct http_response_ctx *response_ctx, void *user_data)
37 {
38 	int ret;
39 	static uint8_t prom_buffer[1024];
40 
41 	if (status == HTTP_SERVER_DATA_FINAL) {
42 
43 		/* incrase counter per request */
44 		prometheus_counter_inc(http_request_counter);
45 
46 		(void)memset(prom_buffer, 0, sizeof(prom_buffer));
47 
48 		ret = prometheus_collector_walk_metrics(user_data,
49 							prom_buffer,
50 							sizeof(prom_buffer));
51 		if (ret < 0 && ret != -EAGAIN) {
52 			LOG_ERR("Cannot format exposition data (%d)", ret);
53 			return ret;
54 		}
55 
56 		response_ctx->body = prom_buffer;
57 		response_ctx->body_len = strlen(prom_buffer);
58 
59 		if (ret == 0) {
60 			response_ctx->final_chunk = true;
61 			ret = prometheus_collector_walk_init(&walk_ctx, stats_collector);
62 			if (ret < 0) {
63 				LOG_ERR("Cannot initialize walk context (%d)", ret);
64 			}
65 		}
66 	}
67 
68 	return 0;
69 }
70 
71 struct http_resource_detail_dynamic stats_resource_detail = {
72 	.common = {
73 			.type = HTTP_RESOURCE_TYPE_DYNAMIC,
74 			.bitmask_of_supported_http_methods = BIT(HTTP_GET),
75 			.content_type = "text/plain",
76 	},
77 	.cb = stats_handler,
78 	.user_data = &walk_ctx,
79 };
80 
81 HTTP_RESOURCE_DEFINE(stats_resource, test_http_service, "/statistics", &stats_resource_detail);
82 
init_stats(struct prometheus_counter * counter)83 int init_stats(struct prometheus_counter *counter)
84 {
85 	/* Use a collector from default network interface */
86 	stats_collector = net_if_get_default()->collector;
87 	if (stats_collector == NULL) {
88 		LOG_ERR("Cannot get collector from default network interface");
89 		return -EINVAL;
90 	}
91 
92 	(void)prometheus_collector_walk_init(&walk_ctx, stats_collector);
93 
94 	http_request_counter = counter;
95 
96 	return 0;
97 }
98