66 * Change Logs:
77 * Date Author Notes
88 * 2018-08-25 armink the first version
9+ * 2025-10-30 wdfk-prog add emergency log flush mechanism
910 */
1011
1112#include <stdarg.h>
@@ -501,7 +502,23 @@ rt_weak rt_size_t ulog_hex_formater(char *log_buf, const char *tag, const rt_uin
501502 return ulog_tail_formater (log_buf , log_len , RT_TRUE , LOG_LVL_DBG );
502503}
503504
504- static void ulog_output_to_all_backend (rt_uint32_t level , const char * tag , rt_bool_t is_raw , const char * log , rt_size_t len )
505+ /**
506+ * @brief Internal helper to broadcast a log to backends.
507+ *
508+ * @details This is the core broadcasting function. It iterates through all
509+ * registered backends and outputs the log message. It can operate
510+ * in two modes.
511+ *
512+ * @param emergency_only If RT_TRUE, it will only output to backends marked as
513+ * `is_emergency_backend`. If RT_FALSE, it will output to
514+ * all backends that match the filter criteria.
515+ * @param level The log level.
516+ * @param tag The log tag.
517+ * @param is_raw Whether the log is raw data.
518+ * @param log The log message buffer.
519+ * @param len The length of the log message.
520+ */
521+ static void _ulog_output_to_backends (rt_bool_t emergency_only , rt_uint32_t level , const char * tag , rt_bool_t is_raw , const char * log , rt_size_t len )
505522{
506523 rt_slist_t * node ;
507524 ulog_backend_t backend ;
@@ -520,6 +537,13 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
520537 for (node = rt_slist_first (& ulog .backend_list ); node ; node = rt_slist_next (node ))
521538 {
522539 backend = rt_slist_entry (node , struct ulog_backend , list );
540+
541+ if (emergency_only && !backend -> is_emergency_backend )
542+ {
543+ /* In emergency mode, skip any backend not marked as emergency-safe. */
544+ continue ;
545+ }
546+
523547 if (backend -> out_level < level )
524548 {
525549 continue ;
@@ -558,6 +582,11 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
558582 }
559583}
560584
585+ static void ulog_output_to_all_backend (rt_uint32_t level , const char * tag , rt_bool_t is_raw , const char * log , rt_size_t len )
586+ {
587+ _ulog_output_to_backends (RT_FALSE , level , tag , is_raw , log , len );
588+ }
589+
561590static void do_output (rt_uint32_t level , const char * tag , rt_bool_t is_raw , const char * log_buf , rt_size_t log_len )
562591{
563592#ifdef ULOG_USING_ASYNC_OUTPUT
@@ -1297,6 +1326,7 @@ rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool
12971326 backend -> support_color = support_color ;
12981327 backend -> out_level = LOG_FILTER_LVL_ALL ;
12991328 rt_strncpy (backend -> name , name , RT_NAME_MAX - 1 );
1329+ backend -> is_emergency_backend = RT_FALSE ;
13001330
13011331 level = rt_spin_lock_irqsave (& _spinlock );
13021332 rt_slist_append (& ulog .backend_list , & backend -> list );
@@ -1366,13 +1396,54 @@ ulog_backend_t ulog_backend_find(const char *name)
13661396 return RT_NULL ;
13671397}
13681398
1399+ /**
1400+ * @brief Sets the emergency-safe status for a specific backend at runtime.
1401+ *
1402+ * @details This function allows an application to dynamically mark a backend
1403+ * as safe or unsafe for use in emergency contexts (e.g., fault handlers).
1404+ * Only backends marked as safe will be used by `ulog_emergency_flush`.
1405+ *
1406+ * @param[in] name The name of the target backend.
1407+ * @param[in] is_emergency RT_TRUE to mark the backend as safe for emergency calls,
1408+ * RT_FALSE otherwise.
1409+ *
1410+ * @return rt_err_t RT_EOK on success, -RT_ERROR if the backend with the given name
1411+ * is not found, or -RT_EINVAL if the name is invalid.
1412+ */
1413+ rt_err_t ulog_backend_set_emergency (const char * name , rt_bool_t is_emergency )
1414+ {
1415+ ulog_backend_t backend ;
1416+ rt_err_t result = - RT_ERROR ;
1417+
1418+ if (name == RT_NULL )
1419+ {
1420+ return - RT_EINVAL ;
1421+ }
1422+
1423+ rt_mutex_take (& ulog .output_locker , RT_WAITING_FOREVER );
1424+
1425+ backend = ulog_backend_find (name );
1426+ if (backend != RT_NULL )
1427+ {
1428+ backend -> is_emergency_backend = is_emergency ;
1429+ result = RT_EOK ;
1430+ }
1431+
1432+ rt_mutex_release (& ulog .output_locker );
1433+
1434+ return result ;
1435+ }
1436+
13691437#ifdef ULOG_USING_ASYNC_OUTPUT
13701438/**
13711439 * asynchronous output logs to all backends
13721440 *
1441+ * @param[in] emergency_mode A flag to select the operational mode. RT_FALSE for
1442+ * normal operation, RT_TRUE for emergency fault context.
1443+ *
13731444 * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
13741445 */
1375- void ulog_async_output (void )
1446+ void ulog_async_output (rt_bool_t emergency_mode )
13761447{
13771448 rt_rbb_blk_t log_blk ;
13781449 ulog_frame_t log_frame ;
@@ -1387,14 +1458,12 @@ void ulog_async_output(void)
13871458 log_frame = (ulog_frame_t ) log_blk -> buf ;
13881459 if (log_frame -> magic == ULOG_FRAME_MAGIC )
13891460 {
1390- /* output to all backends */
1391- ulog_output_to_all_backend (log_frame -> level , log_frame -> tag , log_frame -> is_raw , log_frame -> log ,
1392- log_frame -> log_len );
1461+ _ulog_output_to_backends (emergency_mode , log_frame -> level , log_frame -> tag , log_frame -> is_raw , log_frame -> log , log_frame -> log_len );
13931462 }
13941463 rt_rbb_blk_free (ulog .async_rbb , log_blk );
13951464 }
13961465 /* output the log_raw format log */
1397- if (ulog .async_rb )
1466+ if (ulog .async_rb && ! emergency_mode )
13981467 {
13991468 rt_size_t log_len = rt_ringbuffer_data_len (ulog .async_rb );
14001469 char * log = rt_malloc (log_len + 1 );
@@ -1434,14 +1503,14 @@ rt_err_t ulog_async_waiting_log(rt_int32_t time)
14341503
14351504static void async_output_thread_entry (void * param )
14361505{
1437- ulog_async_output ();
1506+ ulog_async_output (RT_FALSE );
14381507
14391508 while (1 )
14401509 {
14411510 ulog_async_waiting_log (RT_WAITING_FOREVER );
14421511 while (1 )
14431512 {
1444- ulog_async_output ();
1513+ ulog_async_output (RT_FALSE );
14451514 /* If there is no log output for a certain period of time,
14461515 * refresh the log buffer
14471516 */
@@ -1471,7 +1540,7 @@ void ulog_flush(void)
14711540 return ;
14721541
14731542#ifdef ULOG_USING_ASYNC_OUTPUT
1474- ulog_async_output ();
1543+ ulog_async_output (RT_FALSE );
14751544#endif
14761545
14771546 /* flush all backends */
@@ -1485,6 +1554,36 @@ void ulog_flush(void)
14851554 }
14861555}
14871556
1557+ /**
1558+ * @brief Flushes pending logs and outputs to emergency-safe backends.
1559+ */
1560+ void ulog_emergency_flush (void )
1561+ {
1562+ rt_slist_t * node ;
1563+ ulog_backend_t backend ;
1564+ rt_base_t irq_flag ;
1565+
1566+ if (!ulog .init_ok )
1567+ return ;
1568+
1569+ irq_flag = rt_hw_interrupt_disable ();
1570+
1571+ #ifdef ULOG_USING_ASYNC_OUTPUT
1572+ ulog_async_output (RT_TRUE );
1573+ #endif
1574+
1575+ rt_slist_for_each (node , & ulog .backend_list )
1576+ {
1577+ backend = rt_slist_entry (node , struct ulog_backend , list );
1578+ if (backend -> is_emergency_backend && backend -> flush )
1579+ {
1580+ backend -> flush (backend );
1581+ }
1582+ }
1583+
1584+ rt_hw_interrupt_enable (irq_flag );
1585+ }
1586+
14881587/**
14891588 * @brief ulog initialization
14901589 *
0 commit comments