FastLED 3.10.8
Loading...
Searching...
No Matches
log.h
Go to the documentation of this file.
1#pragma once
2
4#include "fl/stl/static_assert.h" // FL_STATIC_ASSERT for the printf-form ban
5#include "fl/stl/strstream.h" // IWYU pragma: keep - Required by FL_WARN/FL_ERROR/FL_DBG macros
6#include "fl/stl/stdio.h" // IWYU pragma: keep - fl::print / fl::println for log output
7#include "fl/stl/chrono.h" // IWYU pragma: keep - Required by FL_WARN_EVERY/FL_DBG_EVERY/FL_PRINT_EVERY macros
8#include "fl/stl/compiler_control.h" // IWYU pragma: keep - FL_NO_INLINE for log_emit
9
10// =============================================================================
11// FL_LOG_*_ENABLED -> FASTLED_LOG_*_ENABLED backward compatibility
12// =============================================================================
13// Users can now use the shorter FL_LOG_*_ENABLED defines. The old
14// FASTLED_LOG_*_ENABLED names still work for backward compatibility.
15#if defined(FL_LOG_SPI_ENABLED) && !defined(FASTLED_LOG_SPI_ENABLED)
16 #define FASTLED_LOG_SPI_ENABLED
17#endif
18#if defined(FL_LOG_RMT_ENABLED) && !defined(FASTLED_LOG_RMT_ENABLED)
19 #define FASTLED_LOG_RMT_ENABLED
20#endif
21#if defined(FL_LOG_PARLIO_ENABLED) && !defined(FASTLED_LOG_PARLIO_ENABLED)
22 #define FASTLED_LOG_PARLIO_ENABLED
23#endif
24#if defined(FL_LOG_AUDIO_ENABLED) && !defined(FASTLED_LOG_AUDIO_ENABLED)
25 #define FASTLED_LOG_AUDIO_ENABLED
26#endif
27#if defined(FL_LOG_INTERRUPT_ENABLED) && !defined(FASTLED_LOG_INTERRUPT_ENABLED)
28 #define FASTLED_LOG_INTERRUPT_ENABLED
29#endif
30#if defined(FL_LOG_FLEXIO_ENABLED) && !defined(FASTLED_LOG_FLEXIO_ENABLED)
31 #define FASTLED_LOG_FLEXIO_ENABLED
32#endif
33#if defined(FL_LOG_OBJECTFLED_ENABLED) && !defined(FASTLED_LOG_OBJECTFLED_ENABLED)
34 #define FASTLED_LOG_OBJECTFLED_ENABLED
35#endif
36
37// Conditional include for async logger functions (only when logging features are enabled)
38#if defined(FASTLED_LOG_SPI_ENABLED) || defined(FASTLED_LOG_RMT_ENABLED) || defined(FASTLED_LOG_PARLIO_ENABLED) || defined(FASTLED_LOG_AUDIO_ENABLED) || defined(FASTLED_LOG_INTERRUPT_ENABLED)
39 #include "fl/log/async_logger.h" // IWYU pragma: keep - Required by FL_LOG_*_ASYNC_* macros
40#include "fl/stl/noexcept.h"
41#endif
42
43// =============================================================================
44// FL_LOG_LEVEL - compile-time log level. Each `FL_WARN(...)` call site
45// bakes a `__FILE__ + __LINE__ + message` literal into `.flash.rodata`; on
46// ESP32 that pool is ~37-58 KB (FastLED #2773, #2886, #4712).
47// =============================================================================
48//
49// Levels (higher = more output):
50//
51// * 0 (off) -> FL_ERROR/FL_WARN/FL_INFO/FL_PRINT/FL_DBG and
52// FL_WARN_LIT/FL_LOG_LIT expand to no-ops.
53// * 1 (errors) -> FL_ERROR (real failures) and FL_PRINT (output the sketch
54// asked for explicitly) fire; FL_WARN/FL_INFO/FL_DBG
55// compile out. FL_WARN_LIT/FL_LOG_LIT (one literal, no
56// stream machinery; used by the LPC845 bring-up check)
57// stay live.
58// * 2 (full) -> everything above plus FL_WARN/FL_INFO, and FL_DBG under
59// its own FASTLED_HAS_DBG gate.
60//
61// FL_ERROR/FL_WARN/FL_INFO/FL_PRINT additionally require
62// SKETCH_HAS_LARGE_MEMORY (AVR/ATtiny/LPC845 never run the full pipeline).
63//
64// Resolution order:
65//
66// 1. `FASTLED_TESTING` -> 2 (host tests need diagnostics)
67// 2. `FL_LOG_LEVEL` set by user -> that value
68// 3. `FASTLED_LOG_VERBOSITY` set (legacy knob, kept for back-compat)
69// -> 0 stays 0; any value >= 1 maps
70// to 2, because
71// `-DFASTLED_LOG_VERBOSITY=1` has
72// always meant "full logs"
73// 4. `NDEBUG` (release builds) -> 0
74// 5. otherwise (e.g. Arduino IDE) -> 1 (errors only, #4712)
75//
76// To restore full logs add `-DFL_LOG_LEVEL=2` to the build flags, or
77// `#define FL_LOG_LEVEL 2` before `#include <FastLED.h>`. The legacy
78// `-DFASTLED_LOG_VERBOSITY=1` still works. Per-channel logging defines
79// (`FASTLED_LOG_RMT_ENABLED`, ...) keep their own `#define`s.
80#ifdef FASTLED_TESTING
81 #undef FL_LOG_LEVEL
82 #define FL_LOG_LEVEL 2
83#elif !defined(FL_LOG_LEVEL)
84 #if defined(FASTLED_LOG_VERBOSITY)
85 #if FASTLED_LOG_VERBOSITY >= 1
86 #define FL_LOG_LEVEL 2
87 #else
88 #define FL_LOG_LEVEL 0
89 #endif
90 #elif defined(NDEBUG)
91 #define FL_LOG_LEVEL 0
92 #else
93 #define FL_LOG_LEVEL 1
94 #endif
95#endif
96
97// Error-level gate: the log pipeline is linked and FL_ERROR / FL_PRINT fire.
98// Callers guarding error-only helpers (one-shot flags, names used in
99// FL_ERROR messages) use this.
100#if SKETCH_HAS_LARGE_MEMORY && (FL_LOG_LEVEL >= 1)
101 #define FASTLED_LOG_RUNTIME_ENABLED 1
102#else
103 #define FASTLED_LOG_RUNTIME_ENABLED 0
104#endif
105
106// Public resolved gate for FL_WARN / FL_INFO call sites and their supporting
107// helpers. Requires the full level (2).
108#if SKETCH_HAS_LARGE_MEMORY && (FL_LOG_LEVEL >= 2)
109 #define FL_HAS_WARN 1
110#else
111 #define FL_HAS_WARN 0
112#endif
113
114// Lite gate for FL_WARN_LIT / FL_LOG_LIT - string-literal-only macros that
115// route through fl::println without the sstream / operator<< / log_emit
116// chain. These are intentionally usable on Low-memory targets (LPC845,
117// AVR, ATtiny) where the full FL_WARN pipeline is too expensive - the
118// caller pays for one `fl::println(const char*)` symbol and the literal
119// bytes, nothing more. Live at level >= 1. See FastLED #3002.
120#if FL_LOG_LEVEL >= 1
121 #define FASTLED_LOG_LITE_ENABLED 1
122#else
123 #define FASTLED_LOG_LITE_ENABLED 0
124#endif
125
126// =============================================================================
127// Forward Declarations
128// =============================================================================
129
130// Forward declare println to avoid pulling in full fl/stl/cstdio.h machinery
131// This prevents ~5KB memory bloat for simple applications
132#ifndef FL_PRINTLN_DECLARED
133#define FL_PRINTLN_DECLARED
134namespace fl {
135 void println(const char* str) FL_NO_EXCEPT;
136}
137#endif
138
139// =============================================================================
140// Debug Output Helpers
141// =============================================================================
142
143namespace fl {
144// ".build/src/fl/dbg.h" -> "src/fl/dbg.h"
145// "blah/blah/blah.h" -> "blah.h"
146const char *fastled_file_offset(const char *file) FL_NO_EXCEPT;
147} // namespace fl
148
149// =============================================================================
150// Centralised log emit (#2963 Proposal B, Option 3)
151// =============================================================================
152//
153// `fl::detail::log_emit(kind, file, line, body)` consumes the
154// user-supplied sstream (passed by rvalue reference) and **rewrites
155// it in place** to hold the full prefixed message:
156//
157// "<file>(<line>): <KIND>: <user payload>"
158//
159// then emits via `fl::println(body.c_str())`.
160//
161// Lifetime-safe variant of Path C's "Option 1" attempt. The body
162// sstream temporary lives in the **caller's** frame - its
163// lifetime extends to the end of the FL_WARN/FL_ERROR/FL_INFO
164// full expression, exactly as the OLD inline macro's temporary
165// did. So `body.c_str()` passed to `println` has the same
166// async-handler-safe lifetime as before: whatever the test
167// harness or platform println did synchronously continues to
168// work, and async deferred-flush handlers still see a live
169// pointer until the semicolon.
170//
171// The legacy macro inlined the entire
172// `<< file << "(" << line << "): KIND: " << X`
173// chain at every FL_WARN site (1,372 sites × ~30-50 B of inlined
174// prefix-format code). With this helper, the prefix-format chain
175// is compiled ONCE into libFastLED - each call site only emits
176// the `fl::sstream() << X` ctor + the user's payload chain + a
177// single `log_emit` call.
178namespace fl { namespace detail {
179enum class log_kind : fl::u8 {
180 WARN = 0,
181 ERROR = 1,
182 INFO = 2,
183};
184
186void log_emit_prefix(log_kind kind, const char* file, int line) FL_NO_EXCEPT FL_NO_INLINE;
188
189// Takes `body` by non-const lvalue reference (not `&&`) because the
190// macro's `fl::sstream() << X` expression has type `sstream&` (the
191// returned lvalue ref from chained operator<<). The underlying
192// temporary's lifetime is the FL_WARN full-expression's, so mutating
193// it through this lvalue ref is well-defined and the c_str() handed
194// to println outlives the call exactly as the OLD inline macro's
195// `(fl::sstream() << ...).c_str()` did.
196//
197// `FL_NO_INLINE` enforces the centralisation. Without it, at -O2
198// with LTO the compiler often inlines `log_emit` at every FL_WARN
199// site - defeating the whole point of moving the prefix chain
200// out-of-line. The noinline attribute is what makes this proposal
201// actually win bytes; bare centralisation alone isn't enough.
202void log_emit(log_kind kind, const char* file, int line, fl::sstream& body) FL_NO_EXCEPT FL_NO_INLINE;
203void log_emit_literal(log_kind kind, const char* file, int line,
204 const char* body) FL_NO_EXCEPT FL_NO_INLINE;
205
206// A one-argument log macro cannot distinguish `"literal"` from
207// `"prefix" << value` by argument count. log_seed makes that distinction in
208// the expression's type instead: a lone string literal remains a lightweight
209// log_literal, while a subsequent << materializes an sstream. This preserves
210// legacy stream syntax without charging literal call sites for an sstream.
211template <fl::size N>
213public:
214 explicit log_literal(const char (&text)[N]) FL_NO_EXCEPT : mText(text) {}
215
216 const char* c_str() const FL_NO_EXCEPT { return mText; }
217
218 template <typename T>
220 fl::sstream body;
221 body << mText << value;
222 return body;
223 }
224
225private:
226 const char* mText;
227};
228
229class log_seed {
230public:
231 template <fl::size N>
232 log_literal<N> operator<<(const char (&text)[N]) const FL_NO_EXCEPT {
233 return log_literal<N>(text);
234 }
235
236 template <typename T>
238 fl::sstream body;
239 body << value;
240 return body;
241 }
242};
243
244template <fl::size N>
245inline void log_emit_auto(log_kind kind, const char* file, int line,
246 const log_literal<N>& body) FL_NO_EXCEPT {
247 // Treat a one-argument literal as data, not as a printf format string.
248 // This preserves legacy FL_WARN("100% loaded") behavior while still
249 // avoiding an sstream at the call site.
250 log_emit_literal(kind, file, line, body.c_str());
251}
252
253inline void log_emit_auto(log_kind kind, const char* file, int line,
255 log_emit(kind, file, line, body);
256}
257
258inline void log_emit_auto(log_kind kind, const char* file, int line,
259 fl::sstream&& body) FL_NO_EXCEPT {
260 log_emit(kind, file, line, body);
261}
262
263template <fl::size N>
265 fl::println(body.c_str());
266}
267
269 fl::println(body.c_str());
270}
271
273 fl::println(body.c_str());
274}
275
276template <typename Logger, fl::size N>
277inline void async_log_emit_auto(Logger& logger,
278 const log_literal<N>& body) FL_NO_EXCEPT {
279 logger.push(body.c_str());
280}
281
282template <typename Logger>
283inline void async_log_emit_auto(Logger& logger,
285 logger.push(body.str());
286}
287
288template <typename Logger>
289inline void async_log_emit_auto(Logger& logger,
290 fl::sstream&& body) FL_NO_EXCEPT {
291 logger.push(body.str());
292}
293
294} } // namespace fl::detail
295
296// =============================================================================
297// Unified-dispatch helpers for FL_WARN / FL_ERROR / FL_INFO / FL_PRINT (#3272)
298// =============================================================================
299// `FL_WARN(...)` takes exactly one argument, a literal or a stream chain:
300//
301// FL_WARN("plain literal") // 1 literal -> direct format path
302// FL_WARN("foo " << x << " bar") // 1 stream expression -> sstream
303// FL_WARN("got %d items", n) // 2+ args -> compile error (#4709)
304//
305// Macro-level dispatch (rather than C++ overload resolution) is required
306// because the single-arg stream form is a chained `<<` expression - it
307// only compiles when the macro injects `fl::sstream()` as the LHS.
308// Routing through a function call would force the `<<` chain to evaluate
309// standalone, which fails (`const char*` has no `operator<<(float)`).
310//
311// The dispatch recognizes up to 16 arguments so the banned form gets a clear
312// static_assert instead of a preprocessor error. The
313// filler list `_FL_M*15, _FL_S` is positioned so the (17 - argc)-th element
314// of the merged list lands at the `NAME` slot:
315// 1 user arg -> NAME = _FL_S (lazy literal/stream dispatch)
316// 2..16 args -> NAME = _FL_M (multi -> banned printf form)
317#define _FL_VA_PICK17( \
318 _1, _2, _3, _4, _5, _6, _7, _8, \
319 _9, _10, _11, _12, _13, _14, _15, _16, \
320 NAME, ...) NAME
321
322#define _FL_VA_DISPATCH(_S, _M, ...) \
323 _FL_VA_PICK17(__VA_ARGS__, \
324 _M, _M, _M, _M, _M, _M, _M, _M, \
325 _M, _M, _M, _M, _M, _M, _M, _S)
326
327// printf-style log formatting is banned (#4709). `FL_WARN("x=%d", x)` links
328// the whole fl::printf engine (format spec parser, width, integer/float
329// formatters) into every image that logs; on Teensy 4 that code lives in
330// RAM1. Every log macro's multi-argument form therefore fails to compile,
331// including the no-op variants used on small platforms, so a sketch fails
332// the same way on every board. Use stream form: `FL_WARN("x=" << x)`, and
333// `fl::to_hex(v)` in place of `%x`.
334#define _FL_LOG_PRINTF_BANNED(...) do { \
335 FL_STATIC_ASSERT(sizeof(#__VA_ARGS__) == 0, \
336 "FastLED: printf-style log formatting is not allowed; use stream " \
337 "form, e.g. FL_WARN(\"x=\" << x). See FastLED#4709."); \
338} while(0)
339#define _FL_LOG_NOOP_S(X) do { } while(0)
340// Compile-time check for no-op log variants: one argument is accepted and
341// dropped, more than one is the banned printf form.
342#define _FL_LOG_CHECK(...) _FL_VA_DISPATCH(_FL_LOG_NOOP_S, _FL_LOG_PRINTF_BANNED, __VA_ARGS__)(__VA_ARGS__)
343
344// =============================================================================
345// Error Macros (FL_ERROR)
346// =============================================================================
347
348// Per-kind dispatch helpers - _FL_*_S = single-arg stream form; _FL_*_M = banned printf form.
349#define _FL_ERROR_S(X) ::fl::detail::log_emit_auto( \
350 ::fl::detail::log_kind::ERROR, \
351 ::fl::fastled_file_offset(__FILE__), int(__LINE__), \
352 ::fl::detail::log_seed() << X)
353#define _FL_ERROR_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
354
355#ifndef FASTLED_ERROR
356// FASTLED_ERROR: Stream-style (<<) only; printf-style is a compile error (#4709). Dispatch via
357// argument-count dispatch - see #3272.
358#define FASTLED_ERROR(...) _FL_VA_DISPATCH(_FL_ERROR_S, _FL_ERROR_M, __VA_ARGS__)(__VA_ARGS__)
359#define FASTLED_ERROR_IF(COND, ...) do { if (COND) FASTLED_ERROR(__VA_ARGS__); } while(0)
360#endif
361
362#ifndef FL_ERROR
363#if FASTLED_LOG_RUNTIME_ENABLED
364// FL_ERROR: one literal or `"foo " << x` stream expression; `"foo %d", x` is a
365// compile error (#4709).
366#define FL_ERROR(...) _FL_VA_DISPATCH(_FL_ERROR_S, _FL_ERROR_M, __VA_ARGS__)(__VA_ARGS__)
367#define FL_ERROR_IF(COND, ...) do { if (COND) FL_ERROR(__VA_ARGS__); } while(0)
368#else
369// No-op macros - memory-constrained platform or FL_LOG_LEVEL=0.
370// Args are dropped entirely (matching pre-#3272 behaviour of FL_*_F). See
371// commit notes - earlier draft used `sstream_noop()` type-check but that
372// forced evaluation of args declared only under FASTLED_LOG_*_ENABLED.
373#define FL_ERROR(...) _FL_LOG_CHECK(__VA_ARGS__)
374#define FL_ERROR_IF(COND, ...) _FL_LOG_CHECK(__VA_ARGS__)
375#endif
376#endif
377
378// =============================================================================
379// Warning Macros (FL_WARN)
380// =============================================================================
381
382// Per-kind dispatch helpers - _FL_WARN_S = single-arg stream form; _FL_WARN_M = banned printf form.
383#define _FL_WARN_S(X) ::fl::detail::log_emit_auto( \
384 ::fl::detail::log_kind::WARN, \
385 ::fl::fastled_file_offset(__FILE__), int(__LINE__), \
386 ::fl::detail::log_seed() << X)
387#define _FL_WARN_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
388
389#ifndef FASTLED_WARN
390// FASTLED_WARN: Stream-style (<<) only; printf-style is a compile error (#4709). Dispatch via
391// argument-count dispatch - see #3272.
392#define FASTLED_WARN(...) _FL_VA_DISPATCH(_FL_WARN_S, _FL_WARN_M, __VA_ARGS__)(__VA_ARGS__)
393#define FASTLED_WARN_IF(COND, ...) do { if (COND) FASTLED_WARN(__VA_ARGS__); } while(0)
394#endif
395
396#ifndef FL_WARN
397#if FL_HAS_WARN
398// FL_WARN: one literal or `"foo " << x` stream expression; `"foo %d", x` is a
399// compile error (#4709).
400#define FL_WARN(...) _FL_VA_DISPATCH(_FL_WARN_S, _FL_WARN_M, __VA_ARGS__)(__VA_ARGS__)
401#define FL_WARN_IF(COND, ...) do { if (COND) FL_WARN(__VA_ARGS__); } while(0)
402
403// FL_WARN_ONCE: Emits warning only once per unique location (static flag per call site)
404// Uses static bool flag initialized to false - first call prints, subsequent calls no-op
405#define FL_WARN_ONCE(...) do { \
406 static bool _warned = false; \
407 if (!_warned) { \
408 _warned = true; \
409 FL_WARN(__VA_ARGS__); \
410 } \
411} while(0)
412
413// FL_WARN_FMT: Alias for FL_WARN (kept for backwards compatibility)
414#define FL_WARN_FMT(...) FL_WARN(__VA_ARGS__)
415#define FL_WARN_FMT_IF(COND, ...) FL_WARN_IF(COND, __VA_ARGS__)
416
417// FL_WARN_LIT: defined below outside the FL_HAS_WARN block -
418// gated on FASTLED_LOG_LITE_ENABLED so it stays available on Low-memory
419// targets. See "Lite-variant literal macros" section below.
420
421// FL_WARN_EVERY: Rate-limited warning that prints at most once per interval
422// Uses static timestamp to track last print time - throttles output in tight loops
423#define FL_WARN_EVERY(MILLIS, ...) do { \
424 static fl::u32 _last_warn_time = 0; \
425 fl::u32 _now = fl::millis(); \
426 if (_now - _last_warn_time >= (MILLIS)) { \
427 _last_warn_time = _now; \
428 FL_WARN(__VA_ARGS__); \
429 } \
430} while(0)
431#else
432// No-op macros - memory-constrained platform or FL_LOG_LEVEL < 2.
433// Args dropped entirely - see the corresponding FL_ERROR section above for
434// the rationale (pre-existing call sites declare some args only under
435// FASTLED_LOG_*_ENABLED, so any `if(false)` type-check would force a build
436// error).
437#define FL_WARN(...) _FL_LOG_CHECK(__VA_ARGS__)
438#define FL_WARN_IF(COND, ...) _FL_LOG_CHECK(__VA_ARGS__)
439#define FL_WARN_ONCE(...) _FL_LOG_CHECK(__VA_ARGS__)
440#define FL_WARN_FMT(...) _FL_LOG_CHECK(__VA_ARGS__)
441#define FL_WARN_FMT_IF(COND, ...) _FL_LOG_CHECK(__VA_ARGS__)
442#define FL_WARN_EVERY(MILLIS, ...) _FL_LOG_CHECK(__VA_ARGS__)
443#endif
444#endif
445
446// Lite-variant literal macros: gated on FASTLED_LOG_LITE_ENABLED so they
447// remain usable on Low-memory targets where the full FL_WARN chain is a
448// no-op. Use these at well-known WARN sites where the message is a fixed
449// string and the byte budget matters (FastLED #2856 item 3.2, #3002).
450#ifndef FL_WARN_LIT
451#if FASTLED_LOG_LITE_ENABLED
452#define FL_WARN_LIT(LITERAL) fl::println(LITERAL)
453#define FL_LOG_LIT(LITERAL) fl::println(LITERAL)
454#else
455#define FL_WARN_LIT(LITERAL) do { } while(0)
456#define FL_LOG_LIT(LITERAL) do { } while(0)
457#endif
458#endif
459
460// =============================================================================
461// Info Macros (FL_INFO)
462// =============================================================================
463
464// Per-kind dispatch helpers - _FL_INFO_S = single-arg stream form; _FL_INFO_M = banned printf form.
465#define _FL_INFO_S(X) ::fl::detail::log_emit_auto( \
466 ::fl::detail::log_kind::INFO, \
467 ::fl::fastled_file_offset(__FILE__), int(__LINE__), \
468 ::fl::detail::log_seed() << X)
469#define _FL_INFO_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
470
471#ifndef FASTLED_INFO
472// FASTLED_INFO: Stream-style (<<) only; printf-style is a compile error (#4709). Dispatch via
473// argument-count dispatch - see #3272.
474#define FASTLED_INFO(...) _FL_VA_DISPATCH(_FL_INFO_S, _FL_INFO_M, __VA_ARGS__)(__VA_ARGS__)
475#define FASTLED_INFO_IF(COND, ...) do { if (COND) FASTLED_INFO(__VA_ARGS__); } while(0)
476#endif
477
478#ifndef FL_INFO
479#if FL_HAS_WARN
480// FL_INFO: one literal or stream expression; printf-style is a compile error
481// (#4709).
482#define FL_INFO(...) _FL_VA_DISPATCH(_FL_INFO_S, _FL_INFO_M, __VA_ARGS__)(__VA_ARGS__)
483#define FL_INFO_IF(COND, ...) do { if (COND) FL_INFO(__VA_ARGS__); } while(0)
484
485// FL_INFO_ONCE: Emits info only once per unique location (static flag per call site)
486// Uses static bool flag initialized to false - first call prints, subsequent calls no-op
487#define FL_INFO_ONCE(...) do { \
488 static bool _infoed = false; \
489 if (!_infoed) { \
490 _infoed = true; \
491 FL_INFO(__VA_ARGS__); \
492 } \
493} while(0)
494#else
495// No-op macros - memory-constrained platform or FL_LOG_LEVEL < 2.
496#define FL_INFO(...) _FL_LOG_CHECK(__VA_ARGS__)
497#define FL_INFO_IF(COND, ...) _FL_LOG_CHECK(__VA_ARGS__)
498#define FL_INFO_ONCE(...) _FL_LOG_CHECK(__VA_ARGS__)
499#endif
500#endif
501
502// =============================================================================
503// Debug Macros (FL_DBG)
504// =============================================================================
505
506#if defined(FL_IS_WASM) || !defined(RELEASE) || defined(FASTLED_TESTING)
507#define FASTLED_FORCE_DBG 1
508#endif
509
510// Reusable no-op macro for disabled debug output - avoids linker symbol pollution
511#define FL_DBG_NO_OP(X) do { if (false) { fl::sstream_noop() << X; } } while(0)
512
513// Debug printing control:
514// - FASTLED_DISABLE_DBG=1: Explicitly disable FL_DBG output (highest priority)
515// - FL_LOG_LEVEL < 2: Disable FL_DBG (needs the full log level)
516// - FASTLED_FORCE_DBG: Force enable FL_DBG (auto-set for debug builds)
517// - SKETCH_HAS_LARGE_MEMORY: Enable FL_DBG when platform has enough memory
518//
519// Priority: FASTLED_DISABLE_DBG > FL_LOG_LEVEL > FASTLED_FORCE_DBG > SKETCH_HAS_LARGE_MEMORY
520#if defined(FASTLED_DISABLE_DBG) && FASTLED_DISABLE_DBG
521// Explicit disable takes highest priority - useful for reducing serial spam
522#define FASTLED_HAS_DBG 0
523#define _FASTLED_DGB(X) FL_DBG_NO_OP(X)
524#elif FL_LOG_LEVEL < 2
525// FL_DBG needs the full log level (2). See #2773 item 2.3 and #4712.
526#define FASTLED_HAS_DBG 0
527#define _FASTLED_DGB(X) FL_DBG_NO_OP(X)
528#elif !defined(FASTLED_FORCE_DBG) && !SKETCH_HAS_LARGE_MEMORY
529// By default, debug printing is disabled to prevent memory bloat in simple applications
530#define FASTLED_HAS_DBG 0
531#define _FASTLED_DGB(X) FL_DBG_NO_OP(X)
532#else
533// Explicit debug mode enabled - uses fl::println()
534#define FASTLED_HAS_DBG 1
535#define _FASTLED_DGB(X) ::fl::detail::log_emit_auto( \
536 ::fl::detail::log_kind::INFO, \
537 ::fl::fastled_file_offset(__FILE__), int(__LINE__), \
538 ::fl::detail::log_seed() << X)
539#define _FL_DBG_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
540#endif
541
542#ifndef _FL_DBG_M
543#define _FL_DBG_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
544#endif
545#define FASTLED_DBG(...) _FL_VA_DISPATCH(_FASTLED_DGB, _FL_DBG_M, __VA_ARGS__)(__VA_ARGS__)
546
547#ifndef FASTLED_DBG_IF
548#define FASTLED_DBG_IF(COND, ...) do { if (COND) FASTLED_DBG(__VA_ARGS__); } while(0)
549#endif // FASTLED_DBG_IF
550
551// Short-form aliases for convenience (following pattern from warn.h)
552#ifndef FL_DBG
553#define FL_DBG FASTLED_DBG
554#define FL_DBG_IF FASTLED_DBG_IF
555
556// FL_DBG_EVERY: Rate-limited debug output that prints at most once per interval
557// Uses static timestamp to track last print time - throttles output in tight loops
558#if FASTLED_HAS_DBG
559#define FL_DBG_EVERY(MILLIS, ...) do { \
560 static fl::u32 _last_dbg_time = 0; \
561 fl::u32 _now = fl::millis(); \
562 if (_now - _last_dbg_time >= (MILLIS)) { \
563 _last_dbg_time = _now; \
564 FL_DBG(__VA_ARGS__); \
565 } \
566} while(0)
567#else
568#define FL_DBG_EVERY(MILLIS, ...) _FL_LOG_CHECK(__VA_ARGS__)
569#endif
570#endif
571
572// =============================================================================
573// AsyncLogger Class (in fl/log/async_logger.h)
574// =============================================================================
575// AsyncLogger, LogCategory enum, and accessor functions are now in fl/log/async_logger.h
576// This provides template-based lazy instantiation with linker-friendly auto-registration
577
594
595// =============================================================================
596// General Logging Macros
597// =============================================================================
598
606// Per-form dispatch helpers - _FL_PRINT_S = stream; _FL_PRINT_M = printf.
607// Unlike FL_WARN, no "<file>(<line>): WARN:" prefix is prepended.
608#define _FL_PRINT_S(X) ::fl::detail::print_emit_auto(::fl::detail::log_seed() << X)
609#define _FL_PRINT_M(...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
610
611#ifndef FL_PRINT
612#if FASTLED_LOG_RUNTIME_ENABLED
613// FL_PRINT: one literal or stream expression; printf-style is a compile error (#4709).
614// No "WARN:" / "file(line):" prefix (unlike FL_WARN). See #3272.
615#define FL_PRINT(...) _FL_VA_DISPATCH(_FL_PRINT_S, _FL_PRINT_M, __VA_ARGS__)(__VA_ARGS__)
616
617// FL_PRINT_EVERY: Rate-limited print that outputs at most once per interval
618// Uses static timestamp to track last print time - throttles output in tight loops
619#define FL_PRINT_EVERY(MILLIS, ...) do { \
620 static fl::u32 _last_print_time = 0; \
621 fl::u32 _now = fl::millis(); \
622 if (_now - _last_print_time >= (MILLIS)) { \
623 _last_print_time = _now; \
624 FL_PRINT(__VA_ARGS__); \
625 } \
626} while(0)
627#else
628// No-op macro for memory-constrained platforms
629#define FL_PRINT(...) _FL_LOG_CHECK(__VA_ARGS__)
630#define FL_PRINT_EVERY(MILLIS, ...) _FL_LOG_CHECK(__VA_ARGS__)
631#endif
632#endif
633
634// =============================================================================
635// Hardware Interface Logging Categories
636// =============================================================================
637
640
643#ifdef FASTLED_LOG_SPI_ENABLED
644 #define FL_LOG_SPI(...) FL_WARN(__VA_ARGS__)
645#else
646 #define FL_LOG_SPI(...) _FL_LOG_CHECK(__VA_ARGS__)
647#endif
648
651#if defined(FASTLED_LOG_RMT_ENABLED) && FL_HAS_WARN
652 #define FL_HAS_RMT_LOG 1
653 #define FL_LOG_RMT(...) FL_WARN(__VA_ARGS__)
654#else
655 #define FL_HAS_RMT_LOG 0
656 #define FL_LOG_RMT(...) _FL_LOG_CHECK(__VA_ARGS__)
657#endif
658
661#ifdef FASTLED_LOG_PARLIO_ENABLED
662 #define FL_LOG_PARLIO(...) FL_WARN(__VA_ARGS__)
663#else
664 #define FL_LOG_PARLIO(...) _FL_LOG_CHECK(__VA_ARGS__)
665#endif
666
669#ifdef FASTLED_LOG_AUDIO_ENABLED
670 #define FL_LOG_AUDIO(...) FL_WARN(__VA_ARGS__)
671#else
672 #define FL_LOG_AUDIO(...) _FL_LOG_CHECK(__VA_ARGS__)
673#endif
674
677#ifdef FASTLED_LOG_INTERRUPT_ENABLED
678 #define FL_LOG_INTERRUPT(...) FL_WARN(__VA_ARGS__)
679#else
680 #define FL_LOG_INTERRUPT(...) _FL_LOG_CHECK(__VA_ARGS__)
681#endif
682
685#ifdef FASTLED_LOG_FLEXIO_ENABLED
686 #define FL_LOG_FLEXIO(...) FL_WARN(__VA_ARGS__)
687#else
688 #define FL_LOG_FLEXIO(...) _FL_LOG_CHECK(__VA_ARGS__)
689#endif
690
693#ifdef FASTLED_LOG_OBJECTFLED_ENABLED
694 #define FL_LOG_OBJECTFLED(...) FL_WARN(__VA_ARGS__)
695#else
696 #define FL_LOG_OBJECTFLED(...) _FL_LOG_CHECK(__VA_ARGS__)
697#endif
698
700
701// =============================================================================
702// Async Logging Infrastructure (ISR-Safe)
703// =============================================================================
704
757
763// Per-form dispatch helpers - _S = stream form, _M = banned printf form. Pushes the
764// composed message to the caller-provided AsyncLogger instance.
765#define _FL_LOG_ASYNC_S(logger, X) do { \
766 ::fl::detail::async_log_emit_auto((logger), ::fl::detail::log_seed() << X); \
767} while(0)
768#define _FL_LOG_ASYNC_M(logger, ...) _FL_LOG_PRINTF_BANNED(__VA_ARGS__)
769
770// Unified async-log dispatch (#3272). Single-arg payload -> stream form (legacy
771// compatible); two-or-more args -> compile error (#4709).
772#define FL_LOG_ASYNC(logger, ...) _FL_VA_DISPATCH(_FL_LOG_ASYNC_S, _FL_LOG_ASYNC_M, __VA_ARGS__)(logger, __VA_ARGS__)
773
780#define FL_LOG_ASYNC_ISR(logger, msg) \
781 do { \
782 (logger).push(msg); \
783 } while(0)
784
785// -----------------------------------------------------------------------------
786// SPI Async Logging
787// -----------------------------------------------------------------------------
788
789#ifdef FASTLED_LOG_SPI_ENABLED
790 #define FL_LOG_SPI_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_spi_async_logger_isr(), X)
791 #define FL_LOG_SPI_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_spi_async_logger_main(), __VA_ARGS__)
792 #define FL_LOG_SPI_ASYNC_FLUSH() do { \
793 fl::get_spi_async_logger_isr().flush(); \
794 fl::get_spi_async_logger_main().flush(); \
795 } while(0)
796#else
797 #define FL_LOG_SPI_ASYNC_ISR(X) FL_DBG_NO_OP(X)
798 #define FL_LOG_SPI_ASYNC_MAIN(...) do { } while(0)
799 #define FL_LOG_SPI_ASYNC_FLUSH() do {} while(0)
800#endif
801
802// -----------------------------------------------------------------------------
803// RMT Async Logging
804// -----------------------------------------------------------------------------
805
806#ifdef FASTLED_LOG_RMT_ENABLED
807 #define FL_LOG_RMT_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_rmt_async_logger_isr(), X)
808 #define FL_LOG_RMT_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_rmt_async_logger_main(), __VA_ARGS__)
809 #define FL_LOG_RMT_ASYNC_FLUSH() do { \
810 fl::get_rmt_async_logger_isr().flush(); \
811 fl::get_rmt_async_logger_main().flush(); \
812 } while(0)
813#else
814 #define FL_LOG_RMT_ASYNC_ISR(X) FL_DBG_NO_OP(X)
815 #define FL_LOG_RMT_ASYNC_MAIN(...) do { } while(0)
816 #define FL_LOG_RMT_ASYNC_FLUSH() do {} while(0)
817#endif
818
819// -----------------------------------------------------------------------------
820// PARLIO Async Logging
821// -----------------------------------------------------------------------------
822
823#ifdef FASTLED_LOG_PARLIO_ENABLED
824 #define FL_LOG_PARLIO_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_parlio_async_logger_isr(), X)
825 #define FL_LOG_PARLIO_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_parlio_async_logger_main(), __VA_ARGS__)
826 #define FL_LOG_PARLIO_ASYNC_FLUSH() do { \
827 fl::get_parlio_async_logger_isr().flush(); \
828 fl::get_parlio_async_logger_main().flush(); \
829 } while(0)
830#else
831 #define FL_LOG_PARLIO_ASYNC_ISR(X) FL_DBG_NO_OP(X)
832 #define FL_LOG_PARLIO_ASYNC_MAIN(...) do { } while(0)
833 #define FL_LOG_PARLIO_ASYNC_FLUSH() do {} while(0)
834#endif
835
836// -----------------------------------------------------------------------------
837// AUDIO Async Logging
838// -----------------------------------------------------------------------------
839
840#ifdef FASTLED_LOG_AUDIO_ENABLED
841 #define FL_LOG_AUDIO_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_audio_async_logger_isr(), X)
842 #define FL_LOG_AUDIO_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_audio_async_logger_main(), __VA_ARGS__)
843 #define FL_LOG_AUDIO_ASYNC_FLUSH() do { \
844 fl::get_audio_async_logger_isr().flush(); \
845 fl::get_audio_async_logger_main().flush(); \
846 } while(0)
847#else
848 #define FL_LOG_AUDIO_ASYNC_ISR(X) FL_DBG_NO_OP(X)
849 #define FL_LOG_AUDIO_ASYNC_MAIN(...) do { } while(0)
850 #define FL_LOG_AUDIO_ASYNC_FLUSH() do {} while(0)
851#endif
852
853// -----------------------------------------------------------------------------
854// INTERRUPT Async Logging
855// -----------------------------------------------------------------------------
856
857#ifdef FASTLED_LOG_INTERRUPT_ENABLED
858 #define FL_LOG_INTERRUPT_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_interrupt_async_logger_isr(), X)
859 #define FL_LOG_INTERRUPT_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_interrupt_async_logger_main(), __VA_ARGS__)
860 #define FL_LOG_INTERRUPT_ASYNC_FLUSH() do { \
861 fl::get_interrupt_async_logger_isr().flush(); \
862 fl::get_interrupt_async_logger_main().flush(); \
863 } while(0)
864#else
865 #define FL_LOG_INTERRUPT_ASYNC_ISR(X) FL_DBG_NO_OP(X)
866 #define FL_LOG_INTERRUPT_ASYNC_MAIN(...) do { } while(0)
867 #define FL_LOG_INTERRUPT_ASYNC_FLUSH() do {} while(0)
868#endif
869
870// -----------------------------------------------------------------------------
871// FLEXIO Async Logging
872// -----------------------------------------------------------------------------
873
874#ifdef FASTLED_LOG_FLEXIO_ENABLED
875 #define FL_LOG_FLEXIO_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_flexio_async_logger_isr(), X)
876 #define FL_LOG_FLEXIO_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_flexio_async_logger_main(), __VA_ARGS__)
877 #define FL_LOG_FLEXIO_ASYNC_FLUSH() do { \
878 fl::get_flexio_async_logger_isr().flush(); \
879 fl::get_flexio_async_logger_main().flush(); \
880 } while(0)
881#else
882 #define FL_LOG_FLEXIO_ASYNC_ISR(X) FL_DBG_NO_OP(X)
883 #define FL_LOG_FLEXIO_ASYNC_MAIN(...) do { } while(0)
884 #define FL_LOG_FLEXIO_ASYNC_FLUSH() do {} while(0)
885#endif
886
887// -----------------------------------------------------------------------------
888// OBJECTFLED Async Logging
889// -----------------------------------------------------------------------------
890
891#ifdef FASTLED_LOG_OBJECTFLED_ENABLED
892 #define FL_LOG_OBJECTFLED_ASYNC_ISR(X) FL_LOG_ASYNC_ISR(fl::get_objectfled_async_logger_isr(), X)
893 #define FL_LOG_OBJECTFLED_ASYNC_MAIN(...) FL_LOG_ASYNC(fl::get_objectfled_async_logger_main(), __VA_ARGS__)
894 #define FL_LOG_OBJECTFLED_ASYNC_FLUSH() do { \
895 fl::get_objectfled_async_logger_isr().flush(); \
896 fl::get_objectfled_async_logger_main().flush(); \
897 } while(0)
898#else
899 #define FL_LOG_OBJECTFLED_ASYNC_ISR(X) FL_DBG_NO_OP(X)
900 #define FL_LOG_OBJECTFLED_ASYNC_MAIN(...) do { } while(0)
901 #define FL_LOG_OBJECTFLED_ASYNC_FLUSH() do {} while(0)
902#endif
903
904// -----------------------------------------------------------------------------
905// Background Flush Service
906// -----------------------------------------------------------------------------
907
913namespace fl {
915
935 void configureAsyncLogService(u32 interval_ms = 16, fl::size messages_per_tick = 5) FL_NO_EXCEPT;
936}
937
ISR-safe async logger using SPSC queue backend (zero heap allocation)
FastLED chrono implementation - duration types for time measurements.
const char * mText
Definition log.h:226
const char * c_str() const FL_NO_EXCEPT
Definition log.h:216
log_literal(const char(&text)[N]) FL_NO_EXCEPT
Definition log.h:214
fl::sstream operator<<(const T &value) const FL_NO_EXCEPT
Definition log.h:219
log_literal< N > operator<<(const char(&text)[N]) const FL_NO_EXCEPT
Definition log.h:232
fl::sstream operator<<(const T &value) const FL_NO_EXCEPT
Definition log.h:237
#define FL_NO_INLINE
void print_emit_auto(const log_literal< N > &body) FL_NO_EXCEPT
Definition log.h:264
FL_NO_INLINE const char * log_kind_name(log_kind kind) FL_NO_EXCEPT
Definition log.cpp.hpp:13
FL_NO_INLINE void log_emit_prefix(log_kind kind, const char *file, int line) FL_NO_EXCEPT
Definition log.cpp.hpp:26
void async_log_emit_auto(Logger &logger, const log_literal< N > &body) FL_NO_EXCEPT
Definition log.h:277
FL_NO_INLINE void log_emit_newline() FL_NO_EXCEPT
Definition log.cpp.hpp:48
FL_NO_INLINE void log_emit(log_kind kind, const char *file, int line, fl::sstream &body) FL_NO_EXCEPT
Definition log.cpp.hpp:68
void log_emit_auto(log_kind kind, const char *file, int line, const log_literal< N > &body) FL_NO_EXCEPT
Definition log.h:245
FL_NO_INLINE void log_emit_literal(log_kind kind, const char *file, int line, const char *body) FL_NO_EXCEPT
Definition log.cpp.hpp:87
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_info.h:57
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
constexpr int type_rank< T >::value
void configureAsyncLogService(u32 interval_ms, fl::size messages_per_tick) FL_NO_EXCEPT
Configure async logger automatic servicing task.
void async_log_service() FL_NO_EXCEPT
void println(const char *str) FL_NO_EXCEPT
const char * fastled_file_offset(const char *file) FL_NO_EXCEPT
Definition log.cpp.hpp:101
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NO_EXCEPT
Portable compile-time assertion wrapper.