Dynamically turning UART logging on and off

This issue is exposed, or exacerbated, by our ongoing project of trying to upgrade from NCS 2.3.0 (Zephyr 3.2.99) to NCS 2.6.1 (Zephyr 3.5.99).

In our product, we emit log messages over USB UART, for development purposes.

Once we launch, when the product is ready to leave the factory, we want to turn off USB UART logging.

But, should the need arises - e.g. debug of returned units - we want to be able to turn it back on.

tl;dr: We want to dynamically, during runtime, turn on/off UART logging.

Other requirements:

  • We need to control this without affecting UART input.
    • So, e.g., powering off the UART controller won't work for us.
  • We need to do this without affecting other logging backends (other than the UART logging backend).
    • So, e.g., a solution that changes the logging level to NONE globally, across all backends, won't work for us.
  • We don't want to have to configure this on each software unit individually.
    • So, e.g., a solution that requires us to set the logging level on a per-unit granularity won't work for us.
      • In other words, we want a lever that configures logging globally for the UART logging backend, but leaves other logging backends in the system as they are.

We're currently doing it in what feels like a dirty, awkward way, which is even dirtier with NCS.2.6.1/Zephyr 3.5.

We're wondering if there's a cleaner solution.

What we're doing now (NCS 2.3.1)

This is working for us with NCS 2.3.1/Zephyr 3.2:

To disable:

  • Get the pointer to the UART logging backend using log_backend_get_by_name(), specifying "log_backend_uart" as the name:
const char * backend_name =  "log_backend_uart";
const struct log_backend * backend = log_backend_get_by_name(backend_name);
  • Disable using log_backend_disable():
log_backend_disable(backend);

To re-enable:

  • Get the pointer to the UART logging backend using log_backend_get_by_name() as above.
  • Disable using log_backend_disable():
log_backend_enable(backend, NULL, LOG_LEVEL_DBG);

With NCS 2.6.1

Doing the same thing in NCS 2.6.1/Zephyr 3.5, the system crashes on attempt to re-enable.

It crashes due to the NULL being passed for the void *ctx parameter in log_backend_enable().

In Zephyr 3.2, the UART logging backend doesn't use the .cb.ctx attribute of struct log_backend, so this is fine. In Zephyr 3.5, it does use the attribute. (For more details, see file log_backend_uart.c - in particular, the invocation of LOG_BACKEND_DEFINE in the file, and functions log_backend_uart_init() and process().)

So, changing the call to this makes it work:

log_backend_enable(backend, backend->cb->ctx, LOG_LEVEL_DBG);

But this really feels like messing with details that are probably intended to be hidden from the application developer.

Is there a cleaner, API-based way to accomplish what we're trying to do?

Related