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