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