1 /*
2  * Copyright (c) 2024 Arif Balik <arifbalik@outlook.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(log_backend_mqtt, CONFIG_LOG_DEFAULT_LEVEL);
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/logging/log_backend.h>
12 #include <zephyr/logging/log_ctrl.h>
13 #include <zephyr/logging/log_backend_std.h>
14 #include <zephyr/logging/log_backend_mqtt.h>
15 #include <zephyr/logging/log_core.h>
16 #include <zephyr/logging/log_output.h>
17 #include <zephyr/net/mqtt.h>
18 #include <zephyr/sys/util.h>
19 #include <zephyr/random/random.h>
20 
21 #include <string.h>
22 #include <errno.h>
23 
24 static bool panic;
25 static const char *mqtt_topic = CONFIG_LOG_BACKEND_MQTT_TOPIC_DEFAULT;
26 static uint8_t log_buf[CONFIG_LOG_BACKEND_MQTT_MAX_MSG_SIZE];
27 static uint32_t log_format_current = CONFIG_LOG_BACKEND_MQTT_OUTPUT_DEFAULT;
28 
log_output_func(uint8_t * data,size_t length,void * output_ctx)29 static int log_output_func(uint8_t *data, size_t length, void *output_ctx)
30 {
31 	__ASSERT(output_ctx != NULL, "Output context must not be NULL");
32 
33 	struct mqtt_client *client = (struct mqtt_client *)output_ctx;
34 	struct mqtt_publish_param param = {0};
35 
36 	param.message.topic.topic.utf8 = (const uint8_t *)mqtt_topic;
37 	param.message.topic.topic.size = strlen(mqtt_topic);
38 	param.message.topic.qos = CONFIG_LOG_BACKEND_MQTT_QOS;
39 	param.message.payload.data = data;
40 	param.message.payload.len = length;
41 	param.retain_flag = IS_ENABLED(CONFIG_LOG_BACKEND_MQTT_RETAIN);
42 
43 #if (CONFIG_LOG_BACKEND_MQTT_QOS > MQTT_QOS_0_AT_MOST_ONCE)
44 	param.message_id = sys_rand32_get();
45 #endif
46 
47 	int ret = mqtt_publish(client, &param);
48 
49 	if (ret != 0) {
50 		return ret;
51 	}
52 
53 	return length;
54 }
55 
56 LOG_OUTPUT_DEFINE(log_output_mqtt, log_output_func, log_buf, sizeof(log_buf));
57 
mqtt_backend_process(const struct log_backend * const backend,union log_msg_generic * msg)58 static void mqtt_backend_process(const struct log_backend *const backend,
59 				 union log_msg_generic *msg)
60 {
61 	if (panic) {
62 		return;
63 	}
64 
65 	uint32_t flags = log_backend_std_get_flags();
66 
67 	log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
68 
69 	log_output_ctx_set(&log_output_mqtt, backend->cb->ctx);
70 
71 	log_output_func(&log_output_mqtt, &msg->log, flags);
72 }
73 
mqtt_backend_format_set(const struct log_backend * const backend,uint32_t log_type)74 static int mqtt_backend_format_set(const struct log_backend *const backend, uint32_t log_type)
75 {
76 	ARG_UNUSED(backend);
77 	log_format_current = log_type;
78 	return 0;
79 }
80 
mqtt_backend_panic(const struct log_backend * const backend)81 static void mqtt_backend_panic(const struct log_backend *const backend)
82 {
83 	ARG_UNUSED(backend);
84 	panic = true;
85 }
86 
87 const struct log_backend_api log_backend_mqtt_api = {
88 	.process = mqtt_backend_process,
89 	.format_set = mqtt_backend_format_set,
90 	.panic = mqtt_backend_panic,
91 };
92 
93 LOG_BACKEND_DEFINE(log_backend_mqtt, log_backend_mqtt_api, false);
94 
log_backend_mqtt_client_set(struct mqtt_client * client)95 int log_backend_mqtt_client_set(struct mqtt_client *client)
96 {
97 	log_backend_disable(&log_backend_mqtt);
98 
99 	if (client != NULL) {
100 		log_backend_enable(&log_backend_mqtt, (void *)client, CONFIG_LOG_MAX_LEVEL);
101 	}
102 
103 	return 0;
104 }
105 
log_backend_mqtt_topic_set(const char * topic)106 int log_backend_mqtt_topic_set(const char *topic)
107 {
108 	if (topic == NULL || strlen(topic) == 0) {
109 		return -EINVAL;
110 	}
111 
112 	mqtt_topic = topic;
113 
114 	return 0;
115 }
116