Skip to content

Commit 7761b16

Browse files
Damian-Nordicnordicjm
authored andcommitted
logging: rpc: log history support
Add initial code for supporting the log history, that is, the mode of nRF RPC logging library in which log messages are stored in a ring buffer, and nRF RPC commands can be used for configuring and fetching the log history. The rationale for supporting this mode is to reduce the nRF RPC traffic so that the bandwidth is available for higher priority tasks, while preserving the possibility to analyse the logs when an issue occurs. This commit adds the support for saving the log messages in the ring buffer, fetching the log history, and setting the maximum level of saved messages. Signed-off-by: Damian Krolik <[email protected]>
1 parent 5e4a92b commit 7761b16

File tree

10 files changed

+485
-6
lines changed

10 files changed

+485
-6
lines changed

include/logging/log_rpc.h

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,26 @@ enum log_rpc_level {
3232
};
3333

3434
/**
35-
* Sets the logging stream verbosity level.
35+
* Log history handler.
3636
*
37-
* Configures the remote device to only stream log messages whose level is less
38-
* than or equal to the specified level.
37+
* The type of a callback function that is invoked for each received log message
38+
* while fetching the log history from the remote device.
39+
*
40+
* @param level The message level, see @c log_rpc_level.
41+
* @param msg A pointer to the message payload.
42+
* @param msg_len The message payload length.
43+
*/
44+
typedef void (*log_rpc_history_handler_t)(enum log_rpc_level level, const char *msg,
45+
size_t msg_len);
46+
47+
/**
48+
* Sets the log streaming verbosity level.
49+
*
50+
* The log streaming is the feature of nRF RPC logging that allows receiving log
51+
* messages as they are generated by the application running on the remote device.
52+
*
53+
* This function issues an nRF RPC command that configures the remote device to
54+
* stream log messages whose level is less than or equal to the specified level.
3955
*
4056
* @param level Logging level, see @c log_rpc_level.
4157
*
@@ -44,6 +60,42 @@ enum log_rpc_level {
4460
*/
4561
int log_rpc_set_stream_level(enum log_rpc_level level);
4662

