nros C API
Lightweight ROS 2 client for embedded real-time systems
Loading...
Searching...
No Matches
log.h
Go to the documentation of this file.
1
26#ifndef NROS_LOG_H
27#define NROS_LOG_H
28
29#include <stdarg.h>
30#include <stdbool.h>
31#include <stddef.h>
32#include <stdint.h>
33
34#ifdef __cplusplus
35extern "C" {
36#endif
37
87typedef enum rcl_log_severity_t {
95 /* NOT a level — a WIDTH PIN (issue 1330). Every ARM EABI compiler defaults
96 * to `-fshort-enums` (it is the AAPCS default, not a flag this tree picks),
97 * so an enum whose largest value is 50 is packed to ONE byte there. That
98 * contradicts both the Rust mirror (`repr(transparent)` over `c_int`) and
99 * this enum's own promise above that any `int` a C caller can produce is
100 * representable — and it turned the `_Static_assert` below into a hard
101 * build error for every cross C/C++ image the day the guard landed.
102 * `-fno-short-enums` is not the fix: it would have to reach every consumer
103 * in and out of tree, and a TU that forgot would disagree about the width
104 * of a type in a shared header, silently. The pin belongs in the header.
105 *
106 * Trailing underscore: reserved, never emitted, never switched on.
107 * `to_facade` is TOTAL, so a caller that somehow passed it resolves to
108 * FATAL like any other out-of-range `int` rather than being rejected. */
111
112/* ── Width guard ──────────────────────────────────────────────────────────────
113 * This `enum` is HAND-MIRRORED in Rust (`packages/api/nros-c/src/log.rs`), which
114 * is the class `check-ffi-struct-mirrors` polices one layer over. The mirror is
115 * `#[repr(transparent)]` over `c_int`; it was `#[repr(u8)]` until phase-417
116 * stage 3, so one side passed a byte where the other passed four and the values
117 * survived only by how the ABIs in play happen to pass small integers. Nothing
118 * asserted the width anywhere in `include/` or `src/`. It does now. */
119#if defined(__cplusplus) && __cplusplus >= 201103L
120static_assert(sizeof(rcl_log_severity_t) == sizeof(int),
121 "rcl_log_severity_t must be int-sized: the Rust mirror is repr(transparent) "
122 "over c_int");
123static_assert(RCLC_LOG_SEVERITY_INFO == 20, "rcl_log_severity_t must stay on rcutils's numbering");
124static_assert(RCLC_LOG_SEVERITY_FATAL == 50, "rcl_log_severity_t must stay on rcutils's numbering");
125#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
126_Static_assert(sizeof(rcl_log_severity_t) == sizeof(int),
127 "rcl_log_severity_t must be int-sized: the Rust mirror is repr(transparent) "
128 "over c_int");
129_Static_assert(RCLC_LOG_SEVERITY_INFO == 20, "rcl_log_severity_t must stay on rcutils's numbering");
130_Static_assert(RCLC_LOG_SEVERITY_FATAL == 50,
131 "rcl_log_severity_t must stay on rcutils's numbering");
132#endif /* static_assert */
133
138typedef const void* rclc_logger_t;
139
154void rclc_log_emit(rclc_logger_t logger, rcl_log_severity_t severity, const char* message,
155 size_t message_len);
156
162void rclc_log_emit_fmt(rclc_logger_t logger, rcl_log_severity_t severity, const char* fmt, ...)
163 __attribute__((format(printf, 3, 4)));
164
181void rclc_log_emit_at(rclc_logger_t logger, rcl_log_severity_t severity, const char* message,
182 size_t message_len, const char* file, uint32_t line);
183
191#define NROS_LOG_FMT_BUFFER_SIZE 256
192
203void rclc_log_emit_fmt_at(rclc_logger_t logger, rcl_log_severity_t severity, const char* file,
204 uint32_t line, const char* fmt, ...)
205 __attribute__((format(printf, 5, 6)));
206
207/* ---- Convenience macros ---- */
208/* The macros stage the printf args into a stack buffer inside
209 * `rclc_log_emit_fmt_at`, which attaches `__FILE__` / `__LINE__`.
210 * Filtering happens on the Rust side: the image's compile-time CEILING
211 * (`NROS_LOG_MAX_LEVEL`, issue 1037 — `trace` unless stated) and then the
212 * per-logger threshold (`rclc_logger_set_level`), both inside the same check
213 * `rclc_logger_is_enabled` answers. The call site still formats; guard it with
214 * `rclc_logger_is_enabled` if the arguments are expensive. */
215
216#define RCLC_LOG_TRACE(logger, ...) \
217 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_TRACE, __FILE__, (uint32_t)__LINE__, \
218 __VA_ARGS__)
219#define RCLC_LOG_DEBUG(logger, ...) \
220 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_DEBUG, __FILE__, (uint32_t)__LINE__, \
221 __VA_ARGS__)
222#define RCLC_LOG_INFO(logger, ...) \
223 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_INFO, __FILE__, (uint32_t)__LINE__, \
224 __VA_ARGS__)
225#define RCLC_LOG_WARN(logger, ...) \
226 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_WARN, __FILE__, (uint32_t)__LINE__, \
227 __VA_ARGS__)
228#define RCLC_LOG_ERROR(logger, ...) \
229 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_ERROR, __FILE__, (uint32_t)__LINE__, \
230 __VA_ARGS__)
231#define RCLC_LOG_FATAL(logger, ...) \
232 rclc_log_emit_fmt_at((logger), RCLC_LOG_SEVERITY_FATAL, __FILE__, (uint32_t)__LINE__, \
233 __VA_ARGS__)
234
249void rclc_log_init(void);
250
261
262/* -------------------------------------------------------------------------
263 * phase-417 W4.d — named loggers, per-logger levels, sinks, throttle.
264 *
265 * Every entry point below is a thin forwarder onto `nros_log` (RFC-0019: the
266 * Rust API is the implementation, C delegates). No level table, no throttle
267 * arithmetic and no sink list lives on this side.
268 * ------------------------------------------------------------------------- */
269
289
301size_t rclc_logger_get_name(rclc_logger_t logger, char* buf, size_t buf_len);
302
314
334
347
365
372
381
398typedef void (*rclc_log_sink_fn)(void* user_data, rcl_log_severity_t severity,
399 const char* logger_name, size_t logger_name_len,
400 const char* message, size_t message_len, const char* file,
401 size_t file_len, uint32_t line, uint64_t timestamp_ns);
402
422bool rclc_log_add_sink(rclc_log_sink_fn sink, void* user_data);
423
434
462bool rclc_log_throttle_admit(uint64_t* last_ns, uint64_t interval_ms);
463
464/* ---- Throttled convenience macros ---- */
465/* One `static uint64_t` per call site, which is what makes the window
466 * per-SITE and not per-logger — the same shape as `rclcpp`'s
467 * `RCLCPP_INFO_THROTTLE`. Statements, not expressions (they declare storage),
468 * so they need their own `;` and cannot be used inside a comma expression.
469 *
470 * Severity is tested BEFORE the window: a record the logger's level already
471 * filters must not consume the window, or a raised-then-lowered log level
472 * loses its first record for a whole interval. */
473
474#define RCLC_LOG_THROTTLE_AT(logger, severity, interval_ms, ...) \
475 do { \
476 static uint64_t _nros_log_throttle_last = 0; \
477 if (rclc_logger_is_enabled((logger), (severity)) && \
478 rclc_log_throttle_admit(&_nros_log_throttle_last, (uint64_t)(interval_ms))) { \
479 rclc_log_emit_fmt_at((logger), (severity), __FILE__, (uint32_t)__LINE__, __VA_ARGS__); \
480 } \
481 } while (0)
482
483#define RCLC_LOG_TRACE_THROTTLE(logger, interval_ms, ...) \
484 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_TRACE, (interval_ms), __VA_ARGS__)
485#define RCLC_LOG_DEBUG_THROTTLE(logger, interval_ms, ...) \
486 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_DEBUG, (interval_ms), __VA_ARGS__)
487#define RCLC_LOG_INFO_THROTTLE(logger, interval_ms, ...) \
488 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_INFO, (interval_ms), __VA_ARGS__)
489#define RCLC_LOG_WARN_THROTTLE(logger, interval_ms, ...) \
490 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_WARN, (interval_ms), __VA_ARGS__)
491#define RCLC_LOG_ERROR_THROTTLE(logger, interval_ms, ...) \
492 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_ERROR, (interval_ms), __VA_ARGS__)
493#define RCLC_LOG_FATAL_THROTTLE(logger, interval_ms, ...) \
494 RCLC_LOG_THROTTLE_AT((logger), RCLC_LOG_SEVERITY_FATAL, (interval_ms), __VA_ARGS__)
495
496#ifdef __cplusplus
497} /* extern "C" */
498#endif
499
500#endif /* NROS_LOG_H */
rclc_logger_t rclc_logger_get_child(rclc_logger_t logger, const char *suffix)
void rclc_log_init(void)
rcl_log_severity_t rclc_log_get_default_level(void)
void rclc_log_set_default_level(rcl_log_severity_t severity)
void rclc_log_emit_fmt(rclc_logger_t logger, rcl_log_severity_t severity, const char *fmt,...) __attribute__((format(printf
rcl_log_severity_t rclc_logger_get_level(rclc_logger_t logger)
void void rclc_log_emit_at(rclc_logger_t logger, rcl_log_severity_t severity, const char *message, size_t message_len, const char *file, uint32_t line)
void rclc_log_emit(rclc_logger_t logger, rcl_log_severity_t severity, const char *message, size_t message_len)
void(* rclc_log_sink_fn)(void *user_data, rcl_log_severity_t severity, const char *logger_name, size_t logger_name_len, const char *message, size_t message_len, const char *file, size_t file_len, uint32_t line, uint64_t timestamp_ns)
Definition log.h:398
size_t rclc_logger_get_name(rclc_logger_t logger, char *buf, size_t buf_len)
const void * rclc_logger_t
Definition log.h:138
bool rclc_log_timestamp_available(void)
bool rclc_log_add_sink(rclc_log_sink_fn sink, void *user_data)
rclc_logger_t rclc_log_default_logger(void)
rcl_log_severity_t
Definition log.h:87
@ RCLC_LOG_SEVERITY_FORCE_INT_WIDTH_
Definition log.h:109
@ RCLC_LOG_SEVERITY_FATAL
Definition log.h:94
@ RCLC_LOG_SEVERITY_ERROR
Definition log.h:93
@ RCLC_LOG_SEVERITY_DEBUG
Definition log.h:90
@ RCLC_LOG_SEVERITY_TRACE
Definition log.h:89
@ RCLC_LOG_SEVERITY_UNSET
Definition log.h:88
@ RCLC_LOG_SEVERITY_WARN
Definition log.h:92
@ RCLC_LOG_SEVERITY_INFO
Definition log.h:91
bool rclc_logger_set_level(rclc_logger_t logger, rcl_log_severity_t severity)
bool rclc_logger_is_enabled(rclc_logger_t logger, rcl_log_severity_t severity)
bool rclc_log_throttle_admit(uint64_t *last_ns, uint64_t interval_ms)
void rclc_log_emit_fmt_at(rclc_logger_t logger, rcl_log_severity_t severity, const char *file, uint32_t line, const char *fmt,...) __attribute__((format(printf
rclc_logger_t rclc_log_get_logger(const char *name)