63+
/**
64+
* Sets the log history verbosity level.
65+
*
66+
* The log history is the feature of nRF RPC logging that allows saving log
67+
* messages generated by the application running on the remote device into
68+
* a ring buffer, and then retrieving the log history when needed.
69+
*
70+
* The function issues an nRF RPC command that configures the remote device to
71+
* save log messages whose level is less than or equal to the specified level.
72+
*
73+
* @param level Logging level, see @c log_rpc_level.
74+
*
75+
* @retval 0 On success.
76+
* @retval -errno On failure.
77+
*/
78+
int log_rpc_set_history_level(enum log_rpc_level level);
79+
80+
/**
81+
* Fetches the log history.
82+
*
83+
* The function issues an nRF RPC command that starts the log history transfer
84+
* from the remote device. The @c handler callback function is invoked for each
85+
* received log message. Additionally, it is invoked with @c msg argument set to
86+
* NULL after all log messages have been received.
87+
*
88+
* @note If the function is called while the previous log history transfer is
89+
* still is in progress, the process is restarted from the current oldest
90+
* log message saved in the log history on the remote device.
91+
*
92+
* @param handler History handler, see @c log_rpc_history_handler_t.
93+
*
94+
* @retval 0 On success.
95+
* @retval -errno On failure.
96+
*/
97+
int log_rpc_fetch_history(log_rpc_history_handler_t handler);
98+
4799
/**
48100
* @brief Retrieves the crash log retained on the remote device.
49101
*

samples/nrf_rpc/protocols_serialization/client/src/log_rpc_shell.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,72 @@ static int cmd_log_rpc_stream_level(const struct shell *sh, size_t argc, char *a
3333
return 0;
3434
}
3535

36+
static int cmd_log_rpc_history_level(const struct shell *sh, size_t argc, char *argv[])
37+
{
38+
int rc = 0;
39+
enum log_rpc_level level;
40+
41+
level = (enum log_rpc_level)shell_strtol(argv[1], 10, &rc);
42+
43+
if (rc) {
44+
shell_error(sh, "Invalid argument: %d", rc);
45+
return -EINVAL;
46+
}
47+
48+
rc = log_rpc_set_history_level(level);
49+
50+
if (rc) {
51+
shell_error(sh, "Error: %d", rc);
52+
return -ENOEXEC;
53+
}
54+
55+
return 0;
56+
}
57+
58+
static const struct shell *shell;
59+
60+
static void history_handler(enum log_rpc_level level, const char *msg, size_t msg_len)
61+
{
62+
enum shell_vt100_color color;
63+
64+
if (!msg) {
65+
shell_print(shell, "Done");
66+
return;
67+
}
68+
69+
switch (level) {
70+
case LOG_RPC_LEVEL_INF:
71+
color = SHELL_INFO;
72+
break;
73+
case LOG_RPC_LEVEL_WRN:
74+
color = SHELL_WARNING;
75+
break;
76+
case LOG_RPC_LEVEL_ERR:
77+
color = SHELL_ERROR;
78+
break;
79+
default:
80+
color = SHELL_NORMAL;
81+
break;
82+
}
83+
84+
shell_fprintf(shell, color, "%.*s\n", msg_len, msg);
85+
}
86+
87+
static int cmd_log_rpc_history_fetch(const struct shell *sh, size_t argc, char *argv[])
88+
{
89+
int rc = 0;
90+
91+
shell = sh;
92+
rc = log_rpc_fetch_history(history_handler);
93+
94+
if (rc) {
95+
shell_error(sh, "Error: %d", rc);
96+
return -ENOEXEC;
97+
}
98+
99+
return 0;
100+
}
101+
36102
static int cmd_log_rpc_crash(const struct shell *sh, size_t argc, char *argv[])
37103
{
38104
int rc;
@@ -60,8 +126,12 @@ static int cmd_log_rpc_crash(const struct shell *sh, size_t argc, char *argv[])
60126
}
61127

62128
SHELL_STATIC_SUBCMD_SET_CREATE(log_rpc_cmds,
63-
SHELL_CMD_ARG(stream_level, NULL, "Set stream logging level",
129+
SHELL_CMD_ARG(stream_level, NULL, "Set log streaming level",
64130
cmd_log_rpc_stream_level, 2, 0),
131+
SHELL_CMD_ARG(history_level, NULL, "Set log history level",
132+
cmd_log_rpc_history_level, 2, 0),
133+
SHELL_CMD_ARG(history_fetch, NULL, "Fetch log history",
134+
cmd_log_rpc_history_fetch, 1, 0),
65135
SHELL_CMD_ARG(crash, NULL, "Retrieve remote device crash log",
66136
cmd_log_rpc_crash, 1, 0),
67137
SHELL_SUBCMD_SET_END);

samples/nrf_rpc/protocols_serialization/server/snippets/log_rpc/log_rpc.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
CONFIG_LOG=y
88
CONFIG_LOG_BACKEND_RPC=y
99
CONFIG_LOG_BACKEND_RPC_CRASH_LOG=y
10+
CONFIG_LOG_BACKEND_RPC_HISTORY=y
1011

1112
# nRF RPC requires slightly bigger stack than default
1213
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1024

subsys/logging/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ if (CONFIG_LOG_FORWARDER_RPC OR CONFIG_LOG_BACKEND_RPC)
88
zephyr_library()
99
zephyr_library_sources_ifdef(CONFIG_LOG_FORWARDER_RPC log_forwarder_rpc.c)
1010
zephyr_library_sources_ifdef(CONFIG_LOG_BACKEND_RPC log_backend_rpc.c)
11+
zephyr_library_sources_ifdef(CONFIG_LOG_BACKEND_RPC_HISTORY_STORAGE_RAM log_backend_rpc_history_ram.c)
1112
endif()

subsys/logging/Kconfig

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,42 @@ config LOG_BACKEND_RPC_OUTPUT_BUFFER_SIZE
3838
Defines the size of stack buffer that is used by the RPC logging backend
3939
while formatting a log message.
4040

41+
config LOG_BACKEND_RPC_HISTORY
42+
bool "Log history support"
43+
help
44+
Enables the feature to continuously store logs in a large ring buffer,
45+
and adds nRF RPC commands for configuring and fetching the log history.
46+
47+
if LOG_BACKEND_RPC_HISTORY
48+
49+
choice LOG_BACKEND_RPC_HISTORY_STORAGE_CHOICE
50+
prompt "Log history storage type"
51+
default LOG_BACKEND_RPC_HISTORY_STORAGE_RAM
52+
53+
config LOG_BACKEND_RPC_HISTORY_STORAGE_RAM
54+
bool "RAM buffer"
55+
56+
endchoice # LOG_BACKEND_RPC_HISTORY_STORAGE_CHOICE
57+
58+
config LOG_BACKEND_RPC_HISTORY_UPLOAD_THREAD_STACK_SIZE
59+
int "Log history upload thread stack size"
60+
default 1024
61+
62+
config LOG_BACKEND_RPC_HISTORY_UPLOAD_CHUNK_SIZE
63+
int "Log history upload chunk size"
64+
default 1024
65+
66+
config LOG_BACKEND_RPC_HISTORY_SIZE
67+
int "Log history size"
68+
depends on LOG_BACKEND_RPC_HISTORY_STORAGE_RAM
69+
default 16384
70+
help
71+
Size of the ring buffer used to store the log history, in bytes.
72+
73+
endif # LOG_BACKEND_RPC_HISTORY
74+
4175
config LOG_BACKEND_RPC_CRASH_LOG
42-
bool "nRF RPC crash log"
76+
bool "Crash log support"
4377
select RETENTION
4478
select RETENTION_MUTEX_FORCE_DISABLE
4579
select RETAINED_MEM

subsys/logging/log_backend_rpc.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
*/
1212

1313
#include "log_rpc_group.h"
14+
#include "log_backend_rpc_history.h"
1415

1516
#include <logging/log_rpc.h>
1617
#include <nrf_rpc/nrf_rpc_serialize.h>
1718

1819
#include <nrf_rpc_cbor.h>
1920

21+
#include <zephyr/kernel.h>
2022
#include <zephyr/logging/log.h>
2123
#include <zephyr/logging/log_backend.h>
2224
#include <zephyr/logging/log_backend_std.h>
@@ -45,6 +47,18 @@ static bool panic_mode;
4547
static uint32_t log_format = LOG_OUTPUT_TEXT;
4648
static enum log_rpc_level stream_level = LOG_RPC_LEVEL_NONE;
4749

50+
#ifdef CONFIG_LOG_BACKEND_RPC_HISTORY
51+
static enum log_rpc_level history_level = LOG_RPC_LEVEL_NONE;
52+
static void history_transfer_task(struct k_work *work);
53+
static K_MUTEX_DEFINE(history_transfer_mtx);
54+
static uint32_t history_transfer_id;
55+
static union log_msg_generic *history_cur_msg;
56+
static K_WORK_DEFINE(history_transfer_work, history_transfer_task);
57+
static K_THREAD_STACK_DEFINE(history_transfer_workq_stack,
58+
CONFIG_LOG_BACKEND_RPC_HISTORY_UPLOAD_THREAD_STACK_SIZE);
59+
static struct k_work_q history_transfer_workq;
60+
#endif
61+
4862
/*
4963
* Verify that Zephyr logging level can be used as the nRF RPC logging level without translation.
5064
*/
@@ -341,6 +355,14 @@ static void process(const struct log_backend *const backend, union log_msg_gener
341355
*/
342356
stream_message(msg);
343357
}
358+
359+
#ifdef CONFIG_LOG_BACKEND_RPC_HISTORY
360+
max_level = history_level;
361+
362+
if (max_level != LOG_RPC_LEVEL_NONE && level <= max_level) {
363+
log_rpc_history_push(msg_generic);
364+
}
365+
#endif
344366
}
345367

346368
static void panic(struct log_backend const *const backend)
@@ -354,6 +376,14 @@ static void panic(struct log_backend const *const backend)
354376
static void init(struct log_backend const *const backend)
355377
{
356378
ARG_UNUSED(backend);
379+
380+
#ifdef CONFIG_LOG_BACKEND_RPC_HISTORY
381+
log_rpc_history_init();
382+
k_work_queue_init(&history_transfer_workq);
383+
k_work_queue_start(&history_transfer_workq, history_transfer_workq_stack,
384+
K_THREAD_STACK_SIZEOF(history_transfer_workq_stack),
385+
K_LOWEST_APPLICATION_THREAD_PRIO, NULL);
386+
#endif
357387
}
358388

359389
static void dropped(const struct log_backend *const backend, uint32_t cnt)
@@ -410,3 +440,114 @@ static void log_rpc_set_stream_level_handler(const struct nrf_rpc_group *group,
410440

411441
NRF_RPC_CBOR_CMD_DECODER(log_rpc_group, log_rpc_set_stream_level_handler,
412442
LOG_RPC_CMD_SET_STREAM_LEVEL, log_rpc_set_stream_level_handler, NULL);
443+
444+
#ifdef CONFIG_LOG_BACKEND_RPC_HISTORY
445+
446+
static void log_rpc_set_history_level_handler(const struct nrf_rpc_group *group,
447+
struct nrf_rpc_cbor_ctx *ctx, void *handler_data)
448+
{
449+
enum log_rpc_level level;
450+
451+
level = (enum log_rpc_level)nrf_rpc_decode_uint(ctx);
452+
453+
if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
454+
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, group, LOG_RPC_CMD_SET_HISTORY_LEVEL,
455+
NRF_RPC_PACKET_TYPE_CMD);
456+
return;
457+
}
458+
459+
history_level = level;
460+
461+
nrf_rpc_rsp_send_void(group);
462+
}
463+
464+
NRF_RPC_CBOR_CMD_DECODER(log_rpc_group, log_rpc_set_history_level_handler,
465+
LOG_RPC_CMD_SET_HISTORY_LEVEL, log_rpc_set_history_level_handler, NULL);
466+
467+
static void history_transfer_task(struct k_work *work)
468+
{
469+
const uint32_t flags = common_output_flags | LOG_OUTPUT_FLAG_CRLF_NONE;
470+
471+
struct nrf_rpc_cbor_ctx ctx;
472+
bool any_msg_consumed = false;
473+
struct log_msg *msg;
474+
size_t length;
475+
size_t max_length;
476+
477+
NRF_RPC_CBOR_ALLOC(&log_rpc_group, ctx, CONFIG_LOG_BACKEND_RPC_HISTORY_UPLOAD_CHUNK_SIZE);
478+
479+
k_mutex_lock(&history_transfer_mtx, K_FOREVER);
480+
nrf_rpc_encode_uint(&ctx, history_transfer_id);
481+
482+
while (true) {
483+
if (!history_cur_msg) {
484+
history_cur_msg = log_rpc_history_pop();
485+
}
486+
487+
if (!history_cur_msg) {
488+
break;
489+
}
490+
491+
msg = &history_cur_msg->log;
492+
length = 6 + format_message_to_buf(msg, flags, NULL, 0);
493+
max_length = ctx.zs[0].payload_end - ctx.zs[0].payload_mut;
494+
495+
/* Check if there is enough buffer space to fit in the current message. */
496+
if (length > max_length) {
497+
break;
498+
}
499+
500+
nrf_rpc_encode_uint(&ctx, log_msg_get_level(msg));
501+
502+
if (zcbor_bstr_start_encode(ctx.zs)) {
503+
max_length = ctx.zs[0].payload_end - ctx.zs[0].payload_mut;
504+
length = format_message_to_buf(msg, flags, ctx.zs[0].payload_mut,
505+
max_length);
506+
ctx.zs[0].payload_mut += MIN(length, max_length);
507+
zcbor_bstr_end_encode(ctx.zs, NULL);
508+
}
509+
510+
log_rpc_history_free(history_cur_msg);
511+
history_cur_msg = NULL;
512+
any_msg_consumed = true;
513+
}
514+
515+
/* Determine if the work should be resubmitted to continue the transfer. */
516+
if (any_msg_consumed) {
517+
k_work_submit_to_queue(&history_transfer_workq, work);
518+
} else {
519+
log_rpc_history_free(history_cur_msg);
520+
history_cur_msg = NULL;
521+
}
522+
523+
k_mutex_unlock(&history_transfer_mtx);
524+
525+
nrf_rpc_cbor_cmd_no_err(&log_rpc_group, LOG_RPC_CMD_PUT_HISTORY_CHUNK, &ctx,
526+
nrf_rpc_rsp_decode_void, NULL);
527+
}
528+
529+
static void log_rpc_fetch_history_handler(const struct nrf_rpc_group *group,
530+
struct nrf_rpc_cbor_ctx *ctx, void *handler_data)
531+
{
532+
uint32_t transfer_id;
533+
534+
transfer_id = nrf_rpc_decode_uint(ctx);
535+
536+
if (!nrf_rpc_decoding_done_and_check(group, ctx)) {
537+
nrf_rpc_err(-EBADMSG, NRF_RPC_ERR_SRC_RECV, group, LOG_RPC_CMD_FETCH_HISTORY,
538+
NRF_RPC_PACKET_TYPE_CMD);
539+
return;
540+
}
541+
542+
k_mutex_lock(&history_transfer_mtx, K_FOREVER);
543+
history_transfer_id = transfer_id;
544+
k_work_submit_to_queue(&history_transfer_workq, &history_transfer_work);
545+
k_mutex_unlock(&history_transfer_mtx);
546+
547+
nrf_rpc_rsp_send_void(group);
548+
}
549+
550+
NRF_RPC_CBOR_CMD_DECODER(log_rpc_group, log_rpc_fetch_history_handler, LOG_RPC_CMD_FETCH_HISTORY,
551+
log_rpc_fetch_history_handler, NULL);
552+
553+
#endif

0 commit comments

Comments
 (0)