diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 24b9b63..a44c4e6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -53,6 +53,22 @@ test: - . venv/bin/activate - pip install -q ${PIP_DEPS} script: + # Static guard: SDK code must log through the facade (common/log.h + # log_tag_*), never call log_emit/printf directly -- a raw call site + # bypasses the compile-time ceiling and reappears in every build. + # Allowed: the facade + its sink (common/log.{h,c}) and test harnesses; + # examples and third_party are out of scope. The word boundary is + # hand-rolled as (^|[^A-Za-z0-9_]) so fprintf/snprintf-style libc calls + # are not flagged and the CI image's git needs no PCRE. + - | + LEAKS=$(git grep -nE '(^|[^A-Za-z0-9_])(log_emit|printf)[ ]*\(' -- modules common pal \ + ':!common/log.h' ':!common/log.c' ':(exclude,glob)**/test/**' || true) + if [ -n "$LEAKS" ]; then + echo "Raw log_emit/printf call sites outside the log facade:" + echo "$LEAKS" + echo "Route them through the log_tag_* macros in common/log.h instead." + exit 1 + fi # cd into the build dir rather than using `--test-dir` (the CI image's # ctest does not honour that flag and would silently find no tests). - cd ${CMAKE_BUILD_DIR} @@ -71,14 +87,15 @@ test:memory-leak: VG=modules/iot-client/test/run_valgrind_check.sh chmod +x "$VG" # Leak-check the pure (no-subprocess) binaries, grouped by module: - # rtc-tcp-client : tai_unit_tests, tai_integration_tests + # rtc-tcp-client : tai_unit_tests, tai_log_level_zero_tests, + # tai_log_module_zero_tests, tai_integration_tests # iot-client : iot_cipher_test # tuya-ble : tuya_ble_test # rtc-client is a prebuilt closed lib (headers + libstm.a) with no offline # test, so there is nothing to leak-check for it here. The mock-driven iot # tests are covered by the `test` job; under valgrind their Python # handshakes would time out. - for t in tai_unit_tests tai_integration_tests iot_cipher_test tuya_ble_test; do + for t in tai_unit_tests tai_log_level_zero_tests tai_log_module_zero_tests tai_integration_tests iot_cipher_test tuya_ble_test; do echo "==================== valgrind: ${t} ====================" "$VG" "./${CMAKE_BUILD_DIR}/${t}" 2>&1 | tee "valgrind-${t}.log" rc=${PIPESTATUS[0]} diff --git a/AGENTS.md b/AGENTS.md index bfb9092..a561623 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,9 +93,12 @@ ctest --test-dir build --output-on-failure --no-tests=error --timeout 180 # ne omission is invisible until someone flashes a board. They diverge on purpose — `pal_posix.c` and `iot_pal_defaults.c` host-only (each IDF app defines its own `get_default_pal()`), `pal_freertos.c` IDF-only — so never blind-sync them. -5. **Module code goes through the PAL**: `pal->malloc`/`pal->free` for memory, `log_emit` for - output (via each module's prefixed `log_info`/`log_warn`/`log_error`). A direct `malloc` or - `printf` is a porting bug even where it links on the host. The one deliberate gap: `pal_t` has +5. **Module code goes through the PAL**: `pal->malloc`/`pal->free` for memory, the `log_tag_*` + macros in `common/log.h` for output (via each module's own family: `IOT_LOG*`, + `TAI_LOG*`, `TUYA_BLE_HAL_LOG*`). A direct `malloc` or + `printf` is a porting bug even where it links on the host -- and the `test` CI job greps for + raw `log_emit(`/`printf(` call sites outside `common/log.{h,c}` and the test trees, so one + fails the pipeline. The one deliberate gap: `pal_t` has only a monotonic `time_ms`, so ATOP signing reads libc `time(NULL)` — a port needs a real-time clock the C library can see, or every signed request carries a `t` the cloud rejects. 6. **CHANGELOG entries are terse, and carry a PR number.** One line per change — @@ -138,7 +141,7 @@ ordinary changes. - **`schema` and `dp_state` are one artefact; persist and restore them together.** A NULL, empty, `"[]"` or unparseable schema is not an error anywhere: `iot_dp_rebuild()` installs *loose mode* - with one `log_info` line and `iot_client_init()` still returns a healthy client. In loose mode + with one `IOT_LOGI` line and `iot_client_init()` still returns a healthy client. In loose mode cloud DP-sets are never dispatched, every `iot_dp_get`/`set` returns `OPRT_DP_INVALID_ID`, `iot_dp_restore_json()` discards the whole snapshot and still returns `OPRT_OK`, and `iot_dp_dump_json()` returns `{"dps":{}}` — which an app that periodically persists writes over @@ -169,7 +172,7 @@ ordinary changes. - **A protocol-5 downlink is always "consumed", even when nothing was applied.** `iot_dp_dispatch_downlink()` returns consumed for every protocol-5 envelope — non-object `dps`, every DP rejected, or the callback snapshot's malloc failing — with per-DP rejections at - `log_warn` only. No DP callback, no `message_callback`, no error return, and the cloud already + `IOT_LOGW` only. No DP callback, no `message_callback`, no error return, and the cloud already has its QoS1 ack. If the app needs to see rejected DP-sets, add an explicit path. ### iot-client — credentials, config, transport @@ -293,14 +296,27 @@ ordinary changes. `paths-ignore` excludes docs-only pushes from all C jobs, and `deploy-docs.yml` triggers on `main`, which does not exist here (only its `workflow_dispatch` fires it). GitLab's `pages` job is the only thing that actually catches it, so build the site locally after touching it. -- **`-DLOG_LEVEL=...` does nothing.** `common/log.h` advertises it as a compile-time ceiling but - no file references it; `log_emit()` filters on a runtime global, after the varargs have been - evaluated. The only working compile-time gate is `TAI_LOG_LEVEL`, and it covers rtc-tcp-client - only. +- **`AGENTIC_KIT_LOG_LEVEL` is the SDK-wide log gate, compile-time only.** Every SDK log + macro (log_tag_* in `common/log.h`; iot-client's IOT_LOG*, TAI_LOG*, TUYA_BLE_HAL_LOG* + re-tagged on top) expands to nothing above it. Optional per-module ceilings + (`AGENTIC_KIT_{IOT,TAI,TUYA_BLE}_LOG_LEVEL`, defaults = the global one, in each module's + config file) lower a single module further -- lower-only: the facade gate still applies, so + they can never resurrect what the global ceiling compiled out, and a value above it is a no-op + (clamped once in each module's config file, so block-level gates see the effective ceiling too). + There is no runtime level: below the ceiling a + line emits unconditionally (`log_set_level()`/`log_get_level()` and the tai_set_log_level() + wrappers are gone, and so is the runtime handler -- `log_set_handler()` no longer exists). + The destination is a compile-time fact too: define `AGENTIC_KIT_LOG` and every line dispatches + into your own macro (a function target can reuse the default output via `log_emit_valist()`). + `log_emit()` remains the default sink, but module code never calls it directly -- a raw call + would bypass the ceiling. Even the media sampler in tai_pkt_log.c picks its INFO/DEBUG sink at + compile time -- a sampling build has exactly one level, and no runtime level comparison exists + anywhere in the SDK. + The old per-module `TAI_LOG_LEVEL` gate became the namespaced `AGENTIC_KIT_TAI_LOG_LEVEL`. - **`mqtt_tls_config_t.verify_peer` is dead** — assigned in one place, read nowhere. Peer verification is decided solely by whether `cacert` or `cert_bundle_attach` is non-NULL; leaving both NULL is not "use the system trust store" (there is none on an embedded target), - it connects with verification disabled behind one `log_warn`. + it connects with verification disabled behind one `IOT_LOGW`. - **`iot_client_process(client, timeout_ms)` ignores `timeout_ms`.** The real blocking budget is the compile-time `AGENTIC_KIT_MQTT_RECV_TIMEOUT_MS` (1000 ms), and the CONNECT sets a 60 s keepalive. Do not use the argument to pace the app loop. diff --git a/CHANGELOG.md b/CHANGELOG.md index b2fd8af..7efc953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- docs-site — English edition of the full documentation site, published at `/en/` with Simplified Chinese retained at `/` (PR pending). +- docs-site — English edition of the full documentation site, published at `/en/` with Simplified Chinese retained at `/`. - All 29 docs are mirrored under `docs-site/i18n/en/`, with a locale selector in both the landing-page navbar and the custom docs topbar, localized navbar/footer/sidebar catalogs, and English SVG schematics under `current/images/`. - Heading anchors use explicit IDs shared across locales, and `npm run check:i18n` gates path, image, and anchor parity as part of `npm run build`. @@ -23,10 +23,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SDK-wide — every build-time knob is renamed with an `AGENTIC_KIT_` prefix and its default moves to its owning subsystem's config file; override per product with one `agentic_kit_config.h` on the include path, `-D=`, or `AGENTIC_KIT_USER_CONFIG` (#38). - **BREAKING** `TAI_FRAG_BUF_SIZE` → `AGENTIC_KIT_TAI_FRAG_BUF_SIZE`, `RESPONSE_BUFFER_SIZE` → `AGENTIC_KIT_RESPONSE_BUFFER_SIZE`, and likewise the rest — old→new table in `docs-site/docs/guides/compile-time-knobs.md`. - - Knob defaults live at: the override pickup in `common/log.h`; PAL task sizing in `pal/pal_config_defaults.h`; per-module knobs in each module's include/ (`iot_client_config_defaults.h`, `tai_config_defaults.h`; tuya-ble has no knobs, so no defaults file until one appears). Non-knob module data (version strings, endpoints, log binding) lives in each module's src-side internal header (`src/iot_internal.h` restores and renames the former `iot_config_defaults.h`). + - Knob defaults live at: the override pickup in `common/log.h`; PAL task sizing in `pal/pal_config_defaults.h`; per-module knobs in each module's include/ (`iot_client_config_defaults.h`, `tai_config_defaults.h`, `tuya_ble_config_defaults.h`). Non-knob module data (version strings, endpoints, log binding) lives in each module's src-side internal header (`src/iot_internal.h` restores and renames the former `iot_config_defaults.h`). - Removed the dead `IOT_DO_NOT_USE_CUSTOM_CONFIG` flag from the ESP-IDF component. +- SDK-wide — one compile-time log ceiling `AGENTIC_KIT_LOG_LEVEL` (0–4, default 4) gates every SDK log macro via the new `log_tag_*` facade in `common/log.h`; module code no longer calls `log_emit()` directly (#40). + - **BREAKING** `-DTAI_LOG_LEVEL=N` (rtc-tcp-client only) and the dead `-DLOG_LEVEL` both move to `-DAGENTIC_KIT_LOG_LEVEL=N`, which now silences the whole SDK at compile time. Lowering only rtc-tcp-client afterwards is the new `-DAGENTIC_KIT_TAI_LOG_LEVEL=N` (see below). + - Optional per-module log ceilings were added back on top: `AGENTIC_KIT_IOT_LOG_LEVEL`, `AGENTIC_KIT_TAI_LOG_LEVEL` and `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL` all default to the SDK-wide ceiling and can only lower their one module below it (effective ceiling = the smaller of the two; values above the global ceiling are clamped to it once in each module's config file, so block-level gates see the effective ceiling too). They gate each module's vocabulary where it is defined, ride the same override pickup, and keep the compile-time-only property — below 3, the rtc-tcp-client packet-log formatter vanishes with its calls. tuya-ble gains its first knob and its `tuya_ble_config_defaults.h` this way. + - **BREAKING** the runtime log layer is removed: `log_set_level()`, `log_get_level()` and the `tai_set_log_level()`/`tai_get_log_level()` wrappers are gone, and `log_emit()` no longer filters — the ceiling is the only level gate, so what compiles in is what emits. Migrate runtime `log_set_level(N)` calls to a lower ceiling at build time, or to level filtering inside your `AGENTIC_KIT_LOG` target. + - **BREAKING** the runtime handler is removed too: `log_set_handler()`, `log_fn_t` and the public `log_default_handler()` are gone — the destination is a compile-time fact, with no mutable log state left in the SDK. Make the old handler function the `AGENTIC_KIT_LOG` target (it now receives the level and the bare tag directly), and use the new `log_emit_valist()` — the facade's `va_list` entry, the `esp_log_writev` role — to reuse the default stderr output from a function sink. The SDK's own tests migrated the same way: each test build remaps the dispatch into one sink whose capture/quiet/count are modes, not handler swaps. + - The dispatch itself is customer-remappable at compile time: defining `AGENTIC_KIT_LOG(level, tag, fmt, ...)` routes every SDK log line into your own macro — straight into `ESP_LOGx`-style systems with no `va_list` round trip (the tag arrives as its own token). The `AGENTIC_KIT_LOG_LEVEL` ceiling still applies, and since there is no runtime handler anymore, the remap is the one and only destination switch. The ESP-IDF pair-by-ble example now uses exactly this: `kit_opts/agentic_kit_config.h` maps the facade onto `ESP_LOG_LEVEL_LOCAL`, replacing its old runtime callback (each line also keeps its real module tag instead of one shared `"tuya_ble"` tag). + - rtc-tcp-client packet logging selects its INFO/DEBUG sink at compile time — sampling builds have exactly one level; flood mode needs `AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N=0` plus a DEBUG ceiling and vanishes otherwise — and its JSON formatter compiles out below INFO. A compile-matrix test builds 19 level × sampling combinations across both the SDK-wide and the rtc-tcp-client-only ceiling, including module-above-global clamp probes. - tuya-ble — the SDK-internal log-facade binding, scan-token rotation and pending-credential - delivery are each defined once in `tuya_ble_internal.h` instead of being repeated per module (PR pending). + delivery are each defined once in `tuya_ble_internal.h` instead of being repeated per module. - tuya-ble — bounded per-state Trsmitr reassembly, queued TX with backpressure, and configurable radio capability (#35). - Ports drive `tuya_ble_prov_set_gatt_payload()`, `tuya_ble_prov_tx_ready()` and `tuya_ble_prov_tick()`; call `tuya_ble_prov_close()` on disconnect/reset. - Recompile consumers for the public state/config layout changes; `comm_ability = 0` selects 2.4 GHz. @@ -42,11 +49,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docs — the pair-by-ble callback sample no longer claims credentials are never logged; the demo's DEBUG protocol log prints the credential JSON on purpose, for device bring-up. - Docs — the pair-by-ble stop section now also states the port's `nimble_port_deinit()` abort, - instead of implying every `tuya_ble_nimble_stop()` failure is retryable (PR pending). -- tuya-ble — preserve pending credentials under TX backpressure and route Trsmitr diagnostics through the log facade (PR pending). -- Examples — isolate BLE WiFi scan completions across cancellation, join workers before teardown, and restart STA before connecting (PR pending). -- tuya-ble — enforce Pairing/KEY_12 credential authorization, reject replayed Frames, and accept the app's extra CBC padding block (PR pending). -- tuya-ble — reset Pairing state on device-info re-query and defer credential delivery until queued acknowledgements are accepted by the port (PR pending). + instead of implying every `tuya_ble_nimble_stop()` failure is retryable (#35). +- tuya-ble — preserve pending credentials under TX backpressure and route Trsmitr diagnostics through the log facade (#35). +- Examples — isolate BLE WiFi scan completions across cancellation, join workers before teardown, and restart STA before connecting (#35). +- tuya-ble — enforce Pairing/KEY_12 credential authorization, reject replayed Frames, and accept the app's extra CBC padding block (#35). +- tuya-ble — reset Pairing state on device-info re-query and defer credential delivery until queued acknowledgements are accepted by the port (#35). - iot-client — US-East (`UEAZ`) fell back to an ATOP host that does not resolve(#31). `IOT_UEAZ_HOST` is now `a1-ueaz.tuyaus.com`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 92dcb3e..bc89af3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,10 +276,12 @@ if(AGENTIC_KIT_BUILD_TESTS AND AGENTIC_KIT_ENABLE_PROJECT_TESTS) add_executable(tai_unit_tests ${AI_TCP_SOURCES} "${AI_TCP_TESTS_DIR}/test_core.c" + "${AI_TCP_TESTS_DIR}/test_log.c" ) target_include_directories(tai_unit_tests PRIVATE "${AI_TCP_DIR}/include" "${AI_TCP_DIR}/src" + "${AI_TCP_TESTS_DIR}/log_config" "${PAL_DIR}" ) target_link_libraries(tai_unit_tests PRIVATE @@ -288,15 +290,78 @@ if(AGENTIC_KIT_BUILD_TESTS AND AGENTIC_KIT_ENABLE_PROJECT_TESTS) ) add_test(NAME tai_unit_tests COMMAND tai_unit_tests) + # Compile the same unit suite with the SDK-wide log ceiling fully closed. + # Its packet-log regression verifies both no dispatch and no argument + # evaluation at the call site. + add_executable(tai_log_level_zero_tests + ${AI_TCP_SOURCES} + "${AI_TCP_TESTS_DIR}/test_core.c" + "${AI_TCP_TESTS_DIR}/test_log.c" + ) + target_include_directories(tai_log_level_zero_tests PRIVATE + "${AI_TCP_DIR}/include" + "${AI_TCP_DIR}/src" + "${AI_TCP_TESTS_DIR}/log_config" + "${PAL_DIR}" + ) + target_compile_definitions(tai_log_level_zero_tests PRIVATE + AGENTIC_KIT_LOG_LEVEL=0 + ) + target_link_libraries(tai_log_level_zero_tests PRIVATE + agentic_kit_pal + Threads::Threads + ) + add_test(NAME tai_log_level_zero_tests COMMAND tai_log_level_zero_tests) + + # Same suite with ONLY the rtc-tcp-client module ceiling closed + # (-DAGENTIC_KIT_TAI_LOG_LEVEL=0) while the SDK-wide ceiling stays at + # its default: proves the per-module knob lowers independently -- the + # module assertions take the compiled-out branches while test_core.c's + # direct-facade probe still dispatches. + add_executable(tai_log_module_zero_tests + ${AI_TCP_SOURCES} + "${AI_TCP_TESTS_DIR}/test_core.c" + "${AI_TCP_TESTS_DIR}/test_log.c" + ) + target_include_directories(tai_log_module_zero_tests PRIVATE + "${AI_TCP_DIR}/include" + "${AI_TCP_DIR}/src" + "${AI_TCP_TESTS_DIR}/log_config" + "${PAL_DIR}" + ) + target_compile_definitions(tai_log_module_zero_tests PRIVATE + AGENTIC_KIT_TAI_LOG_LEVEL=0 + ) + target_link_libraries(tai_log_module_zero_tests PRIVATE + agentic_kit_pal + Threads::Threads + ) + add_test(NAME tai_log_module_zero_tests COMMAND tai_log_module_zero_tests) + + # Prove that packet-log level selection is purely compile-time across the + # ceiling/sampling modes. The script preprocesses and compiles tai_pkt_log.c + # in nineteen configurations -- the SDK-wide ceiling and the rtc-tcp-client-only + # ceiling each swept over levels 0/2/3/4 x sampling 0/50, plus module-above-global + # clamp probes -- then inspects the expanded sinks. + add_test(NAME tai_pkt_log_compile_matrix + COMMAND "${CMAKE_COMMAND}" + "-DSOURCE_DIR=${CMAKE_SOURCE_DIR}" + "-DOUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}/tai-pkt-log-compile-matrix" + "-DC_COMPILER=${CMAKE_C_COMPILER}" + -P "${AI_TCP_TESTS_DIR}/test_pkt_log_compile_matrix.cmake" + ) + add_executable(tai_integration_tests ${AI_TCP_SOURCES} "${AI_TCP_TESTS_DIR}/tai_pal_loopback.c" "${AI_TCP_TESTS_DIR}/test_integration.c" + "${AI_TCP_TESTS_DIR}/test_log.c" ) target_include_directories(tai_integration_tests PRIVATE "${AI_TCP_DIR}/include" "${AI_TCP_DIR}/src" "${AI_TCP_TESTS_DIR}" + "${AI_TCP_TESTS_DIR}/log_config" "${PAL_DIR}" ) target_link_libraries(tai_integration_tests PRIVATE @@ -305,9 +370,52 @@ if(AGENTIC_KIT_BUILD_TESTS AND AGENTIC_KIT_ENABLE_PROJECT_TESTS) ) add_test(NAME tai_integration_tests COMMAND tai_integration_tests) + # Test twin of tuya_iot_client: same sources, but the log dispatch is + # remapped into the test sink (log_config/agentic_kit_config.h on its + # PUBLIC include path), so library-TU logs are capturable/quietable. + # The vendored coreHTTP/coreMQTT sources compile in too: their logs are + # routed into the facade by common/core_http_config.h and + # common/core_mqtt_config.h, so they are part of the SDK's logging + # surface -- outside the remap their lines would bypass the sink (and + # a runtime handler no longer exists to catch them library-agnostically). + # The plain tuya_iot_client and the core_http/core_mqtt targets stay + # remap-free for the examples. + # Keep sources/includes/links in sync with tuya_iot_client above and + # the core_http/core_mqtt targets above. + add_library(tuya_iot_client_testlog STATIC + ${IOT_CLIENT_SOURCES} + "${corehttp_source_dir}/source/core_http_client.c" + "${corehttp_source_dir}/source/dependency/3rdparty/llhttp/src/api.c" + "${corehttp_source_dir}/source/dependency/3rdparty/llhttp/src/http.c" + "${corehttp_source_dir}/source/dependency/3rdparty/llhttp/src/llhttp.c" + "${coremqtt_source_dir}/source/core_mqtt.c" + "${coremqtt_source_dir}/source/core_mqtt_serializer.c" + "${coremqtt_source_dir}/source/core_mqtt_state.c" + "${IOT_CLIENT_TESTS_DIR}/test_log.c" + ) + target_include_directories(tuya_iot_client_testlog PUBLIC + "${IOT_CLIENT_DIR}/include" + "${IOT_CLIENT_DIR}/src" + "${IOT_CLIENT_TESTS_DIR}/log_config" + "${corehttp_source_dir}/source/include" + "${corehttp_source_dir}/source/interface" + "${corehttp_source_dir}/source/dependency/3rdparty/llhttp/include" + "${coremqtt_source_dir}/source/include" + "${coremqtt_source_dir}/source/interface" + "${cjson_source_dir}" + "${PAL_DIR}" + "${COMMON_DIR}" + ) + target_link_libraries(tuya_iot_client_testlog PUBLIC + ${AGENTIC_KIT_CJSON_TARGET} + agentic_kit_pal + agentic_kit_common # provides mbedTLS transitively (shared TLS lives here) + Threads::Threads + ) + function(agentic_kit_add_iot_test target source) add_executable(${target} "${source}") - target_link_libraries(${target} PRIVATE tuya_iot_client) + target_link_libraries(${target} PRIVATE tuya_iot_client_testlog) target_compile_definitions(${target} PRIVATE PYTHON3_EXEC="${Python3_EXECUTABLE}" TEST_CONFIG_DIR="${IOT_CLIENT_TESTS_DIR}/config" @@ -334,12 +442,24 @@ if(AGENTIC_KIT_BUILD_TESTS AND AGENTIC_KIT_ENABLE_PROJECT_TESTS) agentic_kit_add_iot_test(iot_ota_test "${IOT_CLIENT_TESTS_DIR}/iot_ota_test.c") agentic_kit_add_iot_test(iot_ota_verify_test "${IOT_CLIENT_TESTS_DIR}/iot_ota_verify_test.c") + # The ble sources compile into the executable (not the tuya_ble + # library) so they pick up the test sink's log remap from + # tuya_iot_client_testlog's include path -- that is what lets + # test_wire assert on "[ble] [TRSMITR]" warns. add_executable(tuya_ble_test + "${TUYA_BLE_DIR}/src/tuya_ble_bigdata.c" + "${TUYA_BLE_DIR}/src/tuya_ble_prov.c" + "${TUYA_BLE_DIR}/src/tuya_ble_trsmitr.c" "${TUYA_BLE_TESTS_DIR}/test_wire.c" "${TUYA_BLE_TESTS_DIR}/test_prov.c" "${TUYA_BLE_TESTS_DIR}/tuya_ble_hal_host.c" ) - target_link_libraries(tuya_ble_test PRIVATE tuya_ble tuya_iot_client) + target_include_directories(tuya_ble_test PRIVATE + "${TUYA_BLE_DIR}/include" + "${IOT_CLIENT_TESTS_DIR}" # test_log.h -- the shared test sink + "${cjson_source_dir}" + ) + target_link_libraries(tuya_ble_test PRIVATE tuya_iot_client_testlog) add_test(NAME tuya_ble_test COMMAND tuya_ble_test) endif() @@ -349,5 +469,3 @@ endif() if(AGENTIC_KIT_BUILD_EXAMPLES) add_subdirectory(examples/posix) endif() - - diff --git a/common/core_http_config.h b/common/core_http_config.h index c7d3d59..8820de0 100644 --- a/common/core_http_config.h +++ b/common/core_http_config.h @@ -23,8 +23,8 @@ /* coreHTTP invokes these with a doubly-parenthesised argument -- * LogError( ( "fmt", args ) ) -- so `message` arrives complete with its own * parentheses, which then serve as the call parentheses of the macro below. */ -#define CORE_HTTP_LOG_ERROR( ... ) log_emit(LOG_ERROR, "[http] " __VA_ARGS__) -#define CORE_HTTP_LOG_WARN( ... ) log_emit(LOG_WARN, "[http] " __VA_ARGS__) +#define CORE_HTTP_LOG_ERROR( ... ) log_tag_error("http", "" __VA_ARGS__) +#define CORE_HTTP_LOG_WARN( ... ) log_tag_warn("http", "" __VA_ARGS__) /* #undef first, mirroring core_mqtt_config.h: whichever of the two vendored * config headers a translation unit reaches second must win rather than warn. diff --git a/common/core_mqtt_config.h b/common/core_mqtt_config.h index 4f467ef..8e9c720 100644 --- a/common/core_mqtt_config.h +++ b/common/core_mqtt_config.h @@ -26,9 +26,9 @@ /* coreMQTT invokes these with a doubly-parenthesised argument -- * LogError( ( "fmt", args ) ) -- so `message` arrives complete with its own * parentheses, which then serve as the call parentheses of the macro below. - * Same shape as the log_* wrappers in iot_client_config_defaults.h. */ -#define CORE_MQTT_LOG_ERROR( ... ) log_emit(LOG_ERROR, "[mqtt] " __VA_ARGS__) -#define CORE_MQTT_LOG_WARN( ... ) log_emit(LOG_WARN, "[mqtt] " __VA_ARGS__) + * Same shape as the wrappers in core_http_config.h. */ +#define CORE_MQTT_LOG_ERROR( ... ) log_tag_error("mqtt", "" __VA_ARGS__) +#define CORE_MQTT_LOG_WARN( ... ) log_tag_warn("mqtt", "" __VA_ARGS__) /* #undef first: coreHTTP's core_http_config_defaults.h defines both as empty * under its own #ifndef. A translation unit that reached that header first diff --git a/common/log.c b/common/log.c index acea6ec..e4866de 100644 --- a/common/log.c +++ b/common/log.c @@ -1,9 +1,9 @@ /* * log.c -- Default implementation of the log facade. * - * State (handler + runtime level) is kept in plain globals. Updates are - * unsynchronised: setters are expected to run during startup before worker - * threads emit logs. Read-side races are harmless on word-sized loads. + * Stateless by design: no handler pointer to swap, no level to set, + * nothing mutable at runtime. The destination is a compile-time fact + * (the AGENTIC_KIT_LOG remap in log.h); this file is the default one. */ #include "log.h" @@ -12,27 +12,12 @@ #include #include -static log_fn_t g_handler = NULL; /* NULL => default */ -static log_level_t g_level = LOG_INFO; /* runtime ceiling */ - -void log_set_handler(log_fn_t fn) -{ - g_handler = fn; -} - -void log_set_level(log_level_t level) -{ - if (level < LOG_NONE) level = LOG_NONE; - if (level > LOG_DEBUG) level = LOG_DEBUG; - g_level = level; -} - -log_level_t log_get_level(void) -{ - return g_level; -} - -void log_default_handler(log_level_t level, const char *fmt, va_list args) +/* The default destination: "HH:MM:SS [L] [tag] msg" to stderr (the tag + * is already folded into fmt by the macro layer). No intermediate + * buffer: format directly to stderr. Take stderr's stdio lock so the + * prefix + body + newline emit as one atomic line even when worker + * threads log concurrently. */ +static void default_output(log_level_t level, const char *fmt, va_list args) { static const char *L[] = { "-", "E", "W", "I", "D" }; const char *lc = (level >= LOG_ERROR && level <= LOG_DEBUG) ? L[level] : "?"; @@ -47,9 +32,6 @@ void log_default_handler(log_level_t level, const char *fmt, va_list args) #endif strftime(ts, sizeof ts, "%H:%M:%S", &tm_buf); - /* No intermediate buffer: format directly to stderr. Take stderr's - * stdio lock so the prefix + body + newline emit as one atomic line - * even when worker threads log concurrently. */ #if defined(_WIN32) _lock_file(stderr); #else @@ -65,15 +47,15 @@ void log_default_handler(log_level_t level, const char *fmt, va_list args) #endif } -void log_emit(log_level_t level, const char *fmt, ...) +void log_emit_valist(log_level_t level, const char *fmt, va_list args) { - if (level > g_level) - return; - - log_fn_t handler = g_handler ? g_handler : log_default_handler; + default_output(level, fmt, args); +} +void log_emit(log_level_t level, const char *fmt, ...) +{ va_list args; va_start(args, fmt); - handler(level, fmt, args); + default_output(level, fmt, args); va_end(args); } diff --git a/common/log.h b/common/log.h index ef12e7b..f90d629 100644 --- a/common/log.h +++ b/common/log.h @@ -1,14 +1,18 @@ /* * log.h -- Process-wide logging facade for agentic-kit. * - * One handler, one runtime level, one tagged dispatch — shared by ai-tcp, - * iot-client, tuya-ble, and any caller that wants to plug in. The default - * handler writes "HH:MM:SS [L] msg" to stderr; replace it via - * log_set_handler() to redirect to syslog, a UART, etc. + * One compile-time log ceiling, one tagged dispatch, one destination + * decided at build time — shared by ai-tcp, iot-client, tuya-ble, and + * any caller that wants to plug in. Where lines land is not runtime + * state: define AGENTIC_KIT_LOG (below) and the dispatch lands in your + * own macro — no function, no va_list round trip; the default expansion + * calls log_emit, which writes "HH:MM:SS [L] [tag] msg" to stderr. A + * sink implemented as a variadic function can route back through + * log_emit_valist to reuse that output. * * Module tags (e.g. "[iot] ", "[tai] ") are folded into the format string - * by per-module wrapper macros, so the handler signature stays minimal: - * level + fmt + va_list, mirroring ESP-IDF's esp_log_set_vprintf(). + * by per-module wrapper macros, so the dispatch stays tag-agnostic: + * level + fmt + va_list, mirroring ESP-IDF's esp_log_writev(). * * Levels: * 1 = error, 2 = warn, 3 = info, 4 = debug. @@ -21,14 +25,14 @@ #include /* ------------------------------------------------------------------------- - * Build-time config: the integrator-override pickup. Every SDK subsystem - * keeps its knob defaults in its own *_config_defaults.h - * (modules//include/, pal/pal_config_defaults.h); the one thing they - * all share is this header, because every SDK translation unit includes - * it. That makes log.h the right home for the override pickup: it must - * run BEFORE any #ifndef knob default, whichever file that default lives - * in -- and each defaults file includes this header first for exactly - * that reason. + * Build-time config: the integrator-override pickup + the SDK-wide log + * ceiling. Every SDK subsystem keeps its knob defaults in its own + * *_config_defaults.h (modules//include/, pal/pal_config_defaults.h); + * the one thing they all share is this header, because every SDK + * translation unit includes it. That makes log.h the right home for the + * override pickup: it must run BEFORE any #ifndef knob default, whichever + * file that default lives in -- and each defaults file includes this + * header first for exactly that reason. * * Integrators override by defining a knob FIRST -- pick whichever fits the * build system (all of them must apply to every target that compiles SDK @@ -39,8 +43,8 @@ * picked up automatically, before every default. * 2. Add -D= to the compile options. * 3. -DAGENTIC_KIT_USER_CONFIG='"my_kit_opts.h"' names an override - * header with an arbitrary file name (its directory still needs - * to be on the include path) -- for toolchains without __has_include. + * header with an arbitrary file name (its directory still needs to + * be on the include path) -- for toolchains without __has_include. * When set, it wins and the agentic_kit_config.h search is skipped. * * Why the SDK never owns a file named agentic_kit_config.h: a quoted @@ -74,6 +78,52 @@ #endif #endif +/* The single compile-time log ceiling for the whole SDK: every log macro + * (log_tag_* below; iot-client's IOT_LOG*, TAI_LOG* and + * TUYA_BLE_HAL_LOG* re-tagged on top of those) compiles out above it -- + * no call, no argument evaluation, no format string in the image. + * 0 = none, 1 = error, 2 = +warn, 3 = +info, 4 = +debug (default). + * This ceiling is the only SDK-wide level filter: nothing is held back + * for runtime, so what compiles in is what emits. Quieting a build is + * itself a build decision (-DAGENTIC_KIT_LOG_LEVEL=2 for error+warn); so + * is redirecting or dropping lines wholesale -- define AGENTIC_KIT_LOG + * (below) and the dispatch lands in your own macro. Optional per-module + * ceilings (AGENTIC_KIT_IOT_LOG_LEVEL, AGENTIC_KIT_TAI_LOG_LEVEL, + * AGENTIC_KIT_TUYA_BLE_LOG_LEVEL; defaults = this one, in each module's + * config file) lower a single module further: they ride the same pickup, + * gate the module vocabulary where it is defined, and can never raise a + * line above this ceiling. */ +#ifndef AGENTIC_KIT_LOG_LEVEL +#define AGENTIC_KIT_LOG_LEVEL 4 /* LOG_DEBUG */ +#endif + +/* The compile-time remap point for the SDK's log dispatch. Define + * AGENTIC_KIT_LOG in your agentic_kit_config.h (or -D it, like any knob) + * and every log line dispatches into YOUR macro instead of the facade's + * log_emit — macro-to-macro, which is what bridges into macro-based + * logging systems (ESP-IDF's ESP_LOGx, Zephyr's LOG_*) with no + * va_list -> buffer -> "%s" round trip. You receive the level + * (LOG_ERROR..LOG_DEBUG), the bare module-tag literal ("iot", "ble", + * "mqtt", "http", "tls", "rng", "pal", ...; rtc-tcp-client passes + * per-source-file tags such as "client" and "pkt"), a printf format + * and its arguments — the tag as its own token is what makes + * structured sinks and per-tag filtering possible. + * + * Two rules, binding both directions: + * - the ceiling above still gates: log_tag_* collapse to ((void)0) at + * levels above it, so a remap cannot resurrect compiled-out lines; + * - the remap names the destination for EVERY translation unit of the + * build — the same iron rule as the knobs (apply it to all targets + * that compile SDK sources). There is no runtime dispatch left to + * fall back on and no second switch: what compiles in is what prints. + * The default expansion keeps log_emit's format(printf) checking; your + * own macro opts out unless you re-add the attribute. + */ +#ifndef AGENTIC_KIT_LOG +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + log_emit(level, "[" tag "] " fmt, ##__VA_ARGS__) +#endif + #ifdef __cplusplus extern "C" { #endif @@ -84,35 +134,57 @@ extern "C" { #define LOG_INFO 3 #define LOG_DEBUG 4 -#ifndef LOG_LEVEL -# define LOG_LEVEL LOG_DEBUG /* compile-time ceiling */ +/* Compile-time ceiling — the single gate for SDK logging. + * + * AGENTIC_KIT_LOG_LEVEL (default 4 = debug) is the maximum level compiled + * into the SDK. Every log macro the SDK itself uses dispatches through one + * of the four below — the per-module families (iot-client's IOT_LOG*, + * TAI_LOG*, TUYA_BLE_HAL_LOG*) are thin re-tags of these — and expands to + * ((void)0) above the ceiling: no call, no argument evaluation, no format + * string in the image. Below the ceiling the line emits unconditionally — + * there is no runtime level, so what compiles in is what prints. + * + * `tag` must be a string literal (folded into the format so the facade + * stays tag-agnostic). Don't call log_emit() directly: it bypasses the + * ceiling (CI rejects new raw call sites); runtime-chosen levels dispatch + * over these macros instead, as tai_pkt_log.c does. The dispatch target + * itself is customer-remappable: AGENTIC_KIT_LOG, above. */ +#if AGENTIC_KIT_LOG_LEVEL >= 1 +#define log_tag_error(tag, fmt, ...) AGENTIC_KIT_LOG(LOG_ERROR, tag, fmt, ##__VA_ARGS__) +#else +#define log_tag_error(tag, fmt, ...) ((void)0) +#endif +#if AGENTIC_KIT_LOG_LEVEL >= 2 +#define log_tag_warn(tag, fmt, ...) AGENTIC_KIT_LOG(LOG_WARN, tag, fmt, ##__VA_ARGS__) +#else +#define log_tag_warn(tag, fmt, ...) ((void)0) +#endif +#if AGENTIC_KIT_LOG_LEVEL >= 3 +#define log_tag_info(tag, fmt, ...) AGENTIC_KIT_LOG(LOG_INFO, tag, fmt, ##__VA_ARGS__) +#else +#define log_tag_info(tag, fmt, ...) ((void)0) +#endif +#if AGENTIC_KIT_LOG_LEVEL >= 4 +#define log_tag_debug(tag, fmt, ...) AGENTIC_KIT_LOG(LOG_DEBUG, tag, fmt, ##__VA_ARGS__) +#else +#define log_tag_debug(tag, fmt, ...) ((void)0) #endif -/* Runtime level type — int-typedef so the LOG_* macros above remain +/* Log level type — int-typedef so the LOG_* macros above remain * usable in preprocessor `#if` checks (which can't see enum values). */ typedef int log_level_t; -/* Handler signature: receives the (already level-filtered) format string - * and va_list. Handlers may vsnprintf into their own buffer or forward - * to a structured sink. Implementations must be reentrant or self-locking. */ -typedef void (*log_fn_t)(log_level_t level, - const char *fmt, va_list args); - -/* Replace the active handler. Pass NULL to restore the default. */ -void log_set_handler(log_fn_t fn); - -/* Runtime ceiling — messages with level > this are dropped before formatting. */ -void log_set_level(log_level_t level); -log_level_t log_get_level(void); - -/* Default handler: writes "HH:MM:SS [L] msg" to stderr. Holds stderr's - * stdio lock across the prefix + body + newline so concurrent threads - * never interleave a single line. Used as the fallback when no handler - * has been registered. */ -void log_default_handler(log_level_t level, - const char *fmt, va_list args); +/* va_list entry into the facade (the esp_log_writev pattern): emit a + * line you already hold as a va_list. This is for a custom destination + * implemented as a variadic function — it forwards here to reuse the + * default stderr output instead of re-implementing it. The remap and + * the log_tag_* macros above are the normal way in; nothing filters + * here (the ceiling has already applied at the macro layer). */ +void log_emit_valist(log_level_t level, const char *fmt, va_list args); -/* Format and dispatch one log line. Safe to call before set_handler/set_level. */ +/* Format and dispatch one log line to the default stderr output — the + * facade's own floor, not an escape hatch around the ceiling (CI + * rejects raw call sites in modules). */ #if defined(__GNUC__) || defined(__clang__) void log_emit(log_level_t level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); diff --git a/common/rng.c b/common/rng.c index 7a33ba9..9b305b5 100644 --- a/common/rng.c +++ b/common/rng.c @@ -49,7 +49,7 @@ int rng_init(const pal_t *pal) return 0; } if (!pal || !pal->mutex_create) { - log_emit(LOG_ERROR, "[rng] rng_init requires a pal with mutex support"); + log_tag_error("rng", "rng_init requires a pal with mutex support"); return -1; } mbedtls_entropy_init(&g_entropy); @@ -58,12 +58,12 @@ int rng_init(const pal_t *pal) int rc = mbedtls_ctr_drbg_seed(&g_drbg, mbedtls_entropy_func, &g_entropy, pers, sizeof(pers) - 1); if (rc != 0) { - log_emit(LOG_ERROR, "[rng] DRBG seed failed: -0x%04x", (unsigned) -rc); + log_tag_error("rng", "DRBG seed failed: -0x%04x", (unsigned) -rc); return -1; } g_rng_mutex = pal->mutex_create(); if (!g_rng_mutex) { - log_emit(LOG_ERROR, "[rng] mutex_create failed"); + log_tag_error("rng", "mutex_create failed"); return -1; } g_ready = 1; @@ -81,7 +81,7 @@ int rng_bytes(const pal_t *pal, uint8_t *buf, size_t len) return 0; } if (!g_ready) { - log_emit(LOG_ERROR, "[rng] rng_bytes() called before rng_init()"); + log_tag_error("rng", "rng_bytes() called before rng_init()"); return -1; } /* g_rng_mutex is always set once g_ready is true; the caller's pal (the one @@ -90,7 +90,7 @@ int rng_bytes(const pal_t *pal, uint8_t *buf, size_t len) int rc = mbedtls_ctr_drbg_random(&g_drbg, buf, len); pal->mutex_unlock(g_rng_mutex); if (rc != 0) { - log_emit(LOG_ERROR, "[rng] DRBG random failed: -0x%04x", (unsigned) -rc); + log_tag_error("rng", "DRBG random failed: -0x%04x", (unsigned) -rc); } return rc == 0 ? 0 : -1; } diff --git a/common/tls.c b/common/tls.c index 0b4370b..7e2acd1 100644 --- a/common/tls.c +++ b/common/tls.c @@ -144,7 +144,7 @@ tls_t *tls_connect(const tls_config_t *cfg) pal->free(pem); } if (pret != 0) { - log_emit(LOG_ERROR, "[tls] failed to parse CA certificate: -0x%04X", + log_tag_error("tls", "failed to parse CA certificate: -0x%04X", (unsigned)-pret); goto fail; } @@ -159,13 +159,13 @@ tls_t *tls_connect(const tls_config_t *cfg) t->tcp_handle = pal->tcp_connect(cfg->host, cfg->port, est_to); if (!t->tcp_handle) { - log_emit(LOG_ERROR, "[tls] TCP connect failed to %s:%u", + log_tag_error("tls", "TCP connect failed to %s:%u", cfg->host, (unsigned)cfg->port); goto fail; } if (pal->time_ms() >= deadline) { - log_emit(LOG_ERROR, - "[tls] connect to %s:%u exhausted %ums budget during TCP connect", + log_tag_error("tls", + "connect to %s:%u exhausted %ums budget during TCP connect", cfg->host, (unsigned)cfg->port, (unsigned)est_to); goto fail; } @@ -201,8 +201,8 @@ tls_t *tls_connect(const tls_config_t *cfg) } else { mbedtls_ssl_conf_authmode(&t->conf, tls_map_verify(cfg->verify)); if (cfg->verify == TLS_VERIFY_NONE) - log_emit(LOG_WARN, - "[tls] peer verification disabled (no CA certificate)"); + log_tag_warn("tls", + "peer verification disabled (no CA certificate)"); } if (mbedtls_ssl_setup(&t->ssl, &t->conf) != 0) goto fail; @@ -227,7 +227,7 @@ tls_t *tls_connect(const tls_config_t *cfg) uint64_t now = pal->time_ms(); if (now >= deadline) { - log_emit(LOG_ERROR, "[tls] connect to %s:%u timed out after %ums (handshake)", + log_tag_error("tls", "connect to %s:%u timed out after %ums (handshake)", cfg->host, (unsigned)cfg->port, (unsigned)est_to); goto fail; } @@ -238,13 +238,13 @@ tls_t *tls_connect(const tls_config_t *cfg) /* <0 is a socket error: fail fast instead of re-polling a dead fd until * the deadline (which would burn CPU on a tight retry loop). */ if (pal->tcp_poll(t->tcp_handle, ev, poll_ms) < 0) { - log_emit(LOG_ERROR, "[tls] handshake poll error on %s:%u", + log_tag_error("tls", "handshake poll error on %s:%u", cfg->host, (unsigned)cfg->port); goto fail; } } if (ret != 0) { - log_emit(LOG_ERROR, "[tls] handshake failed to %s:%u: -0x%04X", + log_tag_error("tls", "handshake failed to %s:%u: -0x%04X", cfg->host, (unsigned)cfg->port, (unsigned)-ret); goto fail; } @@ -261,16 +261,16 @@ tls_t *tls_connect(const tls_config_t *cfg) int vn = mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags); if (vn > 0) - log_emit(LOG_WARN, - "[tls] peer certificate not verified (0x%08X):\n%s", + log_tag_warn("tls", + "peer certificate not verified (0x%08X):\n%s", (unsigned)flags, vrfy_buf); else - log_emit(LOG_WARN, "[tls] peer certificate not verified (0x%08X)", + log_tag_warn("tls", "peer certificate not verified (0x%08X)", (unsigned)flags); } } - log_emit(LOG_INFO, "[tls] connected to %s:%u (%s, %s)", + log_tag_info("tls", "connected to %s:%u (%s, %s)", cfg->host, (unsigned)cfg->port, mbedtls_ssl_get_version(&t->ssl), mbedtls_ssl_get_ciphersuite(&t->ssl)); @@ -307,8 +307,8 @@ int tls_write(tls_t *t, const uint8_t *buf, size_t len, uint32_t timeout_ms) /* Deadline hit while still draining -- distinguish this from a * hard mbedTLS error below so a slow/backed-up link is not * misread as a broken connection. */ - log_emit(LOG_ERROR, - "[tls] write timed out after %ums (%u/%u bytes sent)", + log_tag_error("tls", + "write timed out after %ums (%u/%u bytes sent)", (unsigned)timeout_ms, (unsigned)written, (unsigned)len); return TLS_ERR_NET; } @@ -318,7 +318,7 @@ int tls_write(tls_t *t, const uint8_t *buf, size_t len, uint32_t timeout_ms) continue; } /* Fatal write error -- log the raw mbedTLS cause before collapsing. */ - log_emit(LOG_ERROR, "[tls] write failed: -0x%04X", (unsigned)-n); + log_tag_error("tls", "write failed: -0x%04X", (unsigned)-n); return TLS_ERR_NET; } return TLS_OK; @@ -352,7 +352,7 @@ int tls_read(tls_t *t, uint8_t *buf, size_t len, uint32_t timeout_ms) * underlying socket error). `n` is the raw mbedTLS cause -- log it * before collapsing to TLS_ERR_NET, which otherwise hides which * fault occurred from callers that only see -3. */ - log_emit(LOG_ERROR, "[tls] read failed: -0x%04X", (unsigned)-n); + log_tag_error("tls", "read failed: -0x%04X", (unsigned)-n); return TLS_ERR_NET; } diff --git a/docs-site/docs/architecture.mdx b/docs-site/docs/architecture.mdx index 84e7d23..af20c20 100644 --- a/docs-site/docs/architecture.mdx +++ b/docs-site/docs/architecture.mdx @@ -59,6 +59,7 @@ Agentic-kit 是 AI 智能设备的端侧 SDK,负责设备与涂鸦云平台之 共享 TLScommon/tls.c基于 mbedTLS 的 TLS-over-TCP 传输层,由 IoT(MQTT / HTTP)与 RTC(rtc-tcp-client)两栈共用 共享 RNGcommon/rng.c进程级唯一的 CTR-DRBG 随机数发生器,在启动时一次性播种,供全 SDK 复用 + 日志门面common/log.h、common/log.c全 SDK 编译期日志级别与分发(AGENTIC_KIT_LOG_LEVEL / AGENTIC_KIT_LOG),见《编译期旋钮》 diff --git a/docs-site/docs/guides/atop-generic-call.md b/docs-site/docs/guides/atop-generic-call.md index 3f01a96..4bf3558 100644 --- a/docs-site/docs/guides/atop-generic-call.md +++ b/docs-site/docs/guides/atop-generic-call.md @@ -76,6 +76,7 @@ iot_atop_request_t req = { .api = "tuya.device.schema.newest.get", .data = body }; iot_atop_response_t resp = {0}; +/* 出错路径用 SDK 公共日志门面 log_tag_*(应用代码可直接调用,与 SDK 日志同一编译期上限与分发) */ int rc = iot_atop_call(client, &req, &resp); if (rc == OPRT_OK) { if (resp.result != NULL) { @@ -83,10 +84,10 @@ if (rc == OPRT_OK) { } } else if (rc == OPRT_ATOP_BUSINESS_ERROR) { /* 请求到达了云端,被云端拒绝 —— error_code 说明原因 */ - log_error("rejected: %s (%s)", resp.error_code, resp.error_msg); + log_tag_error("app", "rejected: %s (%s)", resp.error_code, resp.error_msg); } else { /* 传输层失败:DNS / TLS / HTTP / 解密 */ - log_error("call failed: %d", rc); + log_tag_error("app", "call failed: %d", rc); } iot_atop_response_free(client, &resp); /* 每条路径都要调,包括失败路径 */ diff --git a/docs-site/docs/guides/compile-time-knobs.md b/docs-site/docs/guides/compile-time-knobs.md index fc3b4d5..da97b70 100644 --- a/docs-site/docs/guides/compile-time-knobs.md +++ b/docs-site/docs/guides/compile-time-knobs.md @@ -6,7 +6,7 @@ sidebar_position: 9 # 编译期旋钮配置 -SDK 的全部编译期旋钮——TAI 收发缓冲与调度、MQTT 超时与包大小、ATOP HTTP 缓冲、FreeRTOS 任务栈——共 18 个,默认值按**所属子系统**存放(集成方覆盖的统一挂载点在 `common/log.h`,原因见下文):FreeRTOS 任务旋钮在 `pal/pal_config_defaults.h`;模块旋钮在各自 include/ 目录——`modules/iot-client/include/iot_client_config_defaults.h`(MQTT + ATOP HTTP)、`modules/rtc-tcp-client/include/tai_config_defaults.h`(TAI 缓冲与调度);tuya-ble 目前没有编译期旋钮(原因见下文速查表)。每个旋钮的完整说明(单位、联动、踩过的坑)在各自文件的注释里;本页讲怎么按产品覆盖它们、这套机制为什么长这样,并给出速查表。 +SDK 的全部编译期旋钮——TAI 收发缓冲与调度、MQTT 超时与包大小、ATOP HTTP 缓冲、FreeRTOS 任务栈、日志级别(全局一条 + 模块级三条,每模块一条)——共 22 个,默认值按**所属子系统**存放:全局日志上限 `AGENTIC_KIT_LOG_LEVEL` 在 `common/log.h`(这个头同时承载集成方覆盖的统一挂载点,原因见下文);FreeRTOS 任务旋钮在 `pal/pal_config_defaults.h`;模块旋钮在各自 include/ 目录——`modules/iot-client/include/iot_client_config_defaults.h`(MQTT + ATOP HTTP + 模块日志上限)、`modules/rtc-tcp-client/include/tai_config_defaults.h`(TAI 缓冲、调度与模块日志上限)、`modules/tuya-ble/include/tuya_ble_config_defaults.h`(模块日志上限;tuya-ble 目前仅此一个旋钮,原因见下文)。每个旋钮的完整说明(单位、联动、踩过的坑)在各自文件的注释里;本页讲怎么按产品覆盖它们、这套机制为什么长这样,并给出速查表。 ## 三种覆盖方式(任选其一) {#三种覆盖方式任选其一} @@ -18,6 +18,8 @@ SDK 的全部编译期旋钮——TAI 收发缓冲与调度、MQTT 超时与包 /* agentic_kit_config.h —— 只写要改的,其余用 SDK 默认 */ #define AGENTIC_KIT_RESPONSE_BUFFER_SIZE 8192 /* 产品 DP schema 大 */ #define AGENTIC_KIT_TAI_FRAG_BUF_SIZE 16000U /* ESP32 无 PSRAM:缩小收包缓冲 */ +#define AGENTIC_KIT_LOG_LEVEL 2 /* 量产只留 error+warn;代价见下文"日志"一节 */ +#define AGENTIC_KIT_TAI_LOG_LEVEL 0 /* 全局压到 2 之后,把 rtc-tcp-client 整个模块再关死(连 ERROR/WARN 也静音;只降不升,见下文) */ ``` ESP-IDF 工程(SDK 以组件形式编译):把文件放进项目任意目录,加一行让组件看得到它—— @@ -39,7 +41,8 @@ cmake -B build -DCMAKE_C_FLAGS="-I" ```cmake target_compile_definitions(my_sdk_target PRIVATE - AGENTIC_KIT_RESPONSE_BUFFER_SIZE=8192) + AGENTIC_KIT_RESPONSE_BUFFER_SIZE=8192 + AGENTIC_KIT_LOG_LEVEL=2) ``` ### 方式三:`AGENTIC_KIT_USER_CONFIG` 指定任意文件名 {#方式三-agentic_kit_user_config-指定任意文件名} @@ -58,7 +61,7 @@ target_compile_definitions(my_sdk_target PRIVATE ## 为什么这样设计 {#为什么这样设计} -**为什么默认值分散在各子系统、覆盖挂载点却只有一个。** 这些默认值原本散落在各调用点,缓冲大小实际是**产品属性**(schema 多大、有没有 PSRAM、音频帧长多少),选型时需要按内存预算逐项审;生产事故复盘时也要能一眼回答"这块内存是哪个旋钮、为什么是这个值"。现在默认值跟着所属子系统走——FreeRTOS 任务在 `pal/pal_config_defaults.h`、模块旋钮在各自 include/——审预算、做评审时对着所属文件即可。而集成方覆盖的**捡起逻辑**只存在于 `common/log.h` 一处:SDK 每个编译单元都包含这个头,各 `*_config_defaults.h` 也都先包含它,因此任何 `#ifndef` 默认值生效前,你的覆盖一定已经就位——一份 `agentic_kit_config.h` 打动全部 18 个旋钮,不需要按子系统拆多个覆盖文件。 +**为什么默认值分散在各子系统、覆盖挂载点却只有一个。** 这些默认值原本散落在各调用点,缓冲大小实际是**产品属性**(schema 多大、有没有 PSRAM、音频帧长多少),选型时需要按内存预算逐项审;生产事故复盘时也要能一眼回答"这块内存是哪个旋钮、为什么是这个值"。现在默认值跟着所属子系统走——日志在 `common/log.h`、FreeRTOS 任务在 `pal/pal_config_defaults.h`、模块旋钮在各自 include/——审预算、做评审时对着所属文件即可。而集成方覆盖的**捡起逻辑**只存在于 `common/log.h` 一处:SDK 每个编译单元都包含这个头,各 `*_config_defaults.h` 也都先包含它,因此任何 `#ifndef` 默认值生效前,你的覆盖一定已经就位——一份 `agentic_kit_config.h` 打动全部 22 个旋钮,不需要按子系统拆多个覆盖文件。 **为什么都加 `AGENTIC_KIT_` 前缀。** 撞名不是假设出来的风险:coreMQTT 自带的 `core_mqtt_config_defaults.h` 定义了同名 `MQTT_SEND_TIMEOUT_MS`(默认 20000U),与 SDK 的 2000U 谁生效取决于包含顺序;`LOG_LEVEL` 也被多个平台 SDK 占用。前缀把这些名字搬进 SDK 自己的命名空间——你的 `-D` 不会再打到别人的宏,别人的也不会打到你的。 @@ -66,9 +69,72 @@ target_compile_definitions(my_sdk_target PRIVATE **为什么是 `#ifndef` 默认 + 先包含你的文件,而不是让你直接改 SDK 文件。** 你不碰 SDK 源文件,升级没有合并冲突;你的文件里没写的旋钮自动跟随 SDK 默认值。 +## 日志:编译期闸门,单层 {#日志编译期闸门单层} + +`AGENTIC_KIT_LOG_LEVEL` 是所有从源码编译的 SDK 模块的全局日志开关(预编译的 rtc-client 除外——它的日志走自己的运行时接口,见其参考页 §3.5),且只在编译期起作用:**0 = 全关,1 = error,2 = +warn,3 = +info,4 = +debug(默认)**。高于上限的日志在编译期整体消失——没有函数调用、不求值参数、格式字符串也不进固件(直接省 flash/RAM);上限以下的日志无条件输出。**没有运行时级别,编译进什么就出什么。** + +需要按模块区分级别时(比如 iot-client 只要 info、rtc-tcp-client 要 debug),用**每模块上限**:`AGENTIC_KIT_IOT_LOG_LEVEL`、`AGENTIC_KIT_TAI_LOG_LEVEL`、`AGENTIC_KIT_TUYA_BLE_LOG_LEVEL`,默认都等于全局上限,且**只能把单个模块再压低**——有效上限 = 两者取小,设得比全局高没有效果(超出部分会在模块的 config 文件里被钳回全局值,块级门控看到的就是有效上限)。它们门控在各自模块词汇表的定义处(`IOT_LOG*` / `TAI_LOG*` / `TUYA_BLE_HAL_LOG*`,外加 rtc-tcp-client 的包日志格式化器),与全局上限走同一条覆盖捡起通道。上面那个"iot 只要 info、rtc 要 debug"就是:全局保持默认 4,再加一行 `-DAGENTIC_KIT_IOT_LOG_LEVEL=3`。注意上限按**行实际输出的级别**判断:tuya-ble 的 `TUYA_BLE_HAL_LOGI` 派发在 debug,想保留它得给 4 而不是 3。 + +因此日志量是构建决策:量产固件用 `-DAGENTIC_KIT_LOG_LEVEL=2` 编译,error + warn 之外的一切(代码与字符串)都不进镜像;开发构建保持默认 4 拿到全部日志。运行时层已整体移除(`log_set_level()`/`log_get_level()`/`tai_set_log_level()` 均已删除),级别不再有第二个开关。**日志去哪儿同样是构建决策**:默认输出到 stderr;定义 `AGENTIC_KIT_LOG`(见下节)把每行分发进你自己的宏——需要"运行时收放"的场合(比如测试里捕获/静默),把模式做进你宏的目标函数即可,SDK 不持有任何日志状态。 + +> **迁移**:原来的 per-module `-DTAI_LOG_LEVEL=N` 现在是 `-DAGENTIC_KIT_TAI_LOG_LEVEL=N`(仍只作用于 rtc-tcp-client,语义变化:只能在全局上限**之下**再压低本模块;要把整个 SDK 一起压低用 `-DAGENTIC_KIT_LOG_LEVEL=N`)。原来在启动时调 `log_set_level(N)` 的代码:删除调用,改用 `-DAGENTIC_KIT_LOG_LEVEL=N` 编译,或在你 `AGENTIC_KIT_LOG` 的目标里按 level 过滤。原来用 `log_set_handler()` 安装 handler 的代码:把那个函数变成 `AGENTIC_KIT_LOG` 的目标(它现在直接拿到 level 和裸 tag,连格式串里的 tag 前缀都不用再剥)。 + +### 改写日志分发:AGENTIC_KIT_LOG {#改写日志分发agentic_kit_log} + +日志的去向没有运行时开关,它由你在编译期决定:在旋钮的同一个覆盖文件里定义 `AGENTIC_KIT_LOG`,SDK 的每行日志就分发进你自己的宏。这正是接宏式日志系统(ESP-IDF 的 `ESP_LOGx`、Zephyr 的 `LOG_*`)的路——宏对宏,没有 `va_list` 中转(`va_list` 没法转发给宏,凡走函数桥接的方案都得 `vsnprintf` 进缓冲再拿 `%s` 喂出去:多一次拷贝、多一个截断点、丢掉对方宏的格式检查): + +```c +/* agentic_kit_config.h —— 与旋钮同一个文件、同一个捡起机制; + * level 是 SDK 的 1–4 整数,esp_log_level_t 需要一层映射 */ +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + ESP_LOG_LEVEL_LOCAL((level) == LOG_ERROR ? ESP_LOG_ERROR : \ + (level) == LOG_WARN ? ESP_LOG_WARN : \ + (level) == LOG_INFO ? ESP_LOG_INFO : \ + ESP_LOG_DEBUG, \ + tag, fmt, ##__VA_ARGS__) +``` + +SDK 里所有日志宏(iot-client 的 `IOT_LOG*`、`TAI_LOG*`、`TUYA_BLE_HAL_LOG*`)都汇到 `log_tag_*`,再经 `AGENTIC_KIT_LOG` 分发——一个定义接管全部。你拿到的是 **level、裸 tag、printf 格式串与参数**:tag 是独立 token,结构化 sink 和按 tag 过滤从此可行。实际取值:iot-client 一律 `"iot"`,tuya-ble 一律 `"ble"`,rtc-tcp-client 按源文件细分(`"client"`/`"transport"`/`"proto"`/`"crypto"`/`"pkt"`),公共与 PAL 层另有 `"mqtt"`/`"http"`/`"tls"`/`"rng"`/`"pal"`。 + +两条规则,两个方向都成立: + +- **闸门仍然在上**:被 `AGENTIC_KIT_LOG_LEVEL` 编译掉的行,改写救不回来(`log_tag_*` 在上限之上展开为 `((void)0)`); +- **改写必须覆盖编译 SDK 源的每个目标**——与旋钮同一条铁律。没有运行时分发可以回退,也没有第二道开关:编译进什么,就打印什么。默认展开保留 `log_emit` 的 `format(printf)` 编译期检查;换成你自己的宏就不再有这层检查(除非你自己加回 attribute)。你的目标若是函数而不是宏,它可以调 `log_emit_valist()`(facade 的 `va_list` 入口,对应 `esp_log_writev` 的角色)复用默认 stderr 输出,不必重写格式化。 + +函数型 sink 的完整形状——注意 `log_emit_valist()` **不带 tag 参数**,tag 要在宏里折进格式串,输出才会像默认那样带 `[tag]` 前缀: + +```c +/* agentic_kit_config.h */ +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + my_sink(level, "[" tag "] " fmt, ##__VA_ARGS__) + +/* 你的代码(另需 #include ) */ +void my_sink(log_level_t level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + log_emit_valist(level, fmt, ap); + va_end(ap); +} +``` + ## 旋钮速查 {#旋钮速查} -默认值与详细理由以各 config 文件的注释为准;"何时调整"是最常见的场景提示。各表所在文件:PAL 表在 `pal/pal_config_defaults.h`;iot-client 两表在 `modules/iot-client/include/iot_client_config_defaults.h`;TAI 表在 `modules/rtc-tcp-client/include/tai_config_defaults.h`。 +默认值与详细理由以各 config 文件的注释为准;"何时调整"是最常见的场景提示。各表所在文件:全局日志上限在 `common/log.h`;三个模块日志上限在各自模块的 config 文件(见本页开头的文件清单);PAL 表在 `pal/pal_config_defaults.h`;iot-client 三表在 `modules/iot-client/include/iot_client_config_defaults.h`;TAI 表在 `modules/rtc-tcp-client/include/tai_config_defaults.h`;tuya-ble 表在 `modules/tuya-ble/include/tuya_ble_config_defaults.h`。 + +### 全 SDK {#全-sdk} + +| 旋钮 | 默认 | 何时调整 | +|------|------|---------| +| `AGENTIC_KIT_LOG_LEVEL` | 4(debug) | 量产降到 1–2(编译期单层,见上文) | + +### 每模块日志上限 {#每模块日志上限} + +| 旋钮 | 默认 | 何时调整 | +|------|------|---------| +| `AGENTIC_KIT_IOT_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | 只压低 iot-client 一个模块(只降不升,见上文"日志"节) | +| `AGENTIC_KIT_TAI_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | 只压低 rtc-tcp-client;低于 3 时连包日志格式化器一起去掉 | +| `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | 只压低 tuya-ble;`TUYA_BLE_HAL_LOGI` 派发在 debug,保留它需 4 | ### iot-client:MQTT {#iot-client-mqtt} @@ -114,6 +180,8 @@ target_compile_definitions(my_sdk_target PRIVATE | 旧名 | 新名 | |------|------| +| `LOG_LEVEL` | `AGENTIC_KIT_LOG_LEVEL` | +| `TAI_LOG_LEVEL` | `AGENTIC_KIT_TAI_LOG_LEVEL`(仍只作用于 rtc-tcp-client,且只能压低到全局上限之下;全局用 `AGENTIC_KIT_LOG_LEVEL`) | | `MQTT_MAX_PACKET_SIZE` | `AGENTIC_KIT_MQTT_MAX_PACKET_SIZE` | | `MQTT_SEND_TIMEOUT_MS` | `AGENTIC_KIT_MQTT_SEND_TIMEOUT_MS` | | `MQTT_RECV_TIMEOUT_MS` | `AGENTIC_KIT_MQTT_RECV_TIMEOUT_MS` | @@ -136,9 +204,9 @@ target_compile_definitions(my_sdk_target PRIVATE ## 这些名字故意不在 config 文件里 {#这些名字故意不在-config-文件里} - **`TUYA_BLE_HAL_LOGI/LOGW/LOGE/HEXDUMP`** —— 定义在 `modules/tuya-ble/include/tuya_ble_prov.h`,是该公开头文件的端口绑定契约,由端口在包含前覆盖。 -- **`TUYA_BLE_RX_BUF_SIZE` / `TUYA_BLE_TX_BUF_SIZE` / `TUYA_BLE_TX_QUEUE_DEPTH`** —— 同样在 `tuya_ble_prov.h`,但原因不同:它们决定公共结构体 `tuya_ble_prov_state_t` 的布局,端口按它们设定自己的缓冲尺寸,属于端口 API 的一部分;改它们改的是端口要跟着重编、重定尺寸的结构体布局(该头文件本身声明布局不保证 ABI 稳定),不是构建旋钮。tuya-ble 目前因此没有任何编译期旋钮(完整说明见 `tuya_ble_prov.h` 的头注释;将来出现旋钮时将以 `AGENTIC_KIT_TUYA_BLE_*` 命名落在 `modules/tuya-ble/include/tuya_ble_config_defaults.h`)。 +- **`TUYA_BLE_RX_BUF_SIZE` / `TUYA_BLE_TX_BUF_SIZE` / `TUYA_BLE_TX_QUEUE_DEPTH`** —— 同样在 `tuya_ble_prov.h`,但原因不同:它们决定公共结构体 `tuya_ble_prov_state_t` 的布局,端口按它们设定自己的缓冲尺寸,属于端口 API 的一部分;改它们改的是端口要跟着重编、重定尺寸的结构体布局(该头文件本身声明布局不保证 ABI 稳定),不是构建旋钮。tuya-ble 的编译期旋钮因此只有一个——模块日志上限 `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL`,按上述命名约定落在 `modules/tuya-ble/include/tuya_ble_config_defaults.h`(几何/布局常量的完整说明见 `tuya_ble_prov.h` 的头注释)。 - **`IOT_SDK_SW_VER` / `PV` / `BV` 与区域 ATOP 域名** —— `modules/iot-client/src/iot_internal.h`,发版管理的值,不是构建旋钮(内部头,与旋钮分家,不走覆盖机制)。 -- **coreMQTT / coreHTTP 日志路由** —— `common/core_mqtt_config.h`、`common/core_http_config.h`;它们把 coreMQTT/coreHTTP 的内部日志路由到全局日志 facade,不是构建旋钮。 +- **coreMQTT / coreHTTP 日志路由** —— `common/core_mqtt_config.h`、`common/core_http_config.h`;路由后的日志行同样过 `AGENTIC_KIT_LOG_LEVEL` 闸门,无需单独调整。 ## 怎么确认覆盖生效了 {#怎么确认覆盖生效了} @@ -156,7 +224,7 @@ target_compile_definitions(my_sdk_target PRIVATE cc -I/modules/iot-client/include -I/common -I<你的 config 目录> -c probe.c # 安静通过 = 生效 ``` -**行为观察**。ATOP 响应超限时,错误日志会直接给出当前缓冲大小并建议对应 `-D`(默认 handler 的输出形状是 `HH:MM:SS [E] [模块tag]`;`(server said …)` 是 HTTP 状态码——服务端往往已成功返回 200,这正是这行日志要澄清的误解): +**行为观察**。ATOP 响应超限时,错误日志会直接给出当前缓冲大小并建议对应 `-D`(默认输出的形状是 `HH:MM:SS [E] [模块tag]`;`(server said …)` 是 HTTP 状态码——服务端往往已成功返回 200,这正是这行日志要澄清的误解): ```text 14:13:15 [E] [iot] HTTP response does not fit: need 6558 B body + 300 B headers, buffer is 4096 B (server said 200). Rebuild with a larger -DAGENTIC_KIT_RESPONSE_BUFFER_SIZE. diff --git a/docs-site/docs/guides/porting-to-new-platform.md b/docs-site/docs/guides/porting-to-new-platform.md index fb2281e..6ba7f25 100644 --- a/docs-site/docs/guides/porting-to-new-platform.md +++ b/docs-site/docs/guides/porting-to-new-platform.md @@ -146,7 +146,7 @@ CONFIG_FREERTOS_HZ=1000 ## 通用注意事项 {#通用注意事项} -- SDK 全部编译期旋钮(TAI 缓冲与调度、MQTT 超时与包大小、ATOP HTTP 缓冲、FreeRTOS 任务栈、日志级别)的默认值与说明按所属子系统存放:日志上限与集成方覆盖的统一挂载点在 `common/log.h`(SDK 每个编译单元都包含它),FreeRTOS 任务旋钮在 `pal/pal_config_defaults.h`,模块旋钮在各自 include/ 下——`modules/iot-client/include/iot_client_config_defaults.h`、`modules/rtc-tcp-client/include/tai_config_defaults.h`(tuya-ble 目前没有编译期旋钮)。按产品覆盖任选其一(见 `common/log.h` 头注释;完整说明见[编译期旋钮](./compile-time-knobs)):自己创建 `agentic_kit_config.h`(只写要改的 `#define`,无论旋钮属于哪个子系统都写在这一个文件里),把所在目录加进编译 SDK 源码的 target 的 include 路径(CMake:`target_include_directories( PRIVATE <目录>)`)即可被自动捡起;沿用 `-D<宏>=<值>`;或用 `-DAGENTIC_KIT_USER_CONFIG='"my_opts.h"'` 指定任意文件名(无 `__has_include` 的工具链用这种)。注意:旋钮值必须对每个编译 SDK 源码的 target 保持一致 +- SDK 全部编译期旋钮(TAI 缓冲与调度、MQTT 超时与包大小、ATOP HTTP 缓冲、FreeRTOS 任务栈、日志级别)的默认值与说明按所属子系统存放:日志上限与集成方覆盖的统一挂载点在 `common/log.h`(SDK 每个编译单元都包含它),FreeRTOS 任务旋钮在 `pal/pal_config_defaults.h`,模块旋钮在各自 include/ 下——`modules/iot-client/include/iot_client_config_defaults.h`、`modules/rtc-tcp-client/include/tai_config_defaults.h`、`modules/tuya-ble/include/tuya_ble_config_defaults.h`(tuya-ble 目前仅一个旋钮:模块日志上限)。按产品覆盖任选其一(见 `common/log.h` 头注释;完整说明见[编译期旋钮](./compile-time-knobs)):自己创建 `agentic_kit_config.h`(只写要改的 `#define`,无论旋钮属于哪个子系统都写在这一个文件里),把所在目录加进编译 SDK 源码的 target 的 include 路径(CMake:`target_include_directories( PRIVATE <目录>)`)即可被自动捡起;沿用 `-D<宏>=<值>`;或用 `-DAGENTIC_KIT_USER_CONFIG='"my_opts.h"'` 指定任意文件名(无 `__has_include` 的工具链用这种)。注意:旋钮值必须对每个编译 SDK 源码的 target 保持一致 - PAL `thread_create` 需要设置足够的栈大小;`pal_freertos.c` 默认使用 `AGENTIC_KIT_PAL_FR_TASK_STACK_WORDS`(6144 words,32 位平台上约 24KB,可按平台内存情况调小或调大) - SDK 内部通过 mbedTLS 处理 TLS,需要正确的系统时间用于证书验证;若未提供 CA 证书,TLS 连接可能退化为不校验证书的模式 - `tcp_recv` 应支持阻塞/超时语义(后台线程会循环调用) diff --git a/docs-site/docs/reference/iot-client.md b/docs-site/docs/reference/iot-client.md index a4eae91..2150838 100644 --- a/docs-site/docs/reference/iot-client.md +++ b/docs-site/docs/reference/iot-client.md @@ -72,7 +72,7 @@ IoT Client 模块(CMake 目标 `tuya_iot_client`,产物 `libtuya_iot_client. 10:15:13 [E] [iot] MQTT_Connect failed: MQTTServerRefused (6) ``` -前三行来自 coreMQTT(`[mqtt]`),最后一行来自 SDK(`[iot]`)。时间戳前缀由默认日志处理器加上;若应用用 `log_set_handler()` 换了处理器,前缀形式取决于该实现。 +前三行来自 coreMQTT(`[mqtt]`),最后一行来自 SDK(`[iot]`)。时间戳前缀由默认输出加上;若应用定义 `AGENTIC_KIT_LOG` 接管了分发,输出形式取决于该宏的展开。 | CONNACK code | 文案 | 常见原因与处理 | |----|------|------| @@ -124,7 +124,7 @@ IoT Client 模块(CMake 目标 `tuya_iot_client`,产物 `libtuya_iot_client. ### Log Level(`log_level_t`) {#log-levellog_level_t} -日志通过 `common/log.h` 的全局日志门面控制,使用 `log_set_level()` 设置运行时级别,使用 `log_set_handler()` 自定义输出。 +日志通过 `common/log.h` 的全局日志门面控制:日志量由编译期的 `AGENTIC_KIT_LOG_LEVEL` 全局决定,`AGENTIC_KIT_IOT_LOG_LEVEL` 可再单独压低本模块(只降不升,默认等于全局值;无运行时级别);日志去哪儿同样是编译期决定——定义 `AGENTIC_KIT_LOG` 把每行分发进你自己的宏,在其中输出或按级别丢弃。 注:`log_level_t` 并非枚举,而是 `typedef int`(以便 `LOG_*` 可用于预处理器 `#if` 判断),下表中的名称均为宏定义。 @@ -495,11 +495,10 @@ int iot_get_ca_certificate(iot_client_t *client, const char *host, uint16_t port IoT Client 使用 `common/log.h` 提供的全局日志门面,不再提供单独的日志回调设置 API。 ```c -#include "log.h" +// 日志量在编译期由 -DAGENTIC_KIT_LOG_LEVEL=N 决定(-DAGENTIC_KIT_IOT_LOG_LEVEL=N 只压低本模块),无运行时级别 -// 设置运行时日志级别 -log_set_level(LOG_INFO); - -// 自定义日志输出处理函数 -log_set_handler(my_log_handler); +// 日志去向也在编译期决定:在你的 agentic_kit_config.h 里 +// 把分发 remap 进自己的宏(在其中输出或按级别丢弃均可) +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + my_log(level, tag, fmt, ##__VA_ARGS__) ``` diff --git a/docs-site/docs/reference/rtc-client.md b/docs-site/docs/reference/rtc-client.md index ed591ad..7dc4d76 100644 --- a/docs-site/docs/reference/rtc-client.md +++ b/docs-site/docs/reference/rtc-client.md @@ -134,6 +134,8 @@ stm_ret stm_open_set_log_level(stm_log_level_e level); | 5 | `STM_LOG_LEVEL_FATAL` | 致命 | | 6 | `STM_LOG_LEVEL_NONE` | 不输出 | +注意:rtc-client 是预编译库,其日志经初始化时的 `on_log` 回调输出、级别由本运行时接口控制——不在 SDK 编译期 `AGENTIC_KIT_LOG_LEVEL` / `AGENTIC_KIT_LOG` 门面之内(从源码编译的模块才走该门面,见《编译期旋钮》)。 + --- ## 4. 会话管理 {#4-会话管理} diff --git a/docs-site/docs/reference/rtc-tcp-client.md b/docs-site/docs/reference/rtc-tcp-client.md index 34bbc19..2746456 100644 --- a/docs-site/docs/reference/rtc-tcp-client.md +++ b/docs-site/docs/reference/rtc-tcp-client.md @@ -529,22 +529,19 @@ int tai_send_mcp_response(tai_ctx_t *ctx, const char *json_rpc_response); ## 6. 日志 {#6-日志} -```c -static inline void tai_set_log_level(int level); -static inline int tai_get_log_level(void); -``` - -设置/获取运行时日志级别。有效值: +SDK 日志的全局开关是编译期的 `AGENTIC_KIT_LOG_LEVEL`(在 `common/log.h` 统一设限,作用于整个 SDK,不限于本模块);本模块可再用 `AGENTIC_KIT_TAI_LOG_LEVEL` 单独压低(原 `TAI_LOG_LEVEL` 的改名,只降不升,默认等于全局值)。有效值: | 值 | 含义 | |----|------| -| 0 | 禁用所有日志 | +| 0 | 全关 | | 1 | `TAI_LOG_ERROR` | | 2 | `TAI_LOG_WARN` | -| 3 | `TAI_LOG_INFO`(运行时默认) | -| 4 | `TAI_LOG_DEBUG` | +| 3 | `TAI_LOG_INFO` | +| 4 | `TAI_LOG_DEBUG`(默认) | + +高于上限的日志在编译期消除;上限以下无条件输出——没有运行时级别(`tai_set_log_level()` 已随运行时层一起移除)。量产用 `-DAGENTIC_KIT_LOG_LEVEL=2` 压到 error + warn,或用 `-DAGENTIC_KIT_TAI_LOG_LEVEL=N` 只压本模块(低于 3 时 `tai_log_packet` 的 JSON 格式化器连同调用一并消失);改变日志去向(或按级别丢弃)同样是构建决策:定义 `AGENTIC_KIT_LOG` 把分发接管进你自己的宏。 -运行时默认级别为 `TAI_LOG_INFO`(3),需要 DEBUG 输出时显式调用 `tai_set_log_level(4)`。编译时可通过定义 `TAI_LOG_LEVEL` 宏设置最大编译级别(默认 4,超过的日志在编译期消除)。 +媒体中间帧默认按 1/N 采样打 INFO(`AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N`,默认 50;设 0 = 关采样进 flood 模式)。flood 模式下中间帧改打 DEBUG,仅当有效上限为 4 时才输出——上限为 3 时把采样设为 0,中间帧会**完全没有日志**(比默认采样更少,别误判成丢包)。 --- diff --git a/docs-site/docs/tutorials/agent-trigger.md b/docs-site/docs/tutorials/agent-trigger.md index 3b10279..8fd818c 100644 --- a/docs-site/docs/tutorials/agent-trigger.md +++ b/docs-site/docs/tutorials/agent-trigger.md @@ -239,7 +239,7 @@ cmake --build build --target agent_trigger_demo | `--timeout S` | 等待推送的超时秒数 | `120` | | `--repeat N` | 收到 N 次推送后退出,`0` = 一直监听 | `1` | | `--audio FILE` | TTS 音频输出文件,传空串则丢弃 | `output_trigger_tts.pcm` | -| `-v, --verbose` | 打开 SDK 详细日志 | — | +| `-v, --verbose` | 仅打印提示:日志级别是编译期的(`-DAGENTIC_KIT_LOG_LEVEL=N`),默认构建已全开 | — | 运行成功后,控制台输出示例: diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/architecture.mdx b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/architecture.mdx index 44b4e41..8b1d872 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/architecture.mdx +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/architecture.mdx @@ -61,6 +61,7 @@ These low-level foundational libraries are shared by the IoT and RTC protocol st Shared TLScommon/tls.cThe mbedTLS-based TLS-over-TCP transport layer shared by the IoT (MQTT / HTTP) and RTC (rtc-tcp-client) stacks Shared RNGcommon/rng.cThe single process-wide CTR-DRBG random number generator, seeded once at startup and reused throughout the SDK + Log facadecommon/log.h / common/log.cSDK-wide compile-time log ceiling and dispatch (AGENTIC_KIT_LOG_LEVEL / AGENTIC_KIT_LOG); see Compile-Time Knobs diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/atop-generic-call.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/atop-generic-call.md index 2344d3c..a41fa12 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/atop-generic-call.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/atop-generic-call.md @@ -76,6 +76,8 @@ iot_atop_request_t req = { .api = "tuya.device.schema.newest.get", .data = body }; iot_atop_response_t resp = {0}; +/* Error paths use the SDK's public log facade log_tag_* (callable from application + * code; same compile-time ceiling and dispatch as SDK logs) */ int rc = iot_atop_call(client, &req, &resp); if (rc == OPRT_OK) { if (resp.result != NULL) { @@ -83,10 +85,10 @@ if (rc == OPRT_OK) { } } else if (rc == OPRT_ATOP_BUSINESS_ERROR) { /* The request reached the cloud but was rejected — error_code explains why */ - log_error("rejected: %s (%s)", resp.error_code, resp.error_msg); + log_tag_error("app", "rejected: %s (%s)", resp.error_code, resp.error_msg); } else { /* Transport-layer failure: DNS / TLS / HTTP / decryption */ - log_error("call failed: %d", rc); + log_tag_error("app", "call failed: %d", rc); } iot_atop_response_free(client, &resp); /* Call on every path, including failures */ diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/compile-time-knobs.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/compile-time-knobs.md index 6309c08..7d36b49 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/compile-time-knobs.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/compile-time-knobs.md @@ -6,7 +6,7 @@ sidebar_position: 9 # Compile-Time Knobs -Every compile-time knob in the SDK — TAI send/receive buffers and scheduling, MQTT timeouts and packet size, the ATOP-over-HTTP buffers, the FreeRTOS task stack — 18 knobs in total — keeps its default **with the subsystem that owns it** (the single integrator-override pickup lives in `common/log.h` — why, below): the FreeRTOS task knobs live in `pal/pal_config_defaults.h`; the per-module knobs live in each module's include/ directory — `modules/iot-client/include/iot_client_config_defaults.h` (MQTT + ATOP HTTP), `modules/rtc-tcp-client/include/tai_config_defaults.h` (TAI buffers and scheduling); tuya-ble has no compile-time knobs today (why, in the quick-reference below). The complete story for each knob (units, couplings, pitfalls hit) is in the comments of the file it lives in; this page covers how to override them per product, why the mechanism is shaped this way, and gives the quick-reference tables. +Every compile-time knob in the SDK — TAI send/receive buffers and scheduling, MQTT timeouts and packet size, the ATOP-over-HTTP buffers, the FreeRTOS task stack, log levels (one SDK-wide plus one per module) — 22 knobs in total — keeps its default **with the subsystem that owns it**: the SDK-wide log ceiling `AGENTIC_KIT_LOG_LEVEL` lives in `common/log.h` (that header also carries the single integrator-override pickup — why, below); the FreeRTOS task knobs live in `pal/pal_config_defaults.h`; the per-module knobs live in each module's include/ directory — `modules/iot-client/include/iot_client_config_defaults.h` (MQTT + ATOP HTTP + the module log ceiling), `modules/rtc-tcp-client/include/tai_config_defaults.h` (TAI buffers, scheduling and the module log ceiling), `modules/tuya-ble/include/tuya_ble_config_defaults.h` (the module log ceiling; tuya-ble's only knob today — why, in the quick-reference below). The complete story for each knob (units, couplings, pitfalls hit) is in the comments of the file it lives in; this page covers how to override them per product, why the mechanism is shaped this way, and gives the quick-reference tables. ## Three Ways to Override (Pick One) {#三种覆盖方式任选其一} @@ -18,6 +18,8 @@ Write only the knobs you want to change (plain `#define`, no `#ifndef`), and put /* agentic_kit_config.h — only what you change; the rest follows SDK defaults */ #define AGENTIC_KIT_RESPONSE_BUFFER_SIZE 8192 /* the product's DP schema is large */ #define AGENTIC_KIT_TAI_FRAG_BUF_SIZE 16000U /* ESP32 without PSRAM: shrink the RX reassembly buffer */ +#define AGENTIC_KIT_LOG_LEVEL 2 /* keep error+warn only in production; the cost is in the log section below */ +#define AGENTIC_KIT_TAI_LOG_LEVEL 0 /* with the SDK at 2, also silence rtc-tcp-client entirely (its ERROR/WARN lines too; lower-only, see below) */ ``` ESP-IDF project (SDK compiled as a component): put the file anywhere in your project and add one line so the component can see it — @@ -39,7 +41,8 @@ Cheapest when touching one or two knobs, or when a CI matrix switches them: ```cmake target_compile_definitions(my_sdk_target PRIVATE - AGENTIC_KIT_RESPONSE_BUFFER_SIZE=8192) + AGENTIC_KIT_RESPONSE_BUFFER_SIZE=8192 + AGENTIC_KIT_LOG_LEVEL=2) ``` ### Option 3: `AGENTIC_KIT_USER_CONFIG` names an arbitrary file {#方式三-agentic_kit_user_config-指定任意文件名} @@ -58,7 +61,7 @@ Knobs are compile-time constants that directly determine struct layouts and buff ## Why It Is Designed This Way {#为什么这样设计} -**Why the defaults are distributed across subsystems while the override pickup is in one place.** These defaults used to be scattered at their call sites, and buffer sizes are really **product properties** (how large the schema is, whether there is PSRAM, the audio frame length) — choosing them means going over the memory budget item by item, and a production incident review needs to answer at a glance "which knob owns this memory and why that value". Now each default lives with its owning subsystem — the FreeRTOS task in `pal/pal_config_defaults.h`, module knobs in each module's include/ — so budget reviews and code reviews look at the owning file. The **pickup logic** for integrator overrides, though, exists in exactly one place, `common/log.h`: every SDK translation unit includes that header, and every `*_config_defaults.h` includes it first, so by the time any `#ifndef` default takes effect your override is already in place — one `agentic_kit_config.h` moves all 18 knobs, with no need to split overrides per subsystem. +**Why the defaults are distributed across subsystems while the override pickup is in one place.** These defaults used to be scattered at their call sites, and buffer sizes are really **product properties** (how large the schema is, whether there is PSRAM, the audio frame length) — choosing them means going over the memory budget item by item, and a production incident review needs to answer at a glance "which knob owns this memory and why that value". Now each default lives with its owning subsystem — logs in `common/log.h`, the FreeRTOS task in `pal/pal_config_defaults.h`, module knobs in each module's include/ — so budget reviews and code reviews look at the owning file. The **pickup logic** for integrator overrides, though, exists in exactly one place, `common/log.h`: every SDK translation unit includes that header, and every `*_config_defaults.h` includes it first, so by the time any `#ifndef` default takes effect your override is already in place — one `agentic_kit_config.h` moves all 22 knobs, with no need to split overrides per subsystem. **Why every knob carries the `AGENTIC_KIT_` prefix.** The name collision is not hypothetical: coreMQTT's bundled `core_mqtt_config_defaults.h` defines a same-named `MQTT_SEND_TIMEOUT_MS` (default 20000U) that fought the SDK's 2000U by include order, and `LOG_LEVEL` is claimed by several platform SDKs. The prefix moves these names into the SDK's own namespace — your `-D` no longer hits someone else's macro, and theirs no longer hits yours. @@ -66,9 +69,72 @@ Knobs are compile-time constants that directly determine struct layouts and buff **Why `#ifndef` defaults plus including your file first, instead of letting you edit the SDK files.** You never touch SDK sources, so upgrades merge cleanly; knobs your file doesn't mention automatically follow the SDK defaults. +## Logging: a Compile-Time Gate, Single Layer {#日志编译期闸门单层} + +`AGENTIC_KIT_LOG_LEVEL` is the global log switch for every SDK module compiled from source (the prebuilt rtc-client is the exception — its logs go through its own runtime interface, see §3.5 of its reference page), and it only acts at compile time: **0 = off entirely, 1 = error, 2 = +warn, 3 = +info, 4 = +debug (default)**. Log lines above the ceiling vanish at compile time — no function call, no argument evaluation, and the format strings never enter the firmware (a direct flash/RAM saving). Lines below the ceiling emit unconditionally. **There is no runtime level: what compiles in is what prints.** + +When modules need different levels (iot-client at info while rtc-tcp-client stays at debug, say), use the **per-module ceilings**: `AGENTIC_KIT_IOT_LOG_LEVEL`, `AGENTIC_KIT_TAI_LOG_LEVEL` and `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL` all default to the SDK-wide ceiling and can only **lower their one module** further — the effective ceiling is the smaller of the two, so a value above `AGENTIC_KIT_LOG_LEVEL` has no effect (the excess is clamped back to the global value in the module's config file, so block-level gates see the effective ceiling). They gate each module's vocabulary where it is defined (`IOT_LOG*` / `TAI_LOG*` / `TUYA_BLE_HAL_LOG*`, plus rtc-tcp-client's packet-log formatter) and ride the same override pickup as the global ceiling. The example above — iot at info, rtc at debug — is the default ceiling of 4 plus one line, `-DAGENTIC_KIT_IOT_LOG_LEVEL=3`. Note that the ceiling applies at the level a line actually emits at: tuya-ble's `TUYA_BLE_HAL_LOGI` dispatches at debug, so keeping it takes a 4, not a 3. + +Log volume is therefore a build decision: compile production firmware with `-DAGENTIC_KIT_LOG_LEVEL=2` and everything beyond error + warn (code and strings alike) stays out of the image; keep the default 4 in development builds for full logs. The runtime layer is removed wholesale (`log_set_level()`/`log_get_level()`/`tai_set_log_level()` are gone) — the level has no second switch. **Where lines land is a build decision too**: the default destination is stderr; define `AGENTIC_KIT_LOG` (next section) and every line dispatches into your own macro — the "shape output at runtime" cases (capturing or quieting in a test, say) become a mode inside your macro's target function. The SDK holds no log state of any kind. + +> **Migration**: the old per-module `-DTAI_LOG_LEVEL=N` is now `-DAGENTIC_KIT_TAI_LOG_LEVEL=N` (still scoped to rtc-tcp-client, with a semantic change: it can only lower the module below the SDK-wide ceiling; to quiet the whole SDK use `-DAGENTIC_KIT_LOG_LEVEL=N`). Code that called `log_set_level(N)` at boot: drop the call, compile with `-DAGENTIC_KIT_LOG_LEVEL=N` instead, or filter by level inside your `AGENTIC_KIT_LOG` target. Code that installed a handler with `log_set_handler()`: make that function the `AGENTIC_KIT_LOG` target — it now receives the level and the bare tag directly, no need to strip the tag prefix out of the format string anymore. + +### Remapping the Dispatch: AGENTIC_KIT_LOG {#改写日志分发agentic_kit_log} + +Where logs go has no runtime switch: you decide it at compile time by defining `AGENTIC_KIT_LOG` in the same override file as your knobs, and every SDK log line dispatches into your own macro. That is precisely the road into macro-based logging systems (ESP-IDF's `ESP_LOGx`, Zephyr's `LOG_*`) — macro to macro, no `va_list` in between (a `va_list` cannot be forwarded to a macro, so any function-bridge design degrades into `vsnprintf` into a buffer, then feeding it back in with `"%s"`: an extra copy, an extra truncation point, and the target macro's format checking lost along the way): + +```c +/* agentic_kit_config.h — same file, same pickup as the knobs; + * level is the SDK's 1-4 integer, esp_log_level_t needs a mapping */ +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + ESP_LOG_LEVEL_LOCAL((level) == LOG_ERROR ? ESP_LOG_ERROR : \ + (level) == LOG_WARN ? ESP_LOG_WARN : \ + (level) == LOG_INFO ? ESP_LOG_INFO : \ + ESP_LOG_DEBUG, \ + tag, fmt, ##__VA_ARGS__) +``` + +Every log macro in the SDK (iot-client's `IOT_LOG*`, `TAI_LOG*`, `TUYA_BLE_HAL_LOG*`) funnels into `log_tag_*`, which dispatches through `AGENTIC_KIT_LOG` — one definition takes over all of it. You receive the **level, the bare tag, a printf format and its arguments**: the tag as its own token is what makes structured sinks and per-tag filtering possible. The actual values: iot-client always `"iot"`, tuya-ble always `"ble"`, rtc-tcp-client per source file (`"client"`/`"transport"`/`"proto"`/`"crypto"`/`"pkt"`), plus `"mqtt"`/`"http"`/`"tls"`/`"rng"`/`"pal"` from the common and PAL layers. + +Two rules, binding both directions: + +- **The ceiling still gates**: lines compiled out by `AGENTIC_KIT_LOG_LEVEL` cannot be resurrected by a remap (`log_tag_*` expand to `((void)0)` above the ceiling); +- **The remap must reach every target that compiles SDK sources** — the same iron rule as the knobs. There is no runtime dispatch to fall back on and no second switch: what compiles in is what prints. The default expansion keeps `log_emit`'s `format(printf)` compile-time checking; your own macro opts out unless you re-add the attribute. If your target is a function rather than a macro, it can call `log_emit_valist()` (the facade's `va_list` entry, the `esp_log_writev` role) to reuse the default stderr output instead of re-implementing the formatting. + +The full shape of a function sink — note that `log_emit_valist()` **takes no tag argument**: fold the tag into the format string in your macro, and the output keeps its `[tag]` prefix as the default expansion does: + +```c +/* agentic_kit_config.h */ +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + my_sink(level, "[" tag "] " fmt, ##__VA_ARGS__) + +/* your code (also needs #include ) */ +void my_sink(log_level_t level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + log_emit_valist(level, fmt, ap); + va_end(ap); +} +``` + ## Knob Quick Reference {#旋钮速查} -Defaults and detailed rationale live in each config file's comments; "when to adjust" is the most common scenario hint. Where each table lives: the PAL table in `pal/pal_config_defaults.h`; both iot-client tables in `modules/iot-client/include/iot_client_config_defaults.h`; the TAI table in `modules/rtc-tcp-client/include/tai_config_defaults.h`. +Defaults and detailed rationale live in each config file's comments; "when to adjust" is the most common scenario hint. Where each table lives: the SDK-wide log ceiling defaults in `common/log.h`, the per-module log ceilings in each module's config file (see the file list at the top of this page); the PAL table in `pal/pal_config_defaults.h`; all three iot-client tables in `modules/iot-client/include/iot_client_config_defaults.h`; the TAI table in `modules/rtc-tcp-client/include/tai_config_defaults.h`; the tuya-ble table in `modules/tuya-ble/include/tuya_ble_config_defaults.h`. + +### SDK-Wide {#全-sdk} + +| Knob | Default | When to adjust | +|------|------|---------| +| `AGENTIC_KIT_LOG_LEVEL` | 4 (debug) | Lower to 1–2 in production (single compile-time layer, see above) | + +### Per-Module Log Ceilings {#每模块日志上限} + +| Knob | Default | When to adjust | +|------|------|---------| +| `AGENTIC_KIT_IOT_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | Lower iot-client alone (lower-only, see the log section above) | +| `AGENTIC_KIT_TAI_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | Lower rtc-tcp-client alone; below 3 the packet-log formatter goes too | +| `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL` | = `AGENTIC_KIT_LOG_LEVEL` | Lower tuya-ble alone; `TUYA_BLE_HAL_LOGI` dispatches at debug, keeping it takes a 4 | ### iot-client: MQTT {#iot-client-mqtt} @@ -114,6 +180,8 @@ All knobs now carry the `AGENTIC_KIT_` prefix (the collision backstory is above) | Old name | New name | |------|------| +| `LOG_LEVEL` | `AGENTIC_KIT_LOG_LEVEL` | +| `TAI_LOG_LEVEL` | `AGENTIC_KIT_TAI_LOG_LEVEL` (still scoped to rtc-tcp-client, and lower-only below the SDK-wide ceiling; for the whole SDK use `AGENTIC_KIT_LOG_LEVEL`) | | `MQTT_MAX_PACKET_SIZE` | `AGENTIC_KIT_MQTT_MAX_PACKET_SIZE` | | `MQTT_SEND_TIMEOUT_MS` | `AGENTIC_KIT_MQTT_SEND_TIMEOUT_MS` | | `MQTT_RECV_TIMEOUT_MS` | `AGENTIC_KIT_MQTT_RECV_TIMEOUT_MS` | @@ -136,9 +204,9 @@ All knobs now carry the `AGENTIC_KIT_` prefix (the collision backstory is above) ## Names Deliberately Outside the Config Files {#这些名字故意不在-config-文件里} - **`TUYA_BLE_HAL_LOGI/LOGW/LOGE/HEXDUMP`** — defined in `modules/tuya-ble/include/tuya_ble_prov.h`; they are the public header's port-binding contract, overridden by the port before inclusion. -- **`TUYA_BLE_RX_BUF_SIZE` / `TUYA_BLE_TX_BUF_SIZE` / `TUYA_BLE_TX_QUEUE_DEPTH`** — also in `tuya_ble_prov.h`, but for a different reason: they determine the layout of the public struct `tuya_ble_prov_state_t`, and ports size their own buffers against them, which makes them part of the port API; changing them changes a struct layout that ports must recompile and re-size against (the header itself declares the layout not ABI-stable) — they are not build knobs. This is why tuya-ble has no compile-time knobs today (the full story sits in the `tuya_ble_prov.h` header comment; when one appears it will be named `AGENTIC_KIT_TUYA_BLE_*` in `modules/tuya-ble/include/tuya_ble_config_defaults.h`). +- **`TUYA_BLE_RX_BUF_SIZE` / `TUYA_BLE_TX_BUF_SIZE` / `TUYA_BLE_TX_QUEUE_DEPTH`** — also in `tuya_ble_prov.h`, but for a different reason: they determine the layout of the public struct `tuya_ble_prov_state_t`, and ports size their own buffers against them, which makes them part of the port API; changing them changes a struct layout that ports must recompile and re-size against (the header itself declares the layout not ABI-stable) — they are not build knobs. tuya-ble therefore has exactly one compile-time knob — the module log ceiling `AGENTIC_KIT_TUYA_BLE_LOG_LEVEL`, living in `modules/tuya-ble/include/tuya_ble_config_defaults.h` under the naming convention above (the full story on the geometry/layout constants sits in the `tuya_ble_prov.h` header comment). - **`IOT_SDK_SW_VER` / `PV` / `BV` and the per-region ATOP hosts** — `modules/iot-client/src/iot_internal.h`; release-managed values, not build knobs (an internal header, split from the knobs, not riding the override mechanism). -- **coreMQTT / coreHTTP log routing** — `common/core_mqtt_config.h`, `common/core_http_config.h`; they route coreMQTT/coreHTTP internal logs into the global log facade, they are not build knobs. +- **coreMQTT / coreHTTP log routing** — `common/core_mqtt_config.h`, `common/core_http_config.h`; the routed log lines still pass the `AGENTIC_KIT_LOG_LEVEL` gate, nothing to tune separately. ## How to Confirm an Override Took Effect {#怎么确认覆盖生效了} @@ -156,7 +224,7 @@ All knobs now carry the `AGENTIC_KIT_` prefix (the collision backstory is above) cc -I/modules/iot-client/include -I/common -I -c probe.c # quiet pass = effective ``` -**Behavioral observation.** When an ATOP response overflows, the error log states the current buffer size and suggests the matching `-D` (the default handler's output shape is `HH:MM:SS [E] [module tag]`; `(server said …)` is the HTTP status — the server usually already returned 200 successfully, which is exactly the misunderstanding this line exists to clear): +**Behavioral observation.** When an ATOP response overflows, the error log states the current buffer size and suggests the matching `-D` (the default output shape is `HH:MM:SS [E] [module tag]`; `(server said …)` is the HTTP status — the server usually already returned 200 successfully, which is exactly the misunderstanding this line exists to clear): ```text 14:13:15 [E] [iot] HTTP response does not fit: need 6558 B body + 300 B headers, buffer is 4096 B (server said 200). Rebuild with a larger -DAGENTIC_KIT_RESPONSE_BUFFER_SIZE. diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/porting-to-new-platform.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/porting-to-new-platform.md index 5d7947b..cb1a142 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/porting-to-new-platform.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/guides/porting-to-new-platform.md @@ -147,7 +147,7 @@ CONFIG_FREERTOS_HZ=1000 ## General Considerations {#通用注意事项} -- Defaults and documentation for every SDK compile-time knob (TAI buffers and scheduling, MQTT timeouts and packet size, ATOP HTTP buffers, the FreeRTOS task stack, log levels) live with the subsystem that owns them: the log ceiling and the single integrator-override pickup live in `common/log.h` (every SDK translation unit includes it), the FreeRTOS task knobs in `pal/pal_config_defaults.h`, and the per-module knobs in each module's include/ directory — `modules/iot-client/include/iot_client_config_defaults.h`, `modules/rtc-tcp-client/include/tai_config_defaults.h` (tuya-ble has no compile-time knobs today). To override per product, pick one (see the banner in `common/log.h`; the full guide is [Compile-Time Knobs](./compile-time-knobs)): create your own `agentic_kit_config.h` (only the `#define`s you want to change — one file regardless of which subsystem a knob belongs to), add its directory to the include path of every target that compiles SDK sources (CMake: `target_include_directories( PRIVATE )`) and it is picked up automatically; or keep using `-D=`; or use `-DAGENTIC_KIT_USER_CONFIG='"my_opts.h"'` to name an arbitrary file (for toolchains without `__has_include`). Note: knob values must stay identical for every target that compiles SDK sources +- Defaults and documentation for every SDK compile-time knob (TAI buffers and scheduling, MQTT timeouts and packet size, ATOP HTTP buffers, the FreeRTOS task stack, log levels) live with the subsystem that owns them: the log ceiling and the single integrator-override pickup live in `common/log.h` (every SDK translation unit includes it), the FreeRTOS task knobs in `pal/pal_config_defaults.h`, and the per-module knobs in each module's include/ directory — `modules/iot-client/include/iot_client_config_defaults.h`, `modules/rtc-tcp-client/include/tai_config_defaults.h`, `modules/tuya-ble/include/tuya_ble_config_defaults.h` (tuya-ble has exactly one knob today: its module log ceiling). To override per product, pick one (see the banner in `common/log.h`; the full guide is [Compile-Time Knobs](./compile-time-knobs)): create your own `agentic_kit_config.h` (only the `#define`s you want to change — one file regardless of which subsystem a knob belongs to), add its directory to the include path of every target that compiles SDK sources (CMake: `target_include_directories( PRIVATE )`) and it is picked up automatically; or keep using `-D=`; or use `-DAGENTIC_KIT_USER_CONFIG='"my_opts.h"'` to name an arbitrary file (for toolchains without `__has_include`). Note: knob values must stay identical for every target that compiles SDK sources - PAL `thread_create` must configure a sufficiently large stack. `pal_freertos.c` defaults to `AGENTIC_KIT_PAL_FR_TASK_STACK_WORDS` (6144 words, approximately 24 KB on a 32-bit platform), which can be reduced or increased according to the platform's memory constraints. - The SDK handles TLS internally through mbedTLS and requires the correct system time for certificate verification. If no CA certificate is provided, the TLS connection may fall back to a mode that does not verify certificates. - `tcp_recv` should support blocking and timeout semantics; the background thread calls it repeatedly. diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/iot-client.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/iot-client.md index 4b00afc..a617232 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/iot-client.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/iot-client.md @@ -72,7 +72,7 @@ These are the numbers in parentheses in logs such as `MQTT_Connect failed: MQTTS 10:15:13 [E] [iot] MQTT_Connect failed: MQTTServerRefused (6) ``` -The first three lines come from coreMQTT (`[mqtt]`), and the last comes from the SDK (`[iot]`). The default log handler adds the timestamp prefix; if the application replaces the handler through `log_set_handler()`, the prefix format depends on that implementation. +The first three lines come from coreMQTT (`[mqtt]`), and the last comes from the SDK (`[iot]`). The timestamp prefix is added by the default output; if the application takes over the dispatch by defining `AGENTIC_KIT_LOG`, the output shape is whatever that macro expands to. | CONNACK code | Message | Common cause and action | |----|------|------| @@ -123,7 +123,7 @@ Classification for cloud device-removal notifications (protocol 11), received by ### Log Level (`log_level_t`) {#log-levellog_level_t} -Logging is controlled through the global logging facade in `common/log.h`. Use `log_set_level()` to set the runtime level and `log_set_handler()` to customize output. +Logging is controlled through the global logging facade in `common/log.h`: log volume is decided by the compile-time `AGENTIC_KIT_LOG_LEVEL`, with `AGENTIC_KIT_IOT_LOG_LEVEL` lowering this module alone (lower-only, defaulting to the SDK-wide value; there is no runtime level); where lines land is a compile-time decision too — define `AGENTIC_KIT_LOG` to dispatch every line into your own macro, which may print or drop by level. Note: `log_level_t` is not an enum; it is a `typedef int` (so `LOG_*` can be used in preprocessor `#if` conditions). The names in the following table are macros. @@ -494,11 +494,13 @@ Obtains the CA certificate for the target host. The IoT Client uses the global logging facade provided by `common/log.h` and no longer provides a separate API for configuring a log callback. ```c -#include "log.h" - -// Set the runtime log level -log_set_level(LOG_INFO); - -// Set a custom log output handler -log_set_handler(my_log_handler); +// Log volume is a compile-time decision (-DAGENTIC_KIT_LOG_LEVEL=N; +// -DAGENTIC_KIT_IOT_LOG_LEVEL=N lowers this module alone); +// there is no runtime level + +// The destination is a compile-time decision too: in your +// agentic_kit_config.h, remap the dispatch into your own macro +// (print or drop by level inside it, as you like) +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + my_log(level, tag, fmt, ##__VA_ARGS__) ``` diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-client.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-client.md index bdff06e..b30ed20 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-client.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-client.md @@ -134,6 +134,8 @@ stm_ret stm_open_set_log_level(stm_log_level_e level); | 5 | `STM_LOG_LEVEL_FATAL` | Fatal | | 6 | `STM_LOG_LEVEL_NONE` | No output | +Note: rtc-client is a prebuilt library — its logs go through the `on_log` callback set at init, and the level is controlled by this runtime API, outside the SDK's compile-time `AGENTIC_KIT_LOG_LEVEL` / `AGENTIC_KIT_LOG` facade (modules compiled from source go through the facade; see Compile-Time Knobs). + --- ## 4. Session Management {#4-会话管理} diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-tcp-client.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-tcp-client.md index 4e1cdf3..6850265 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-tcp-client.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/reference/rtc-tcp-client.md @@ -529,22 +529,19 @@ Sends the response to an MCP command. When `on_event` receives `TAI_EVT_MCP_CMD` ## 6. Logging {#6-日志} -```c -static inline void tai_set_log_level(int level); -static inline int tai_get_log_level(void); -``` - -Sets/gets the runtime log level. Valid values: +The SDK's global log switch is the compile-time `AGENTIC_KIT_LOG_LEVEL` (gated once in `common/log.h`; it applies to the entire SDK, not only this module); this module can additionally be lowered alone with `AGENTIC_KIT_TAI_LOG_LEVEL` (the renamed former `TAI_LOG_LEVEL` — lower-only, defaulting to the SDK-wide value). Valid values: | Value | Meaning | |----|------| -| 0 | Disable all logging | +| 0 | Off entirely | | 1 | `TAI_LOG_ERROR` | | 2 | `TAI_LOG_WARN` | -| 3 | `TAI_LOG_INFO` (runtime default) | -| 4 | `TAI_LOG_DEBUG` | +| 3 | `TAI_LOG_INFO` | +| 4 | `TAI_LOG_DEBUG` (default) | + +Log lines above the ceiling are eliminated at compile time; below it they emit unconditionally — there is no runtime level (`tai_set_log_level()` was removed together with the runtime layer). Compile production builds with `-DAGENTIC_KIT_LOG_LEVEL=2` to keep only error + warn, or lower only this module with `-DAGENTIC_KIT_TAI_LOG_LEVEL=N` (below 3, the `tai_log_packet` JSON formatter and its calls vanish together); changing where lines go (or dropping them by level) is a build decision as well: define `AGENTIC_KIT_LOG` and take over the dispatch with your own macro. -The runtime default level is `TAI_LOG_INFO` (3). Explicitly call `tai_set_log_level(4)` when DEBUG output is required. The maximum compiled log level can be set at compile time by defining the `TAI_LOG_LEVEL` macro (default 4); logs above that level are eliminated during compilation. +Media middle frames log at INFO with 1/N sampling by default (`AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N`, default 50; set it to 0 to disable sampling and enter flood mode). In flood mode middle frames emit at DEBUG and only when the effective ceiling is 4 — with a ceiling of 3, setting sampling to 0 leaves middle frames with **no logs at all** (fewer than the default sampling; do not mistake it for packet loss). --- diff --git a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/tutorials/agent-trigger.md b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/tutorials/agent-trigger.md index 41f8b4a..eefb4f6 100644 --- a/docs-site/i18n/en/docusaurus-plugin-content-docs/current/tutorials/agent-trigger.md +++ b/docs-site/i18n/en/docusaurus-plugin-content-docs/current/tutorials/agent-trigger.md @@ -254,7 +254,7 @@ All options: | `--timeout S` | Timeout in seconds while waiting for pushes | `120` | | `--repeat N` | Exit after N pushes; `0` = listen indefinitely | `1` | | `--audio FILE` | TTS audio output file; an empty string discards the audio | `output_trigger_tts.pcm` | -| `-v, --verbose` | Enable detailed SDK logs | — | +| `-v, --verbose` | Prints a notice only: the SDK log level is compile-time (`-DAGENTIC_KIT_LOG_LEVEL=N`); the default build already logs everything | — | Example console output from a successful run (literal output, including the Chinese generated message): diff --git a/examples/esp-idf/components/agentic_kit/CMakeLists.txt b/examples/esp-idf/components/agentic_kit/CMakeLists.txt index 55ebc95..7273f1e 100644 --- a/examples/esp-idf/components/agentic_kit/CMakeLists.txt +++ b/examples/esp-idf/components/agentic_kit/CMakeLists.txt @@ -71,14 +71,20 @@ idf_component_register( # No MQTT_DO_NOT_USE_CUSTOM_CONFIG: coreMQTT must pick up common/core_mqtt_config.h # here too, or a refused CONNECT logs only a bare MQTTServerRefused on exactly the # target this diagnostic exists for. ${AK_DIR}/common is already in INCLUDE_DIRS -# above, so common/log.h (the override pickup) resolves with +# above, so common/log.h (the override pickup + log-ceiling default) resolves with # no further wiring; knob defaults resolve the same way from each module's include/ # dir and ${AK_DIR}/pal listed above. # -# Per-product knob overrides (optional): create agentic_kit_config.h holding only -# the #defines you want to change, put it anywhere in your project, and point this -# component at its directory (must reach EVERY target that compiles SDK sources): -# target_include_directories(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/../../kit_opts") +# Per-product knob overrides: kit_opts/agentic_kit_config.h next door is +# picked up by common/log.h's override search via the include path below +# (PRIVATE is enough: only this component's TUs compile SDK sources, and +# the app's own TUs are covered by the component's public INCLUDE_DIRS +# reaching them -- the header is only consumed through common/log.h). +# It currently holds one override: AGENTIC_KIT_LOG, remapping the SDK's +# log dispatch into ESP-IDF logging (esp_log.h), macro-to-macro. +target_include_directories(${COMPONENT_LIB} PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/kit_opts" +) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format diff --git a/examples/esp-idf/components/agentic_kit/kit_opts/agentic_kit_config.h b/examples/esp-idf/components/agentic_kit/kit_opts/agentic_kit_config.h new file mode 100644 index 0000000..2e727d6 --- /dev/null +++ b/examples/esp-idf/components/agentic_kit/kit_opts/agentic_kit_config.h @@ -0,0 +1,30 @@ +/* + * agentic_kit_config.h -- per-product knob overrides for the agentic_kit + * ESP-IDF component. Picked up automatically by common/log.h's override + * search (this directory is on the component's include path), BEFORE any + * SDK-side #ifndef default -- plain #defines only, no #ifndef here. + * + * The one override this project needs: the SDK's log facade defaults to + * stderr, which goes nowhere on ESP-IDF. AGENTIC_KIT_LOG remaps every + * SDK log line (all modules -- iot, tai, ble) straight into ESP-IDF + * logging, macro-to-macro: no va_list round trip, and each line keeps + * its real module tag instead of one shared "tuya_ble" tag. The + * AGENTIC_KIT_LOG_LEVEL ceiling still applies upstream, so lines above + * the ceiling never reach this macro at all. + */ +#ifndef AGENTIC_KIT_CONFIG_H +#define AGENTIC_KIT_CONFIG_H + +#include "esp_log.h" + +/* level arrives as LOG_ERROR..LOG_DEBUG (1..4); map onto esp_log_level_t. + * ESP_LOG_LEVEL_LOCAL keeps its own runtime check against the local + * verbosity, same as the old callback's ESP_LOG_LEVEL_LOCAL call. */ +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + ESP_LOG_LEVEL_LOCAL((level) == LOG_ERROR ? ESP_LOG_ERROR : \ + (level) == LOG_WARN ? ESP_LOG_WARN : \ + (level) == LOG_INFO ? ESP_LOG_INFO : \ + ESP_LOG_DEBUG, \ + tag, fmt, ##__VA_ARGS__) + +#endif /* AGENTIC_KIT_CONFIG_H */ diff --git a/examples/esp-idf/pair/pair-by-ble/main/main.c b/examples/esp-idf/pair/pair-by-ble/main/main.c index 94b09ef..a7fef4b 100644 --- a/examples/esp-idf/pair/pair-by-ble/main/main.c +++ b/examples/esp-idf/pair/pair-by-ble/main/main.c @@ -23,7 +23,6 @@ #include "nvs_flash.h" #include "esp_crt_bundle.h" #include "app_config.h" -#include "log.h" #include "tuya_ble_nimble.h" #include "iot_client.h" #include "pal.h" @@ -151,34 +150,6 @@ static void on_tuya_ble_prov_complete(const tuya_ble_wifi_creds_t *creds) xEventGroupSetBits(s_prov_event_group, PROV_DONE_BIT); } -static void iot_log_callback(log_level_t level, const char *fmt, va_list args) -{ - esp_log_level_t esp_level = ESP_LOG_INFO; - - switch (level) { - case LOG_ERROR: - esp_level = ESP_LOG_ERROR; - break; - case LOG_WARN: - esp_level = ESP_LOG_WARN; - break; - case LOG_INFO: - esp_level = ESP_LOG_INFO; - break; - case LOG_DEBUG: - esp_level = ESP_LOG_DEBUG; - break; - default: - break; - } - - /* esp_log_writev does not append a newline; the facade's messages have - * none of their own, so add one or every line runs into the next. */ - char line[256]; - vsnprintf(line, sizeof(line), fmt, args); - ESP_LOG_LEVEL_LOCAL(esp_level, "tuya_ble", "%s", line); -} - static esp_err_t activate_device_with_ble_token(void) { iot_on_boarding_config_t ob_config = { @@ -218,11 +189,11 @@ void app_main(void) } ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(iot_init_default() == OPRT_OK ? ESP_OK : ESP_FAIL); - /* The SDK's log facade defaults to LOG_INFO and its default handler - * writes to stderr (nowhere on ESP-IDF); route it through ESP logging - * and raise the ceiling so [ble] protocol DEBUG lines are visible. */ - log_set_handler(iot_log_callback); - log_set_level(LOG_DEBUG); + /* SDK log lines already land in ESP-IDF logging: the agentic_kit + * component compiles with kit_opts/agentic_kit_config.h on its include + * path, whose AGENTIC_KIT_LOG remap routes the facade's dispatch into + * ESP_LOGx. The compile-time ceiling defaults to 4, so [ble] protocol + * DEBUG lines are already visible. */ printf("\n"); printf("╔════════════════════════════════════════════════════════════╗\n"); diff --git a/examples/posix/ai/rtc-tcp-client/agent_trigger_demo.c b/examples/posix/ai/rtc-tcp-client/agent_trigger_demo.c index 5f25285..a483718 100644 --- a/examples/posix/ai/rtc-tcp-client/agent_trigger_demo.c +++ b/examples/posix/ai/rtc-tcp-client/agent_trigger_demo.c @@ -195,7 +195,7 @@ static void usage(const char *argv0) "\n" "Other:\n" " -a, --agent-code CODE agent code (default: product's default agent)\n" -" -v, --verbose enable SDK debug logging\n" +" -v, --verbose logs are compile-time: -DAGENTIC_KIT_LOG_LEVEL=N\n" " -h, --help this text\n", argv0, DEFAULT_DEVID, region_name(DEFAULT_REGION), env_name(DEFAULT_ENV), DEFAULT_BATTERY_DP, @@ -826,7 +826,11 @@ int main(int argc, char **argv) fprintf(stderr, "iot_init_default failed\n"); return 1; } - log_set_level(o.verbose ? LOG_DEBUG : LOG_WARN); /* both SDKs share the facade */ + /* Log volume is decided at compile time now; the default ceiling (4) + * emits everything. -v cannot change it anymore, so just say so. */ + if (o.verbose) + fprintf(stderr, "-v: SDK log level is compile-time " + "(-DAGENTIC_KIT_LOG_LEVEL=N); this build is already verbose\n"); iot_client_config_t iot_cfg = { .region = DEFAULT_REGION, diff --git a/examples/posix/ai/rtc-tcp-client/mcp_demo.c b/examples/posix/ai/rtc-tcp-client/mcp_demo.c index 41f299f..aff7de7 100644 --- a/examples/posix/ai/rtc-tcp-client/mcp_demo.c +++ b/examples/posix/ai/rtc-tcp-client/mcp_demo.c @@ -438,7 +438,8 @@ int main(int argc, char *argv[]) tai_ctx_t *ctx = tai_ctx_init(ctx_buf, &tai_cfg); if (!ctx) { fprintf(stderr, "tai_ctx_init failed\n"); pal->free(ctx_buf); return 1; } - tai_set_log_level(TAI_LOG_WARN); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=2 to restore the warn-only volume. */ /* ---- 5-7. Connect, serve MCP — with app-driven reconnect ----------- * diff --git a/examples/posix/ai/rtc-tcp-client/music_play_demo.c b/examples/posix/ai/rtc-tcp-client/music_play_demo.c index 51a9c16..57ae0bd 100644 --- a/examples/posix/ai/rtc-tcp-client/music_play_demo.c +++ b/examples/posix/ai/rtc-tcp-client/music_play_demo.c @@ -505,7 +505,8 @@ int main(int argc, char *argv[]) tai_ctx_t *ctx = tai_ctx_init(ctx_buf, &tai_cfg); if (!ctx) { fprintf(stderr, "tai_ctx_init failed\n"); pal->free(ctx_buf); return 1; } - tai_set_log_level(TAI_LOG_WARN); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=2 to restore the warn-only volume. */ /* ---- 5-7. Connect, send, await response — with app-driven reconnect -- */ int done = 0; diff --git a/examples/posix/ai/rtc-tcp-client/text_chat_demo.c b/examples/posix/ai/rtc-tcp-client/text_chat_demo.c index fb22206..56dc9e8 100644 --- a/examples/posix/ai/rtc-tcp-client/text_chat_demo.c +++ b/examples/posix/ai/rtc-tcp-client/text_chat_demo.c @@ -189,7 +189,8 @@ int main(int argc, char *argv[]) tai_ctx_t *ctx = tai_ctx_init(ctx_buf, &tai_cfg); if (!ctx) { fprintf(stderr, "tai_ctx_init failed\n"); pal->free(ctx_buf); return 1; } - tai_set_log_level(TAI_LOG_WARN); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=2 to restore the warn-only volume. */ /* ---- 5-7. Connect, send, await response — with app-driven reconnect -- * diff --git a/examples/posix/dp-management/dp_management_demo.c b/examples/posix/dp-management/dp_management_demo.c index 5612729..232fa34 100644 --- a/examples/posix/dp-management/dp_management_demo.c +++ b/examples/posix/dp-management/dp_management_demo.c @@ -291,7 +291,8 @@ int demo_dp_management_run(const char *devid, fprintf(stderr, "[%s] iot_init_default failed\n", TAG); return -1; } - log_set_level(LOG_INFO); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=3 to restore the info-only volume. */ signal(SIGINT, on_signal); signal(SIGTERM, on_signal); diff --git a/examples/posix/ota-confirm/ota_confirm_demo.c b/examples/posix/ota-confirm/ota_confirm_demo.c index a917db8..07c2cf8 100644 --- a/examples/posix/ota-confirm/ota_confirm_demo.c +++ b/examples/posix/ota-confirm/ota_confirm_demo.c @@ -154,7 +154,8 @@ int demo_ota_confirm_run(const char *devid, const char *secret_key, fprintf(stderr, "[%s] iot_init_default failed\n", TAG); return 1; } - log_set_level(LOG_INFO); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=3 to restore the info-only volume. */ signal(SIGINT, on_signal); signal(SIGTERM, on_signal); diff --git a/examples/posix/ota-demo/ota_demo.c b/examples/posix/ota-demo/ota_demo.c index 1ae4741..14131e2 100644 --- a/examples/posix/ota-demo/ota_demo.c +++ b/examples/posix/ota-demo/ota_demo.c @@ -135,7 +135,8 @@ int demo_ota_run(const char *devid, const char *secret_key, const char *local_ke fprintf(stderr, "[%s] iot_init_default failed\n", TAG); return -1; } - log_set_level(LOG_INFO); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=3 to restore the info-only volume. */ /* 1. Initialize iot_client */ iot_client_config_t cfg = { diff --git a/examples/posix/pair/api-activate/activate_demo.c b/examples/posix/pair/api-activate/activate_demo.c index 2fdad9b..1aa12ac 100644 --- a/examples/posix/pair/api-activate/activate_demo.c +++ b/examples/posix/pair/api-activate/activate_demo.c @@ -56,7 +56,8 @@ int demo_activate_run(const char *token, fprintf(stderr, "[%s] iot_init_default failed\n", TAG); return -1; } - log_set_level(LOG_DEBUG); + /* Full SDK debug output used to need log_set_level(LOG_DEBUG) here; + * the default compile-time ceiling (4) now emits it unconditionally. */ iot_on_boarding_config_t cfg = { .timeout_ms = 10000, diff --git a/examples/posix/pair/unbind-demo/unbind_demo.c b/examples/posix/pair/unbind-demo/unbind_demo.c index 0747284..a6d79cd 100644 --- a/examples/posix/pair/unbind-demo/unbind_demo.c +++ b/examples/posix/pair/unbind-demo/unbind_demo.c @@ -68,7 +68,8 @@ int demo_unbind_run(const char *devid, const char *secret_key, fprintf(stderr, "[%s] iot_init_default failed\n", TAG); return -1; } - log_set_level(LOG_INFO); + /* Log volume is decided at compile time now; build with + * -DAGENTIC_KIT_LOG_LEVEL=3 to restore the info-only volume. */ signal(SIGINT, on_signal); signal(SIGTERM, on_signal); diff --git a/modules/iot-client/include/iot_atop.h b/modules/iot-client/include/iot_atop.h index 80e0caf..52b66ec 100644 --- a/modules/iot-client/include/iot_atop.h +++ b/modules/iot-client/include/iot_atop.h @@ -49,7 +49,7 @@ * int rc = iot_atop_call(client, &req, &resp); * if (rc == OPRT_ATOP_BUSINESS_ERROR) { * // reached the cloud, cloud said no -- resp.error_code says why - * log_error("rejected: %s (%s)", resp.error_code, resp.error_msg); + * log_tag_error("app", "rejected: %s (%s)", resp.error_code, resp.error_msg); * } else if (rc == OPRT_OK) { * // resp.result is the "result" field as JSON, or NULL if it was null * my_parse(resp.result); diff --git a/modules/iot-client/include/iot_client.h b/modules/iot-client/include/iot_client.h index 753db43..ef2daa1 100644 --- a/modules/iot-client/include/iot_client.h +++ b/modules/iot-client/include/iot_client.h @@ -32,8 +32,12 @@ /* ---- Logging subsystem ---- * The IoT SDK shares the process-wide log facade (see log.h). - * To redirect output: log_set_handler(my_fn); - * To filter at runtime: log_set_level(LOG_INFO); + * To take over the dispatch (destination, format): define AGENTIC_KIT_LOG; + * a function target can reuse the default output via log_emit_valist(); + * To quiet the SDK: build with -DAGENTIC_KIT_LOG_LEVEL=N (no runtime level + * and no runtime handler -- the destination is a compile-time fact); + * to quiet only iot-client: -DAGENTIC_KIT_IOT_LOG_LEVEL=N (defaults to + * the SDK-wide ceiling; cannot exceed it). */ typedef enum { @@ -77,8 +81,8 @@ typedef struct { * @brief Initialize IoT SDK with the built-in default PAL adapter (POSIX / FreeRTOS). * * Must be called before any other SDK function. Logging is dispatched - * through the log facade — install a custom handler with - * log_set_handler() if you need non-default output. + * through the log facade — the destination is a compile-time fact: define + * AGENTIC_KIT_LOG (log.h) for non-default output. * * @return OPRT_OK on success */ diff --git a/modules/iot-client/include/iot_client_config_defaults.h b/modules/iot-client/include/iot_client_config_defaults.h index b0fdd29..67665ec 100644 --- a/modules/iot-client/include/iot_client_config_defaults.h +++ b/modules/iot-client/include/iot_client_config_defaults.h @@ -18,6 +18,27 @@ #include "log.h" +/* ========================================================================= + * iot-client: logging (src/iot_internal.h) + * ========================================================================= */ + +/* Optional per-module log ceiling. Defaults to the SDK-wide ceiling and can + * only LOWER iot-client below it: the effective ceiling is the smaller of + * the two, so a value above AGENTIC_KIT_LOG_LEVEL is clamped to it right + * below. Typical use: keep the SDK-wide ceiling open for debugging while + * silencing one chatty module. Gates the IOT_LOG* vocabulary where it is + * defined (src/iot_internal.h). */ +#ifndef AGENTIC_KIT_IOT_LOG_LEVEL +#define AGENTIC_KIT_IOT_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif +/* Lower-only, enforced ONCE here so any future block-level gate keyed on + * this knob sees the effective ceiling; the vocabulary itself already + * collapses at the facade, the clamp keeps the knob value honest. */ +#if AGENTIC_KIT_IOT_LOG_LEVEL > AGENTIC_KIT_LOG_LEVEL +#undef AGENTIC_KIT_IOT_LOG_LEVEL +#define AGENTIC_KIT_IOT_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif + /* ========================================================================= * iot-client: MQTT transport (modules/iot-client/src/mqtt.c) * ========================================================================= */ diff --git a/modules/iot-client/src/atop.c b/modules/iot-client/src/atop.c index 28675b6..922d632 100644 --- a/modules/iot-client/src/atop.c +++ b/modules/iot-client/src/atop.c @@ -35,13 +35,13 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, activite_response_t *response) { if (request == NULL || response == NULL) { - log_error("atop_activate_request: request or response is NULL"); + IOT_LOGE("atop_activate_request: request or response is NULL"); return OPRT_INVALID_PARAMETER; } #define CHECK_STR_PARAM(field) \ if (request->field == NULL || request->field[0] == '\0') { \ - log_error("atop_activate_request: invalid parameter '%s'", #field); \ + IOT_LOGE("atop_activate_request: invalid parameter '%s'", #field); \ return OPRT_INVALID_PARAMETER; \ } CHECK_STR_PARAM(token); @@ -84,7 +84,7 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a char *buffer = (char *)pal->malloc(prealloc_size); if (NULL == buffer) { - log_error("post buffer malloc fail"); + IOT_LOGE("post buffer malloc fail"); return OPRT_MALLOC_FAILED; } uint32_t timestamp = (uint32_t)time(NULL); @@ -213,7 +213,7 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a } offset += (size_t)write_len; - log_info("POST JSON:%s", buffer); + IOT_LOGI("POST JSON:%s", buffer); /* atop_base_request object construct */ atop_base_request_t atop_request = {.uuid = request->uuid, @@ -238,13 +238,13 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a rt = atop_base_request(pal, &atop_request, &atop_response); pal->free(buffer); if (OPRT_OK != rt) { - log_error("atop_base_request error:%d", rt); + IOT_LOGE("atop_base_request error:%d", rt); return rt; } cJSON *result = atop_response.result; if (result == NULL) { - log_error("activate response no result"); + IOT_LOGE("activate response no result"); atop_base_response_free(pal, &atop_response); return OPRT_COMMUNICATION_ERROR; } @@ -262,7 +262,7 @@ int atop_activate_request(const pal_t *pal, const activite_request_t *request, a response->schema = pal_strdup(pal, cJSON_GetStringValue(cJSON_GetObjectItem(result, "schema"))); if (!response->devid || !response->secret_key || !response->local_key) { - log_error("activate response missing required fields"); + IOT_LOGE("activate response missing required fields"); atop_activate_response_free(pal, response); atop_base_response_free(pal, &atop_response); return OPRT_COMMUNICATION_ERROR; @@ -298,14 +298,14 @@ void atop_activate_response_free(const pal_t *pal, activite_response_t *response int atop_device_meta_save(const pal_t *pal, const device_meta_save_request_t *request, device_meta_save_response_t *response) { if (request == NULL || response == NULL) { - log_error("atop_device_meta_save: request or response is NULL"); + IOT_LOGE("atop_device_meta_save: request or response is NULL"); return OPRT_INVALID_PARAMETER; } if (request->devid == NULL || request->devid[0] == '\0' || request->key == NULL || request->key[0] == '\0' || request->sdk_version == NULL || request->sdk_version[0] == '\0') { - log_error("atop_device_meta_save: devid, key, or sdk_version is empty"); + IOT_LOGE("atop_device_meta_save: devid, key, or sdk_version is empty"); return OPRT_INVALID_PARAMETER; } @@ -317,7 +317,7 @@ int atop_device_meta_save(const pal_t *pal, const device_meta_save_request_t *re size_t post_len = 64 + strlen(request->sdk_version) * 2; char *post_data = (char *)pal->malloc(post_len); if (post_data == NULL) { - log_error("atop_device_meta_save: malloc failed"); + IOT_LOGE("atop_device_meta_save: malloc failed"); return OPRT_MALLOC_FAILED; } int write_len = snprintf(post_data, post_len, @@ -328,7 +328,7 @@ int atop_device_meta_save(const pal_t *pal, const device_meta_save_request_t *re return OPRT_COMMUNICATION_ERROR; } - log_debug("device meta save post data:%s", post_data); + IOT_LOGD("device meta save post data:%s", post_data); atop_base_request_t atop_request = { .devid = request->devid, @@ -351,14 +351,14 @@ int atop_device_meta_save(const pal_t *pal, const device_meta_save_request_t *re pal->free(post_data); if (rt != OPRT_OK) { - log_error("atop_device_meta_save request error:%d", rt); + IOT_LOGE("atop_device_meta_save request error:%d", rt); atop_base_response_free(pal, &atop_response); return rt; } cJSON *result = atop_response.result; if (result == NULL) { - log_error("atop_device_meta_save: no result"); + IOT_LOGE("atop_device_meta_save: no result"); atop_base_response_free(pal, &atop_response); return OPRT_COMMUNICATION_ERROR; } @@ -384,12 +384,12 @@ int atop_device_meta_save(const pal_t *pal, const device_meta_save_request_t *re int atop_qrcode_info_get(const pal_t *pal, const qrcode_info_request_t *request, qrcode_info_response_t *response) { if (request == NULL || response == NULL) { - log_error("atop_qrcode_info_get: request or response is NULL"); + IOT_LOGE("atop_qrcode_info_get: request or response is NULL"); return OPRT_INVALID_PARAMETER; } if (request->authkey == NULL || request->authkey[0] == '\0') { - log_error("atop_qrcode_info_get: authkey is empty"); + IOT_LOGE("atop_qrcode_info_get: authkey is empty"); return OPRT_INVALID_PARAMETER; } @@ -399,7 +399,7 @@ int atop_qrcode_info_get(const pal_t *pal, const qrcode_info_request_t *request, cJSON *root = cJSON_CreateObject(); if (root == NULL) { - log_error("atop_qrcode_info_get: failed to create JSON object"); + IOT_LOGE("atop_qrcode_info_get: failed to create JSON object"); return OPRT_MALLOC_FAILED; } @@ -411,11 +411,11 @@ int atop_qrcode_info_get(const pal_t *pal, const qrcode_info_request_t *request, char *post_data = cJSON_PrintUnformatted(root); cJSON_Delete(root); if (post_data == NULL) { - log_error("atop_qrcode_info_get: failed to print JSON"); + IOT_LOGE("atop_qrcode_info_get: failed to print JSON"); return OPRT_MALLOC_FAILED; } - log_debug("qrcode info post data:%s", post_data); + IOT_LOGD("qrcode info post data:%s", post_data); atop_base_request_t atop_request = { .uuid = request->uuid, @@ -438,21 +438,21 @@ int atop_qrcode_info_get(const pal_t *pal, const qrcode_info_request_t *request, cJSON_free(post_data); if (rt != OPRT_OK) { - log_error("atop_qrcode_info_get request error:%d", rt); + IOT_LOGE("atop_qrcode_info_get request error:%d", rt); atop_base_response_free(pal,&atop_response); return rt; } cJSON *result = atop_response.result; if (result == NULL) { - log_error("atop_qrcode_info_get: no result"); + IOT_LOGE("atop_qrcode_info_get: no result"); atop_base_response_free(pal,&atop_response); return OPRT_COMMUNICATION_ERROR; } cJSON *short_url = cJSON_GetObjectItem(result, "shortUrl"); if (short_url == NULL || !cJSON_IsString(short_url)) { - log_error("atop_qrcode_info_get: missing short_url in result"); + IOT_LOGE("atop_qrcode_info_get: missing short_url in result"); atop_base_response_free(pal,&atop_response); return OPRT_INVALID_RESULT; } @@ -461,11 +461,11 @@ int atop_qrcode_info_get(const pal_t *pal, const qrcode_info_request_t *request, atop_base_response_free(pal,&atop_response); if (response->short_url == NULL) { - log_error("atop_qrcode_info_get: failed to allocate memory for short_url"); + IOT_LOGE("atop_qrcode_info_get: failed to allocate memory for short_url"); return OPRT_MALLOC_FAILED; } - log_debug("qrcode info short_url: %s", response->short_url); + IOT_LOGD("qrcode info short_url: %s", response->short_url); return OPRT_OK; } @@ -503,7 +503,7 @@ int atop_ai_token_get(const pal_t *pal, const ai_token_request_t *request, ai_to // Create request JSON cJSON *root = cJSON_CreateObject(); if (root == NULL) { - log_error("Failed to create JSON object"); + IOT_LOGE("Failed to create JSON object"); return OPRT_MALLOC_FAILED; } @@ -514,11 +514,11 @@ int atop_ai_token_get(const pal_t *pal, const ai_token_request_t *request, ai_to char *post_data = cJSON_PrintUnformatted(root); cJSON_Delete(root); if (post_data == NULL) { - log_error("Failed to print JSON"); + IOT_LOGE("Failed to print JSON"); return OPRT_MALLOC_FAILED; } - log_debug("post data:%s", post_data); + IOT_LOGD("post data:%s", post_data); // Prepare atop_base_request atop_base_request_t atop_request = { @@ -551,14 +551,14 @@ int atop_ai_token_get(const pal_t *pal, const ai_token_request_t *request, ai_to atop_response.error_code); snprintf(response->rejection.msg, sizeof(response->rejection.msg), "%s", atop_response.error_msg); - log_error("http post err, rt:%d", rt); + IOT_LOGE("http post err, rt:%d", rt); atop_base_response_free(pal,&atop_response); return rt; } cJSON *result = atop_response.result; if (result == NULL) { - log_error("http post err, no result"); + IOT_LOGE("http post err, no result"); atop_base_response_free(pal,&atop_response); return OPRT_COMMUNICATION_ERROR; } @@ -575,11 +575,11 @@ int atop_ai_token_get(const pal_t *pal, const ai_token_request_t *request, ai_to atop_base_response_free(pal, &atop_response); if (response->token == NULL) { - log_error("Failed to allocate token"); + IOT_LOGE("Failed to allocate token"); return OPRT_MALLOC_FAILED; } - log_debug("token: [%zu chars, prefix=%.4s...]", + IOT_LOGD("token: [%zu chars, prefix=%.4s...]", strlen(response->token), strlen(response->token) >= 4 ? response->token : "----"); return OPRT_OK; @@ -600,13 +600,13 @@ static bool schema_str_is_empty(const char *s) int atop_schema_newest_get(const pal_t *pal, const schema_newest_request_t *request, schema_newest_response_t *response) { if (request == NULL || response == NULL) { - log_error("atop_schema_newest_get: request or response is NULL"); + IOT_LOGE("atop_schema_newest_get: request or response is NULL"); return OPRT_INVALID_PARAMETER; } if (request->devid == NULL || request->devid[0] == '\0' || request->key == NULL || request->key[0] == '\0' || request->schema_id == NULL || request->schema_id[0] == '\0') { - log_error("atop_schema_newest_get: devid, key, or schema_id is empty"); + IOT_LOGE("atop_schema_newest_get: devid, key, or schema_id is empty"); return OPRT_INVALID_PARAMETER; } @@ -616,7 +616,7 @@ int atop_schema_newest_get(const pal_t *pal, const schema_newest_request_t *requ cJSON *root = cJSON_CreateObject(); if (root == NULL) { - log_error("atop_schema_newest_get: failed to create JSON object"); + IOT_LOGE("atop_schema_newest_get: failed to create JSON object"); return OPRT_MALLOC_FAILED; } cJSON_AddStringToObject(root, "schemaId", request->schema_id); @@ -629,11 +629,11 @@ int atop_schema_newest_get(const pal_t *pal, const schema_newest_request_t *requ char *post_data = cJSON_PrintUnformatted(root); cJSON_Delete(root); if (post_data == NULL) { - log_error("atop_schema_newest_get: failed to print JSON"); + IOT_LOGE("atop_schema_newest_get: failed to print JSON"); return OPRT_MALLOC_FAILED; } - log_debug("schema newest get post data:%s", post_data); + IOT_LOGD("schema newest get post data:%s", post_data); atop_base_request_t atop_request = { .devid = request->devid, @@ -656,7 +656,7 @@ int atop_schema_newest_get(const pal_t *pal, const schema_newest_request_t *requ cJSON_free(post_data); if (rt != OPRT_OK) { - log_error("atop_schema_newest_get request error:%d", rt); + IOT_LOGE("atop_schema_newest_get request error:%d", rt); atop_base_response_free(pal, &atop_response); return rt; } @@ -707,10 +707,10 @@ int atop_schema_newest_get(const pal_t *pal, const schema_newest_request_t *requ atop_base_response_free(pal, &atop_response); if (response->updated) { - log_info("atop_schema_newest_get: newer schema received (%zu bytes)", + IOT_LOGI("atop_schema_newest_get: newer schema received (%zu bytes)", strlen(response->schema)); } else { - log_debug("atop_schema_newest_get: no newer schema"); + IOT_LOGD("atop_schema_newest_get: no newer schema"); } return OPRT_OK; } @@ -737,12 +737,12 @@ void atop_schema_newest_response_free(const pal_t *pal, schema_newest_response_t int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota_upgrade_response_t *response) { if (request == NULL || response == NULL) { - log_error("atop_upgrade_get: request or response is NULL"); + IOT_LOGE("atop_upgrade_get: request or response is NULL"); return OPRT_INVALID_PARAMETER; } if (request->devid == NULL || request->devid[0] == '\0' || request->key == NULL || request->key[0] == '\0') { - log_error("atop_upgrade_get: devid or key is empty"); + IOT_LOGE("atop_upgrade_get: devid or key is empty"); return OPRT_INVALID_PARAMETER; } @@ -752,7 +752,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota cJSON *root = cJSON_CreateObject(); if (root == NULL) { - log_error("atop_upgrade_get: failed to create JSON object"); + IOT_LOGE("atop_upgrade_get: failed to create JSON object"); return OPRT_MALLOC_FAILED; } cJSON_AddNumberToObject(root, "type", request->channel); @@ -761,11 +761,11 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota char *post_data = cJSON_PrintUnformatted(root); cJSON_Delete(root); if (post_data == NULL) { - log_error("atop_upgrade_get: failed to print JSON"); + IOT_LOGE("atop_upgrade_get: failed to print JSON"); return OPRT_MALLOC_FAILED; } - log_debug("upgrade get post data:%s", post_data); + IOT_LOGD("upgrade get post data:%s", post_data); atop_base_request_t atop_request = { .devid = request->devid, @@ -788,7 +788,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota cJSON_free(post_data); if (rt != OPRT_OK) { - log_error("atop_upgrade_get request error:%d", rt); + IOT_LOGE("atop_upgrade_get request error:%d", rt); atop_base_response_free(pal, &atop_response); return rt; } @@ -796,7 +796,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota cJSON *result = atop_response.result; if (result == NULL) { /* success=true but result=null → cloud has no upgrade configured for this device */ - log_debug("atop_upgrade_get: no upgrade available"); + IOT_LOGD("atop_upgrade_get: no upgrade available"); atop_base_response_free(pal, &atop_response); return OPRT_OK; /* no-upgrade is success; response->has_upgrade stays false */ } @@ -814,7 +814,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota } } if (chosen_url == NULL) { - log_debug("atop_upgrade_get: no cdnUrl/httpsUrl (no upgrade)"); + IOT_LOGD("atop_upgrade_get: no cdnUrl/httpsUrl (no upgrade)"); atop_base_response_free(pal, &atop_response); return OPRT_OK; /* no-upgrade is success; response->has_upgrade stays false */ } @@ -853,7 +853,7 @@ int atop_upgrade_get(const pal_t *pal, const ota_upgrade_request_t *request, ota atop_base_response_free(pal, &atop_response); - log_info("atop_upgrade_get: upgrade available, version=%s, size=%ld", + IOT_LOGI("atop_upgrade_get: upgrade available, version=%s, size=%ld", response->version ? response->version : "?", response->file_size); return OPRT_OK; } @@ -878,7 +878,7 @@ int atop_version_update(const pal_t *pal, const ota_version_update_request_t *re if (request->devid == NULL || request->devid[0] == '\0' || request->key == NULL || request->key[0] == '\0' || request->sw_ver == NULL || request->sw_ver[0] == '\0') { - log_error("atop_version_update: devid, key, or sw_ver is empty"); + IOT_LOGE("atop_version_update: devid, key, or sw_ver is empty"); return OPRT_INVALID_PARAMETER; } @@ -891,7 +891,7 @@ int atop_version_update(const pal_t *pal, const ota_version_update_request_t *re #define VER_UPDATE_BUF_LEN 256 char *post_data = (char *)pal->malloc(VER_UPDATE_BUF_LEN); if (post_data == NULL) { - log_error("atop_version_update: malloc failed"); + IOT_LOGE("atop_version_update: malloc failed"); return OPRT_MALLOC_FAILED; } @@ -900,12 +900,12 @@ int atop_version_update(const pal_t *pal, const ota_version_update_request_t *re "\\\"baselineVer\\\":\\\"%s\\\",\\\"softVer\\\":\\\"%s\\\"}]\",\"t\":%" PRIu32 "}", request->channel, pv, bv, request->sw_ver, timestamp); if (write_len < 0 || (size_t)write_len >= VER_UPDATE_BUF_LEN) { - log_error("atop_version_update: failed to build post data, write_len=%d", write_len); + IOT_LOGE("atop_version_update: failed to build post data, write_len=%d", write_len); pal->free(post_data); return OPRT_COMMUNICATION_ERROR; } - log_debug("version update post data:%s", post_data); + IOT_LOGD("version update post data:%s", post_data); atop_base_request_t atop_request = { .devid = request->devid, @@ -928,7 +928,7 @@ int atop_version_update(const pal_t *pal, const ota_version_update_request_t *re pal->free(post_data); if (rt != OPRT_OK) { - log_error("atop_version_update request error:%d", rt); + IOT_LOGE("atop_version_update request error:%d", rt); atop_base_response_free(pal, &atop_response); return rt; } @@ -937,7 +937,7 @@ int atop_version_update(const pal_t *pal, const ota_version_update_request_t *re * envelope now returns OPRT_ATOP_BUSINESS_ERROR and is caught above. */ atop_base_response_free(pal, &atop_response); - log_debug("atop_version_update: success"); + IOT_LOGD("atop_version_update: success"); return OPRT_OK; } @@ -948,7 +948,7 @@ int atop_upgrade_status_update(const pal_t *pal, const ota_status_update_request } if (request->devid == NULL || request->devid[0] == '\0' || request->key == NULL || request->key[0] == '\0') { - log_error("atop_upgrade_status_update: devid or key is empty"); + IOT_LOGE("atop_upgrade_status_update: devid or key is empty"); return OPRT_INVALID_PARAMETER; } @@ -957,7 +957,7 @@ int atop_upgrade_status_update(const pal_t *pal, const ota_status_update_request #define STATUS_UPD_BUF_LEN 128 char *post_data = (char *)pal->malloc(STATUS_UPD_BUF_LEN); if (post_data == NULL) { - log_error("atop_upgrade_status_update: malloc failed"); + IOT_LOGE("atop_upgrade_status_update: malloc failed"); return OPRT_MALLOC_FAILED; } @@ -969,7 +969,7 @@ int atop_upgrade_status_update(const pal_t *pal, const ota_status_update_request return OPRT_COMMUNICATION_ERROR; } - log_debug("upgrade status update post data:%s", post_data); + IOT_LOGD("upgrade status update post data:%s", post_data); atop_base_request_t atop_request = { .devid = request->devid, @@ -992,7 +992,7 @@ int atop_upgrade_status_update(const pal_t *pal, const ota_status_update_request pal->free(post_data); if (rt != OPRT_OK) { - log_error("atop_upgrade_status_update request error:%d", rt); + IOT_LOGE("atop_upgrade_status_update request error:%d", rt); atop_base_response_free(pal, &atop_response); return rt; } @@ -1001,7 +1001,7 @@ int atop_upgrade_status_update(const pal_t *pal, const ota_status_update_request * envelope now returns OPRT_ATOP_BUSINESS_ERROR and is caught above. */ atop_base_response_free(pal, &atop_response); - log_debug("atop_upgrade_status_update: success (channel=%d, status=%d)", + IOT_LOGD("atop_upgrade_status_update: success (channel=%d, status=%d)", request->channel, (int)request->status); return OPRT_OK; } diff --git a/modules/iot-client/src/atop_base.c b/modules/iot-client/src/atop_base.c index d5de202..5a6e90f 100644 --- a/modules/iot-client/src/atop_base.c +++ b/modules/iot-client/src/atop_base.c @@ -87,7 +87,7 @@ if (md5_ret == 0) md5_ret = mbedtls_md5_finish(&ctx, digest); mbedtls_md5_free(&ctx); if (md5_ret != 0) { - log_error("MD5 computation failed: -0x%04x", -md5_ret); + IOT_LOGE("MD5 computation failed: -0x%04x", -md5_ret); return OPRT_COMMUNICATION_ERROR; } @@ -133,7 +133,7 @@ printlen += (size_t)ret; rt = atop_url_params_sign(pal, key, params, param_num, (uint8_t *)buffer + printlen, &sign_len); if (rt != 0) { - log_error("atop_url_params_sign error:%d", rt); + IOT_LOGE("atop_url_params_sign error:%d", rt); return rt; } printlen += sign_len; @@ -164,13 +164,13 @@ size_t buflen = AES_GCM128_NONCE_LEN + ilen + AES_GCM128_TAG_LEN; uint8_t *encrypted_buffer = pal->malloc(buflen); if (encrypted_buffer == NULL) { - log_error("encrypted_buffer malloc fail"); + IOT_LOGE("encrypted_buffer malloc fail"); return OPRT_MALLOC_FAILED; } /* Nonce - generate random nonce from the shared DRBG */ if (rng_bytes(pal, encrypted_buffer, AES_GCM128_NONCE_LEN) != 0) { - log_error("Failed to generate nonce via RNG"); + IOT_LOGE("Failed to generate nonce via RNG"); pal->free(encrypted_buffer); return OPRT_COMMUNICATION_ERROR; } @@ -189,7 +189,7 @@ encrypted_buffer + AES_GCM128_NONCE_LEN, &encrypt_olen, encrypted_buffer + AES_GCM128_NONCE_LEN + ilen, AES_GCM128_TAG_LEN); if (ret != OPRT_OK) { - log_error("mbedtls_cipher_auth_encrypt_wrapper:0x%x", ret); + IOT_LOGE("mbedtls_cipher_auth_encrypt_wrapper:0x%x", ret); pal->free(encrypted_buffer); return ret; } @@ -243,7 +243,7 @@ .data_len = ilen - AES_GCM128_NONCE_LEN - AES_GCM128_TAG_LEN}, output, olen, (unsigned char *)(input + (ilen - AES_GCM128_TAG_LEN)), AES_GCM128_TAG_LEN); if (rt != OPRT_OK) { - log_error("aes128_ecb_decode error:%d", rt); + IOT_LOGE("aes128_ecb_decode error:%d", rt); return rt; } @@ -301,7 +301,7 @@ char *value; size_t value_length; - log_debug("atop_response_data_decode: %s", (char *)input); + IOT_LOGD("atop_response_data_decode: %s", (char *)input); cJSON *root = cJSON_Parse((char *)input); if (NULL == root) { @@ -310,26 +310,26 @@ cJSON *item = cJSON_GetObjectItem(root, "result"); if (NULL == item) { - log_error("no result"); + IOT_LOGE("no result"); cJSON_Delete(root); return OPRT_COMMUNICATION_ERROR; } if (!cJSON_IsString(item) || item->valuestring == NULL) { - log_error("result is not a string"); + IOT_LOGE("result is not a string"); cJSON_Delete(root); return OPRT_COMMUNICATION_ERROR; } value = item->valuestring; value_length = strlen(value); - log_debug("base64 encode result:\r\n%.*s", (int)value_length, value); + IOT_LOGD("base64 encode result:\r\n%.*s", (int)value_length, value); // base64 decode buffer size_t b64buffer_len = value_length * 3 / 4; uint8_t *b64buffer = pal->malloc(b64buffer_len); if (b64buffer == NULL) { - log_error("Failed to allocate base64 decode buffer"); + IOT_LOGE("Failed to allocate base64 decode buffer"); cJSON_Delete(root); return OPRT_MALLOC_FAILED; } @@ -338,7 +338,7 @@ // base64 decode rt = mbedtls_base64_decode(b64buffer, b64buffer_len, &b64buffer_olen, (const uint8_t *)value, value_length); if (rt != OPRT_OK) { - log_error("base64 decode error:%d", rt); + IOT_LOGE("base64 decode error:%d", rt); pal->free(b64buffer); cJSON_Delete(root); return rt; @@ -348,16 +348,16 @@ if (rt != OPRT_OK && b64buffer_olen % 16 == 0) { /* GCM failed — cloud may use the older AES-128-ECB response format (et=1). * Try ECB with the same raw 16-byte key. */ - log_debug("GCM decrypt failed (%d), retrying with AES-ECB (old protocol)", rt); + IOT_LOGD("GCM decrypt failed (%d), retrying with AES-ECB (old protocol)", rt); rt = atop_response_result_decrypt_ecb(key, (const uint8_t *)b64buffer, b64buffer_olen, output, olen); } cJSON_Delete(root); pal->free(b64buffer); if (rt != OPRT_OK) { - log_error("atop_data_decrpyt error: %d", rt); + IOT_LOGE("atop_data_decrpyt error: %d", rt); return rt; } - log_debug("result:\r\n%.*s", (int)*olen, output); + IOT_LOGD("result:\r\n%.*s", (int)*olen, output); return rt; } @@ -368,20 +368,20 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a (void)ilen; if (NULL == input || NULL == response) { - log_error("param error"); + IOT_LOGE("param error"); return OPRT_INVALID_PARAMETER; } // json parse (input buffer is expected to be null-terminated) cJSON *root = cJSON_Parse((const char *)input); if (NULL == root) { - log_error("Json parse error"); + IOT_LOGE("Json parse error"); return OPRT_COMMUNICATION_ERROR; } // verify success key if (!cJSON_HasObjectItem(root, "success")) { - log_error("not found json success key"); + IOT_LOGE("not found json success key"); cJSON_Delete(root); return OPRT_COMMUNICATION_ERROR; } @@ -419,7 +419,7 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a /* Keep the server's explanation in the log even on this malformed * path -- it is often the only clue (e.g. a signature-time-skew * message from a gateway that omits errorCode). */ - log_error("atop rejected without errorCode (errorMsg=%s)", + IOT_LOGE("atop rejected without errorCode (errorMsg=%s)", response->error_msg); cJSON_Delete(root); return OPRT_COMMUNICATION_ERROR; @@ -427,7 +427,7 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a snprintf(response->error_code, sizeof(response->error_code), "%s", error_code_item->valuestring); - log_error("atop rejected: errorCode=%s errorMsg=%s", + IOT_LOGE("atop rejected: errorCode=%s errorMsg=%s", response->error_code, response->error_msg); /* Every rejection is the same verdict: the cloud answered and said no. @@ -508,14 +508,14 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a /* url param buffer make */ char *path_buffer = pal->malloc(MAX_URL_LENGTH); if (NULL == path_buffer) { - log_error("path_buffer malloc fail"); + IOT_LOGE("path_buffer malloc fail"); return OPRT_MALLOC_FAILED; } /* attach path prefix */ int path_buffer_len = snprintf(path_buffer, MAX_URL_LENGTH, "%s?", (char *)request->path); if (path_buffer_len < 0 || path_buffer_len >= MAX_URL_LENGTH) { - log_error("path_buffer snprintf fail"); + IOT_LOGE("path_buffer snprintf fail"); pal->free(path_buffer); return OPRT_COMMUNICATION_ERROR; } @@ -530,40 +530,40 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a rt = atop_url_params_encode(pal, (char *)request->key, params, idx, path_buffer + path_buffer_len, path_buffer_remain, &encode_len); if (rt != OPRT_OK) { - log_error("url param encode error:%d", rt); + IOT_LOGE("url param encode error:%d", rt); pal->free(path_buffer); return rt; } path_buffer_len += encode_len; - log_debug("request url len:%d: %s", path_buffer_len, path_buffer); + IOT_LOGD("request url len:%d: %s", path_buffer_len, path_buffer); /* POST data buffer */ size_t body_length = 0; size_t raw_len = request->datalen + AES_GCM128_NONCE_LEN + AES_GCM128_TAG_LEN; if (raw_len < request->datalen || raw_len > SIZE_MAX / 2) { - log_error("request data too large"); + IOT_LOGE("request data too large"); pal->free(path_buffer); return OPRT_INVALID_PARAMETER; } size_t body_buffer_len = POST_DATA_PREFIX + raw_len * 2 + 1; uint8_t *body_buffer = pal->malloc(body_buffer_len); if (NULL == body_buffer) { - log_error("body_buffer malloc fail"); + IOT_LOGE("body_buffer malloc fail"); pal->free(path_buffer); return OPRT_MALLOC_FAILED; } /* POST data encode */ - log_debug("atop_request_data_encode"); + IOT_LOGD("atop_request_data_encode"); rt = atop_request_data_encode(pal, (char *)request->key, request->data, request->datalen, body_buffer, body_buffer_len, &body_length); if (rt != OPRT_OK) { - log_error("atop_post_data_encrypt error:%d", rt); + IOT_LOGE("atop_post_data_encrypt error:%d", rt); pal->free(path_buffer); pal->free(body_buffer); return rt; } - log_debug("out post data len:%d, data:%s", (int)body_length, body_buffer); + IOT_LOGD("out post data len:%d, data:%s", (int)body_length, body_buffer); /* HTTP headers */ http_client_header_t headers[] = { @@ -575,12 +575,12 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a http_client_response_t http_response = {0}; /* HTTP Request send */ - log_debug("http request send!"); + IOT_LOGD("http request send!"); // Use server address from request, or default to Tuya cloud server const char *server_host = request->host ? request->host : IOT_DEFAULT_HOST; uint16_t server_port = (request->port > 0) ? request->port : IOT_DEFAULT_PORT; - log_info("Connecting to server: %s:%d", server_host, server_port); + IOT_LOGI("Connecting to server: %s:%d", server_host, server_port); http_status = http_client_request(&(const http_client_request_t){.cacert = request->cacert, .cert_bundle_attach = request->cert_bundle_attach, @@ -601,7 +601,7 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a pal->free(body_buffer); if (HTTP_CLIENT_SUCCESS != http_status) { - log_error("http_request_send error:%d", http_status); + IOT_LOGE("http_request_send error:%d", http_status); return (http_status == HTTP_CLIENT_TLS_ERROR) ? OPRT_TLS_HANDSHAKE_FAILED : OPRT_COMMUNICATION_ERROR; @@ -610,7 +610,7 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a size_t result_buffer_length = 0; uint8_t *result_buffer = pal->malloc(http_response.body_length + 1); if (NULL == result_buffer) { - log_error("result_buffer malloc fail"); + IOT_LOGE("result_buffer malloc fail"); http_client_free(pal, &http_response); return OPRT_MALLOC_FAILED; } @@ -623,7 +623,7 @@ static int atop_response_result_parse_cjson(const uint8_t *input, size_t ilen, a if (OPRT_OK == rt) { rt = atop_response_result_parse_cjson(result_buffer, result_buffer_length, response); } else { - log_debug("atop_response_decode error:%d, try parse the plaintext data.", rt); + IOT_LOGD("atop_response_decode error:%d, try parse the plaintext data.", rt); rt = atop_response_result_parse_cjson(http_response.body, http_response.body_length, response); } diff --git a/modules/iot-client/src/cipher_wrapper.c b/modules/iot-client/src/cipher_wrapper.c index a1a822c..2cd4a48 100644 --- a/modules/iot-client/src/cipher_wrapper.c +++ b/modules/iot-client/src/cipher_wrapper.c @@ -44,7 +44,7 @@ int mbedtls_cipher_auth_encrypt_wrapper(const cipher_params_t *params, int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, (const unsigned char *)params->key, key_bits); if (ret != 0) { - log_error("Failed to set GCM key: -0x%04x", -ret); + IOT_LOGE("Failed to set GCM key: -0x%04x", -ret); mbedtls_gcm_free(&gcm); return ret; } @@ -84,7 +84,7 @@ int mbedtls_cipher_auth_decrypt_wrapper(const cipher_params_t *params, int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, (const unsigned char *)params->key, key_bits); if (ret != 0) { - log_error("Failed to set GCM key for decryption: -0x%04x", -ret); + IOT_LOGE("Failed to set GCM key for decryption: -0x%04x", -ret); mbedtls_gcm_free(&gcm); return ret; } @@ -126,7 +126,7 @@ int pv23_encrypt(const pal_t *pal, const uint8_t *plaintext, size_t plaintext_le /* IV — random nonce from the shared process-wide DRBG (common/rng.c); the * caller's pal locks that DRBG. */ if (rng_bytes(pal, iv, P23_IV_LEN) != 0) { - log_error("Failed to generate IV via RNG"); + IOT_LOGE("Failed to generate IV via RNG"); mbedtls_gcm_free(&gcm); return OPRT_COMMUNICATION_ERROR; } @@ -134,7 +134,7 @@ int pv23_encrypt(const pal_t *pal, const uint8_t *plaintext, size_t plaintext_le // Set up GCM ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 128); if (ret != 0) { - log_error("Failed to set GCM key: -0x%04x", -ret); + IOT_LOGE("Failed to set GCM key: -0x%04x", -ret); goto cleanup; } @@ -160,7 +160,7 @@ int pv23_encrypt(const pal_t *pal, const uint8_t *plaintext, size_t plaintext_le plaintext, output + offset, P23_TAG_LEN, tag); if (ret != 0) { - log_error("Failed to encrypt: -0x%04x", -ret); + IOT_LOGE("Failed to encrypt: -0x%04x", -ret); goto cleanup; } @@ -179,12 +179,12 @@ int pv23_decrypt(const pal_t *pal, const uint8_t *ciphertext, size_t ciphertext_ const uint8_t *key, uint8_t *output, size_t *output_len) { (void)pal; /* decryption needs no RNG; pal is for symmetry with pv23_encrypt */ if (!ciphertext || !key || !output || !output_len) { - log_error("Invalid parameters for p23_decrypt"); + IOT_LOGE("Invalid parameters for p23_decrypt"); return OPRT_INVALID_PARAMETER; } if (ciphertext_len < P23_AAD_LEN + P23_IV_LEN + P23_TAG_LEN) { - log_error("Ciphertext too short for P2.3 decryption: %u bytes", (unsigned)ciphertext_len); + IOT_LOGE("Ciphertext too short for P2.3 decryption: %u bytes", (unsigned)ciphertext_len); return OPRT_INVALID_PARAMETER; } @@ -208,7 +208,7 @@ int pv23_decrypt(const pal_t *pal, const uint8_t *ciphertext, size_t ciphertext_ int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 128); if (ret != 0) { - log_error("Failed to set GCM key for decryption: -0x%04x", -ret); + IOT_LOGE("Failed to set GCM key for decryption: -0x%04x", -ret); mbedtls_gcm_free(&gcm); return ret; } @@ -223,7 +223,7 @@ int pv23_decrypt(const pal_t *pal, const uint8_t *ciphertext, size_t ciphertext_ if (ret == 0) { *output_len = encrypted_len; } else { - log_error("Failed to decrypt: -0x%04x", -ret); + IOT_LOGE("Failed to decrypt: -0x%04x", -ret); } mbedtls_gcm_free(&gcm); @@ -241,7 +241,7 @@ int iot_md5_password(const char *key, char *password_out) if (ret == 0) ret = mbedtls_md5_finish(&md5_ctx, md5_hash); mbedtls_md5_free(&md5_ctx); if (ret != 0) { - log_error("MD5 computation failed: -0x%04x", -ret); + IOT_LOGE("MD5 computation failed: -0x%04x", -ret); return ret; } diff --git a/modules/iot-client/src/http_client_interface.c b/modules/iot-client/src/http_client_interface.c index 774360e..7ca54e9 100644 --- a/modules/iot-client/src/http_client_interface.c +++ b/modules/iot-client/src/http_client_interface.c @@ -34,13 +34,13 @@ static int32_t transport_send(NetworkContext_t *pNetworkContext, int r = tls_write(ctx->tls, (const uint8_t *)pBuffer, bytesToSend, 30000 /* 30s */); if (r != TLS_OK) { - log_error("TLS write error"); + IOT_LOGE("TLS write error"); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytesToSend; } else { if (!ctx->tcp_handle) { - log_error("TCP send: connection handle is NULL"); + IOT_LOGE("TCP send: connection handle is NULL"); return OPRT_COMMUNICATION_ERROR; } int bytes_sent = ctx->pal->tcp_send(ctx->tcp_handle, @@ -50,7 +50,7 @@ static int32_t transport_send(NetworkContext_t *pNetworkContext, return 0; } if (bytes_sent < 0) { - log_error("TCP send error"); + IOT_LOGE("TCP send error"); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytes_sent; @@ -77,13 +77,13 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, // no-data to TLS_ERR_AGAIN, so 0 here always means the peer closed // (e.g. server closing the connection mid-response). Surface it as an // error so the receive loop fails fast instead of spinning to timeout. - log_error("TLS read error/closed (n=%d)", n); + IOT_LOGE("TLS read error/closed (n=%d)", n); return OPRT_COMMUNICATION_ERROR; } return n; // >0 bytes } else { if (!ctx->tcp_handle) { - log_error("TCP recv: connection handle is NULL"); + IOT_LOGE("TCP recv: connection handle is NULL"); return OPRT_COMMUNICATION_ERROR; } int bytes_received = ctx->pal->tcp_recv(ctx->tcp_handle, @@ -93,7 +93,7 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, return 0; } if (bytes_received < 0) { - log_error("TCP recv error"); + IOT_LOGE("TCP recv error"); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytes_received; @@ -104,10 +104,10 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, static void *connect_tcp(const pal_t *pal, const char *host, uint16_t port, uint32_t timeout_ms) { void *handle = pal->tcp_connect(host, port, timeout_ms); if (!handle) { - log_error("Failed to connect to %s:%d", host, port); + IOT_LOGE("Failed to connect to %s:%d", host, port); return NULL; } - log_debug("TCP connection established to %s:%d", host, port); + IOT_LOGD("TCP connection established to %s:%d", host, port); return handle; } @@ -115,7 +115,7 @@ static int connect_tls(struct HTTPNetworkContext *ctx, const char *host, uint16_ const char *cacert, tls_cert_bundle_attach_fn cert_bundle_attach) { bool has_cacert = (cacert && cacert[0] != '\0'); if (!has_cacert && !cert_bundle_attach) { - log_warn("No CA certificate provided - server verification disabled"); + IOT_LOGW("No CA certificate provided - server verification disabled"); } tls_config_t cfg = { @@ -133,11 +133,11 @@ static int connect_tls(struct HTTPNetworkContext *ctx, const char *host, uint16_ ctx->tls = tls_connect(&cfg); if (!ctx->tls) { - log_error("Failed to establish TLS connection to %s:%d", host, port); + IOT_LOGE("Failed to establish TLS connection to %s:%d", host, port); return OPRT_TLS_HANDSHAKE_FAILED; } ctx->use_tls = true; - log_debug("TLS connection established to %s:%d", host, port); + IOT_LOGD("TLS connection established to %s:%d", host, port); return OPRT_OK; } @@ -209,7 +209,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, } if (connect_ret != 0) { - log_error("Failed to connect to %s:%d", request->host, request->port); + IOT_LOGE("Failed to connect to %s:%d", request->host, request->port); return (connect_ret == OPRT_TLS_HANDSHAKE_FAILED) ? HTTP_CLIENT_TLS_ERROR : HTTP_CLIENT_ERROR; } @@ -235,7 +235,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, * lands in PSRAM on targets that route large allocations there. */ uint8_t *http_buf = (uint8_t *)pal->malloc(AGENTIC_KIT_REQUEST_HEADER_BUFFER_SIZE + AGENTIC_KIT_RESPONSE_BUFFER_SIZE); if (!http_buf) { - log_error("Failed to allocate HTTP buffers"); + IOT_LOGE("Failed to allocate HTTP buffers"); disconnect(network_ctx); return HTTP_CLIENT_ERROR; } @@ -261,7 +261,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, // Initialize request headers HTTPStatus_t http_status = HTTPClient_InitializeRequestHeaders(&request_headers, &request_info); if (http_status != HTTPSuccess) { - log_error("Failed to initialize request headers: %d", http_status); + IOT_LOGE("Failed to initialize request headers: %d", http_status); pal->free(http_buf); disconnect(network_ctx); return HTTP_CLIENT_ERROR; @@ -275,7 +275,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, request->headers[i].value, strlen(request->headers[i].value)); if (http_status != HTTPSuccess) { - log_error("Failed to add header %s: %d", request->headers[i].key, http_status); + IOT_LOGE("Failed to add header %s: %d", request->headers[i].key, http_status); pal->free(http_buf); disconnect(network_ctx); return HTTP_CLIENT_ERROR; @@ -325,7 +325,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, response->body[http_response.bodyLen] = '\0'; response->body_length = http_response.bodyLen; } else { - log_error("Failed to allocate response body buffer"); + IOT_LOGE("Failed to allocate response body buffer"); ret_status = HTTP_CLIENT_ERROR; response->internal = NULL; pal->free(http_buf); @@ -336,7 +336,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, response->internal = NULL; - log_debug("HTTP request successful: status=%d, body_len=%d", + IOT_LOGD("HTTP request successful: status=%d, body_len=%d", response->status_code, (int)response->body_length); } else { /* Name the actual cause. HTTPInsufficientMemory used to surface to the @@ -345,7 +345,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, * may well have SUCCEEDED on the server (HTTP 200) before we ran out of * room to read the reply. */ if (http_status == HTTPInsufficientMemory) { - log_error("HTTP response does not fit: need %u B body + %u B headers, " + IOT_LOGE("HTTP response does not fit: need %u B body + %u B headers, " "buffer is %d B (server said %u). Rebuild with a larger " "-DAGENTIC_KIT_RESPONSE_BUFFER_SIZE.", (unsigned)http_response.contentLength, @@ -353,7 +353,7 @@ http_client_status_t http_client_request(const http_client_request_t *request, AGENTIC_KIT_RESPONSE_BUFFER_SIZE, (unsigned)http_response.statusCode); } else { - log_error("HTTP request failed: %d", http_status); + IOT_LOGE("HTTP request failed: %d", http_status); } // Map coreHTTP status to our status diff --git a/modules/iot-client/src/iot_atop.c b/modules/iot-client/src/iot_atop.c index e24db22..cde8d72 100644 --- a/modules/iot-client/src/iot_atop.c +++ b/modules/iot-client/src/iot_atop.c @@ -68,14 +68,14 @@ int iot_atop_call(iot_client_t *client, } if (request->api == NULL || request->api[0] == '\0' || request->version == NULL || request->version[0] == '\0') { - log_error("iot_atop_call: api and version are required"); + IOT_LOGE("iot_atop_call: api and version are required"); return OPRT_INVALID_PARAMETER; } /* Activated devices only: this path signs with devid + secret_key. * Activation uses uuid + authkey and stays behind on-boarding. */ if (client->devid[0] == '\0' || client->secret_key[0] == '\0') { - log_error("iot_atop_call: client has no device credentials yet"); + IOT_LOGE("iot_atop_call: client has no device credentials yet"); return OPRT_UNINITIALIZED; } @@ -84,7 +84,7 @@ int iot_atop_call(iot_client_t *client, : "{}"; int rt = atop_body_is_json_object(body); if (rt != OPRT_OK) { - log_error("iot_atop_call: data is not a JSON object"); + IOT_LOGE("iot_atop_call: data is not a JSON object"); return rt; } @@ -120,7 +120,7 @@ int iot_atop_call(iot_client_t *client, response->server_time = atop_response.t; if (rt != OPRT_OK) { - log_error("iot_atop_call(%s v%s) failed: %d", request->api, + IOT_LOGE("iot_atop_call(%s v%s) failed: %d", request->api, request->version, rt); atop_base_response_free(client->pal, &atop_response); return rt; diff --git a/modules/iot-client/src/iot_client.c b/modules/iot-client/src/iot_client.c index 2766bae..467974f 100644 --- a/modules/iot-client/src/iot_client.c +++ b/modules/iot-client/src/iot_client.c @@ -54,11 +54,11 @@ static int parse_host_port(const char *url, char *host_out, size_t host_len, uin char *endptr = NULL; unsigned long port_val = strtoul(colon + 1, &endptr, 10); if (endptr == colon + 1 || port_val == 0 || port_val > 65535) { - log_error("Invalid port in URL: %s", url); + IOT_LOGE("Invalid port in URL: %s", url); return OPRT_INVALID_PARAMETER; } if (*endptr != '\0' && *endptr != '/' && endptr != slash) { - log_error("Invalid characters after port in URL: %s", url); + IOT_LOGE("Invalid characters after port in URL: %s", url); return OPRT_INVALID_PARAMETER; } *port_out = (uint16_t)port_val; @@ -113,7 +113,7 @@ static int iot_client_dns_resolve(iot_client_t *client) iot_dns_url_config_response_t dns_resp = {0}; int ret = iot_dns_url_config(client->pal, &dns_req, &dns_resp); if (ret != OPRT_OK) { - log_warn("Failed to query IoT DNS for service URLs: %d", ret); + IOT_LOGW("Failed to query IoT DNS for service URLs: %d", ret); return ret; } @@ -125,18 +125,18 @@ static int iot_client_dns_resolve(iot_client_t *client) "%s://%s", scheme, addr); if (sn < 0 || (size_t)sn >= sizeof(client->mqtt_url)) { client->mqtt_url[0] = '\0'; /* too long: leave unresolved */ - log_warn("IoT DNS %s url too long, ignored", mqtt_dns_key); + IOT_LOGW("IoT DNS %s url too long, ignored", mqtt_dns_key); } else { - log_info("IoT DNS %s: %s", mqtt_dns_key, client->mqtt_url); + IOT_LOGI("IoT DNS %s: %s", mqtt_dns_key, client->mqtt_url); } } else if (strcmp(dns_resp.endpoints[i].key, IOT_DNS_KEY_HTTPS) == 0) { const char *addr = dns_resp.endpoints[i].addr; int sn = snprintf(client->https_url, sizeof(client->https_url), "%s", addr); if (sn < 0 || (size_t)sn >= sizeof(client->https_url)) { client->https_url[0] = '\0'; - log_warn("IoT DNS %s too long, ignored", IOT_DNS_KEY_HTTPS); + IOT_LOGW("IoT DNS %s too long, ignored", IOT_DNS_KEY_HTTPS); } else { - log_info("IoT DNS %s: %s", IOT_DNS_KEY_HTTPS, client->https_url); + IOT_LOGI("IoT DNS %s: %s", IOT_DNS_KEY_HTTPS, client->https_url); } } } @@ -147,11 +147,11 @@ static int iot_client_dns_resolve(iot_client_t *client) * how an unknown `region` presents). Say so here, or the only symptom is a * refused MQTT connect several layers away, with nothing pointing back. */ if (client->mqtt_url[0] == '\0') { - log_warn("IoT DNS returned no %s for region=%s env=%s — MQTT stays unresolved", + IOT_LOGW("IoT DNS returned no %s for region=%s env=%s — MQTT stays unresolved", mqtt_dns_key, dns_req.region ? dns_req.region : "(unset)", dns_req.env); } if (client->https_url[0] == '\0') { - log_warn("IoT DNS returned no %s for region=%s env=%s — falling back to %s", + IOT_LOGW("IoT DNS returned no %s for region=%s env=%s — falling back to %s", IOT_DNS_KEY_HTTPS, dns_req.region ? dns_req.region : "(unset)", dns_req.env, iot_region_to_host(client->region, client->env)); } @@ -162,7 +162,7 @@ static int iot_client_dns_resolve(iot_client_t *client) int iot_init(const pal_t *pal) { if (!pal_is_valid(pal)) { - log_error("iot_init: invalid PAL adapter (NULL or missing function pointers)"); + IOT_LOGE("iot_init: invalid PAL adapter (NULL or missing function pointers)"); return OPRT_INVALID_PARAMETER; } set_pal(pal); @@ -171,7 +171,7 @@ int iot_init(const pal_t *pal) * fails closed if seeding failed, so surface the failure here rather than * letting it resurface later as opaque handshake / nonce errors. */ if (rng_init(pal) != 0) { - log_error("iot_init: RNG seed failed (no strong entropy source?)"); + IOT_LOGE("iot_init: RNG seed failed (no strong entropy source?)"); return OPRT_COMMUNICATION_ERROR; } @@ -193,7 +193,7 @@ int iot_client_report_init_versions(iot_client_t *client, return OPRT_INVALID_PARAMETER; } if (config->skip_version_report) { - log_info("skip_version_report set: skipping SDK-meta and firmware-version reports"); + IOT_LOGI("skip_version_report set: skipping SDK-meta and firmware-version reports"); return OPRT_OK; } @@ -222,7 +222,7 @@ int iot_client_report_init_versions(iot_client_t *client, device_meta_save_response_t meta_resp = {0}; int meta_ret = atop_device_meta_save(client->pal, &meta_req, &meta_resp); if (meta_ret != OPRT_OK) { - log_warn("atop_device_meta_save failed: %d (non-fatal)", meta_ret); + IOT_LOGW("atop_device_meta_save failed: %d (non-fatal)", meta_ret); ret = meta_ret; } } @@ -234,7 +234,7 @@ int iot_client_report_init_versions(iot_client_t *client, : IOT_SDK_SW_VER; int ver_ret = iot_ota_report_version(client, fw_ver); if (ver_ret != OPRT_OK) { - log_warn("iot_ota_report_version failed: %d (non-fatal)", ver_ret); + IOT_LOGW("iot_ota_report_version failed: %d (non-fatal)", ver_ret); if (ret == OPRT_OK) { ret = ver_ret; } @@ -247,18 +247,18 @@ int iot_client_report_init_versions(iot_client_t *client, IOT_API iot_client_t *iot_client_init(const iot_client_config_t *config) { if (!config) { - log_error("Invalid config for iot_client_init"); + IOT_LOGE("Invalid config for iot_client_init"); return NULL; } const pal_t *pal = get_pal(); if (!pal) { - log_error("iot_client_init: PAL not initialized — call iot_init() first"); + IOT_LOGE("iot_client_init: PAL not initialized — call iot_init() first"); return NULL; } iot_client_t *client = (iot_client_t *)pal->malloc(sizeof(iot_client_t)); if (client == NULL) { - log_error("Failed to allocate iot_client_t"); + IOT_LOGE("Failed to allocate iot_client_t"); return NULL; } memset(client, 0, sizeof(iot_client_t)); @@ -298,7 +298,7 @@ IOT_API iot_client_t *iot_client_init(const iot_client_config_t *config) if (client->mqtt_url[0] != '\0' && !config->mqtt_disable_auto_connect) { int ret = iot_client_message_connect(client); if (ret != OPRT_OK) { - log_error("MQTT connect failed: %d", ret); + IOT_LOGE("MQTT connect failed: %d", ret); iot_client_deinit(client); return NULL; } @@ -397,7 +397,7 @@ IOT_API int iot_client_reset(iot_client_t *client, if (rt != OPRT_OK) { /* Leave the client intact: the caller may retry, and a half-torn-down * client the cloud still considers bound is worse than none. */ - log_error("iot_client_reset failed: %d errorCode=%s", rt, + IOT_LOGE("iot_client_reset failed: %d errorCode=%s", rt, response.error_code); iot_atop_response_free(client, &response); return rt; @@ -415,22 +415,22 @@ IOT_API int iot_client_reset(iot_client_t *client, IOT_API iot_client_t *iot_client_init_on_boarding(const iot_on_boarding_config_t *config) { if (!config) { - log_error("Invalid config for iot_client_init_on_boarding"); + IOT_LOGE("Invalid config for iot_client_init_on_boarding"); return NULL; } const pal_t *pal = get_pal(); if (!pal) { - log_error("iot_client_init_on_boarding: PAL not initialized — call iot_init() first"); + IOT_LOGE("iot_client_init_on_boarding: PAL not initialized — call iot_init() first"); return NULL; } if (config->uuid[0] == '\0') { - log_error("uuid is required for on boarding"); + IOT_LOGE("uuid is required for on boarding"); return NULL; } - log_info("iot_client_init_on_boarding: uuid=%s", config->uuid); + IOT_LOGI("iot_client_init_on_boarding: uuid=%s", config->uuid); /* Build the internal on_boarding_config_t from the public config */ on_boarding_config_t ob_cfg = {0}; @@ -463,11 +463,11 @@ IOT_API iot_client_t *iot_client_init_on_boarding(const iot_on_boarding_config_t on_boarding_response_t ob_resp = {0}; int ret = on_boarding_with_qrcode(pal, &ob_cfg, &ob_resp); if (ret != OPRT_OK) { - log_error("on_boarding_with_qrcode failed: %d", ret); + IOT_LOGE("on_boarding_with_qrcode failed: %d", ret); return NULL; } - log_info("On-boarding successful, initializing client with activated credentials"); + IOT_LOGI("On-boarding successful, initializing client with activated credentials"); /* Build iot_client_config_t from activation results and call iot_client_init. * Hand-copy block — AGENTS.md invariant: a new iot_client_config_t field must @@ -502,7 +502,7 @@ IOT_API iot_client_t *iot_client_init_on_boarding(const iot_on_boarding_config_t iot_client_t *client = iot_client_init(&client_config); pal->free(ob_resp.schema); /* iot_client_init copied it (or failed) */ if (client == NULL) { - log_error("iot_client_init failed after on-boarding"); + IOT_LOGE("iot_client_init failed after on-boarding"); return NULL; } @@ -516,27 +516,27 @@ IOT_API iot_client_t *iot_client_init_on_boarding(const iot_on_boarding_config_t IOT_API iot_client_t *iot_client_init_on_boarding_with_token(const iot_on_boarding_config_t *config, const char *token) { if (!config) { - log_error("Invalid config for iot_client_init_on_boarding_with_token"); + IOT_LOGE("Invalid config for iot_client_init_on_boarding_with_token"); return NULL; } const pal_t *pal = get_pal(); if (!pal) { - log_error("iot_client_init_on_boarding_with_token: PAL not initialized — call iot_init() first"); + IOT_LOGE("iot_client_init_on_boarding_with_token: PAL not initialized — call iot_init() first"); return NULL; } if (config->uuid[0] == '\0') { - log_error("uuid is required for on boarding with token"); + IOT_LOGE("uuid is required for on boarding with token"); return NULL; } if (!token || token[0] == '\0') { - log_error("token is required for on boarding with token"); + IOT_LOGE("token is required for on boarding with token"); return NULL; } - log_info("iot_client_init_on_boarding_with_token: uuid=%s", config->uuid); + IOT_LOGI("iot_client_init_on_boarding_with_token: uuid=%s", config->uuid); on_boarding_config_t ob_cfg = {0}; strncpy(ob_cfg.uuid, config->uuid, sizeof(ob_cfg.uuid) - 1); @@ -566,11 +566,11 @@ IOT_API iot_client_t *iot_client_init_on_boarding_with_token(const iot_on_boardi on_boarding_response_t ob_resp = {0}; int ret = on_boarding_with_token(pal, &ob_cfg, token, &ob_resp); if (ret != OPRT_OK) { - log_error("on_boarding_with_token failed: %d", ret); + IOT_LOGE("on_boarding_with_token failed: %d", ret); return NULL; } - log_info("On-boarding with token successful, initializing client with activated credentials"); + IOT_LOGI("On-boarding with token successful, initializing client with activated credentials"); /* Hand-copy block — same AGENTS.md invariant as in iot_client_init_on_boarding(): * a new iot_client_config_t field must be forwarded here too, and no test @@ -602,7 +602,7 @@ IOT_API iot_client_t *iot_client_init_on_boarding_with_token(const iot_on_boardi iot_client_t *client = iot_client_init(&client_config); pal->free(ob_resp.schema); /* iot_client_init copied it (or failed) */ if (client == NULL) { - log_error("iot_client_init failed after on-boarding with token"); + IOT_LOGE("iot_client_init failed after on-boarding with token"); return NULL; } @@ -627,7 +627,7 @@ IOT_API int iot_client_get_session_token_ex(iot_client_t *client, const char *ag } if (client == NULL || token == NULL || token_len == 0) { - log_error("iot_client_get_session_token: invalid parameters"); + IOT_LOGE("iot_client_get_session_token: invalid parameters"); return OPRT_INVALID_PARAMETER; } @@ -651,12 +651,12 @@ IOT_API int iot_client_get_session_token_ex(iot_client_t *client, const char *ag if (rejection != NULL) { *rejection = resp.rejection; } - log_error("atop_ai_token_get failed: %d", ret); + IOT_LOGE("atop_ai_token_get failed: %d", ret); return ret; } size_t resp_token_len = strlen(resp.token); if (resp_token_len >= token_len) { - log_error("token buffer too small: need %zu, have %zu", resp_token_len + 1, token_len); + IOT_LOGE("token buffer too small: need %zu, have %zu", resp_token_len + 1, token_len); client->pal->free(resp.token); return OPRT_INVALID_RESULT; } @@ -718,18 +718,18 @@ IOT_API int iot_get_ca_certificate(iot_client_t *client, const char *host, uint1 iot_dns_ca_cert_response_t resp = {0}; int ret = iot_dns_get_ca_cert(pal, &req, &resp); if (ret != OPRT_OK) { - log_error("iot_dns_get_ca_cert failed: %d", ret); + IOT_LOGE("iot_dns_get_ca_cert failed: %d", ret); return ret; } size_t cert_len = resp.ca_certificate ? strlen(resp.ca_certificate) : 0; if (cert_len == 0) { - log_error("iot_get_ca_certificate: no CA cert for %s:%u", host, port); + IOT_LOGE("iot_get_ca_certificate: no CA cert for %s:%u", host, port); iot_dns_ca_cert_response_free(pal, &resp); return OPRT_INVALID_RESULT; } if (cert_len >= ca_certificate_len) { - log_error("ca_certificate buffer too small: need %zu, have %zu", cert_len + 1, ca_certificate_len); + IOT_LOGE("ca_certificate buffer too small: need %zu, have %zu", cert_len + 1, ca_certificate_len); iot_dns_ca_cert_response_free(pal, &resp); return OPRT_INVALID_RESULT; } @@ -743,7 +743,7 @@ IOT_API int iot_get_qrcode_info(const iot_qrcode_request_t *request, char *url, { const pal_t *pal = get_pal(); if (!pal) { - log_error("iot_get_qrcode_info: PAL not initialized — call iot_init() first"); + IOT_LOGE("iot_get_qrcode_info: PAL not initialized — call iot_init() first"); return OPRT_UNINITIALIZED; } @@ -773,7 +773,7 @@ IOT_API int iot_get_qrcode_info(const iot_qrcode_request_t *request, char *url, iot_dns_url_config_response_t dns_resp = {0}; int ret = iot_dns_url_config(pal, &dns_req, &dns_resp); if (ret != OPRT_OK) { - log_error("iot_get_qrcode_info: iot_dns_url_config failed: %d", ret); + IOT_LOGE("iot_get_qrcode_info: iot_dns_url_config failed: %d", ret); return ret; } @@ -788,7 +788,7 @@ IOT_API int iot_get_qrcode_info(const iot_qrcode_request_t *request, char *url, iot_dns_url_config_response_free(pal, &dns_resp); if (host[0] == '\0') { - log_error("iot_get_qrcode_info: %s not found in DNS response", IOT_DNS_KEY_HTTPS); + IOT_LOGE("iot_get_qrcode_info: %s not found in DNS response", IOT_DNS_KEY_HTTPS); return OPRT_COMMUNICATION_ERROR; } @@ -806,13 +806,13 @@ IOT_API int iot_get_qrcode_info(const iot_qrcode_request_t *request, char *url, qrcode_info_response_t resp = {0}; ret = atop_qrcode_info_get(pal, &req, &resp); if (ret != OPRT_OK) { - log_error("atop_qrcode_info_get failed: %d", ret); + IOT_LOGE("atop_qrcode_info_get failed: %d", ret); return ret; } size_t resp_url_len = strlen(resp.short_url); if (resp_url_len >= url_len) { - log_error("url buffer too small: need %zu, have %zu", resp_url_len + 1, url_len); + IOT_LOGE("url buffer too small: need %zu, have %zu", resp_url_len + 1, url_len); pal->free(resp.short_url); return OPRT_INVALID_RESULT; } diff --git a/modules/iot-client/src/iot_client_message.c b/modules/iot-client/src/iot_client_message.c index 14070df..453040a 100644 --- a/modules/iot-client/src/iot_client_message.c +++ b/modules/iot-client/src/iot_client_message.c @@ -17,7 +17,7 @@ static void mqtt_message_handler(const char *topic, size_t topic_len, uint8_t *decrypted = (uint8_t *)client->pal->malloc(payload_len); if (!decrypted) { - log_error("Failed to allocate buffer for MQTT message decryption"); + IOT_LOGE("Failed to allocate buffer for MQTT message decryption"); return; } @@ -39,7 +39,7 @@ static void mqtt_message_handler(const char *topic, size_t topic_len, } } else { /* Decryption failed: never feed raw ciphertext to the DP parser. */ - log_warn("pv23_decrypt failed (ret=%d), forwarding raw payload", ret); + IOT_LOGW("pv23_decrypt failed (ret=%d), forwarding raw payload", ret); if (client->message_callback) { client->message_callback(topic, topic_len, payload, payload_len); } @@ -76,7 +76,7 @@ bool iot_client_message_handle_reset(iot_client_t *client, cJSON *jgw = cJSON_GetObjectItem(data, "gwId"); if (jgw && cJSON_IsString(jgw) && client->devid[0] != '\0') { if (strcmp(jgw->valuestring, client->devid) != 0) { - log_warn("reset: gwId mismatch (got '%s', expected '%s') — consumed, not ours", + IOT_LOGW("reset: gwId mismatch (got '%s', expected '%s') — consumed, not ours", jgw->valuestring, client->devid); cJSON_Delete(root); return true; @@ -94,7 +94,7 @@ bool iot_client_message_handle_reset(iot_client_t *client, type = IOT_RESET_REMOTE_FACTORY; } - log_warn("reset: device-remove notice received (type=%s)", + IOT_LOGW("reset: device-remove notice received (type=%s)", type == IOT_RESET_REMOTE_FACTORY ? "factory" : "unbind"); client->reset_callback(type, client->reset_user_data); @@ -134,7 +134,7 @@ bool iot_client_message_handle_ota_confirm(iot_client_t *client, channel = jchannel->valueint; } - log_info("ota confirm: app-confirmed upgrade notice received (channel=%d)", channel); + IOT_LOGI("ota confirm: app-confirmed upgrade notice received (channel=%d)", channel); client->ota_confirm_callback(channel, client->ota_confirm_user_data); cJSON_Delete(root); @@ -150,14 +150,14 @@ static int iot_client_message_try_connect(iot_client_t *client) int sn_ret = snprintf(subscribe_topic, sizeof(subscribe_topic), "smart/device/in/%s", client->devid); if (sn_ret < 0 || (size_t)sn_ret >= (int)sizeof(subscribe_topic)) { - log_error("Failed to build subscribe topic: %d", sn_ret); + IOT_LOGE("Failed to build subscribe topic: %d", sn_ret); return OPRT_COMMUNICATION_ERROR; } char password[17] = {0}; int md5_ret = iot_md5_password(client->secret_key, password); if (md5_ret != 0) { - log_error("iot_md5_password failed: %d", md5_ret); + IOT_LOGE("iot_md5_password failed: %d", md5_ret); return OPRT_COMMUNICATION_ERROR; } @@ -177,13 +177,13 @@ static int iot_client_message_try_connect(iot_client_t *client) client->mqtt = mqtt_client_create_with_config(&mqtt_cfg); if (!client->mqtt) { - log_error("Failed to create MQTT client"); + IOT_LOGE("Failed to create MQTT client"); return OPRT_COMMUNICATION_ERROR; } int ret = mqtt_client_connect(client->mqtt); if (ret != 0) { - log_error("Failed to connect to MQTT broker: %d", ret); + IOT_LOGE("Failed to connect to MQTT broker: %d", ret); mqtt_client_destroy(client->mqtt); client->mqtt = NULL; return (ret == OPRT_TLS_HANDSHAKE_FAILED) ? OPRT_TLS_HANDSHAKE_FAILED @@ -191,13 +191,13 @@ static int iot_client_message_try_connect(iot_client_t *client) } if (mqtt_client_subscribe(client->mqtt) != 0) { - log_error("Failed to subscribe to %s", subscribe_topic); + IOT_LOGE("Failed to subscribe to %s", subscribe_topic); mqtt_client_destroy(client->mqtt); client->mqtt = NULL; return OPRT_COMMUNICATION_ERROR; } - log_info("MQTT connected and subscribed to %s", subscribe_topic); + IOT_LOGI("MQTT connected and subscribed to %s", subscribe_topic); return OPRT_OK; } @@ -215,7 +215,7 @@ int iot_client_message_connect(iot_client_t *client) * link up: an app that also left mqtt_auto_connect true calls it on an * already-connected client. To force a fresh link, disconnect first. */ if (client->mqtt) { - log_warn("iot_client_message_connect: already connected, ignoring " + IOT_LOGW("iot_client_message_connect: already connected, ignoring " "(call iot_client_disconnect() first to reconnect)"); return OPRT_OK; } @@ -254,7 +254,7 @@ int iot_client_message_publish(iot_client_t *client, size_t enc_buf_len = data_len + PV23_OVERHEAD; uint8_t *encrypted = (uint8_t *)client->pal->malloc(enc_buf_len); if (!encrypted) { - log_error("Failed to allocate buffer for message encryption"); + IOT_LOGE("Failed to allocate buffer for message encryption"); return OPRT_MALLOC_FAILED; } @@ -263,7 +263,7 @@ int iot_client_message_publish(iot_client_t *client, (const uint8_t *)client->local_key, encrypted, &encrypted_len); if (ret != 0) { - log_error("pv23_encrypt failed: %d", ret); + IOT_LOGE("pv23_encrypt failed: %d", ret); client->pal->free(encrypted); return OPRT_COMMUNICATION_ERROR; } @@ -276,7 +276,7 @@ int iot_client_message_publish(iot_client_t *client, int sn = snprintf(pub_topic, sizeof(pub_topic), "smart/device/out/%s", client->devid); if (sn < 0 || (size_t)sn >= sizeof(pub_topic)) { - log_error("Failed to build publish topic: %d", sn); + IOT_LOGE("Failed to build publish topic: %d", sn); client->pal->free(encrypted); return OPRT_COMMUNICATION_ERROR; } @@ -284,11 +284,11 @@ int iot_client_message_publish(iot_client_t *client, ret = mqtt_client_publish(client->mqtt, pub_topic, encrypted, encrypted_len); client->pal->free(encrypted); if (ret != 0) { - log_error("Failed to publish to %s", pub_topic); + IOT_LOGE("Failed to publish to %s", pub_topic); return OPRT_COMMUNICATION_ERROR; } - log_debug("Published encrypted message to %s (%u bytes)", + IOT_LOGD("Published encrypted message to %s (%u bytes)", pub_topic, (unsigned)encrypted_len); return OPRT_OK; } diff --git a/modules/iot-client/src/iot_dns.c b/modules/iot-client/src/iot_dns.c index d43dfd8..ca43fd8 100644 --- a/modules/iot-client/src/iot_dns.c +++ b/modules/iot-client/src/iot_dns.c @@ -51,19 +51,19 @@ static int dns_http_request(const pal_t *pal, const char *host, uint16_t port, c &http_resp); if (status != HTTP_CLIENT_SUCCESS) { - log_error("iot_dns: %s %s failed: %d", method, path, status); + IOT_LOGE("iot_dns: %s %s failed: %d", method, path, status); return OPRT_COMMUNICATION_ERROR; } if (!http_resp.body || http_resp.body_length == 0) { - log_error("iot_dns: empty response from %s %s", method, path); + IOT_LOGE("iot_dns: empty response from %s %s", method, path); http_client_free(pal, &http_resp); return OPRT_COMMUNICATION_ERROR; } char *body_str = pal->malloc(http_resp.body_length + 1); if (!body_str) { - log_error("iot_dns: alloc response buffer failed (%u bytes)", (unsigned)(http_resp.body_length + 1)); + IOT_LOGE("iot_dns: alloc response buffer failed (%u bytes)", (unsigned)(http_resp.body_length + 1)); http_client_free(pal, &http_resp); return OPRT_MALLOC_FAILED; } @@ -74,7 +74,7 @@ static int dns_http_request(const pal_t *pal, const char *host, uint16_t port, c *out_json = cJSON_Parse(body_str); pal->free(body_str); if (!*out_json) { - log_error("iot_dns: failed to parse JSON response from %s %s", method, path); + IOT_LOGE("iot_dns: failed to parse JSON response from %s %s", method, path); return OPRT_COMMUNICATION_ERROR; } return OPRT_OK; @@ -97,7 +97,7 @@ int iot_dns_query(const pal_t *pal, const iot_dns_query_request_t *request, cJSON *req_arr = cJSON_CreateArray(); if (!req_arr) { - log_error("iot_dns: cJSON_CreateArray failed for dns_query request"); + IOT_LOGE("iot_dns: cJSON_CreateArray failed for dns_query request"); return OPRT_MALLOC_FAILED; } @@ -113,7 +113,7 @@ int iot_dns_query(const pal_t *pal, const iot_dns_query_request_t *request, char *json_body = cJSON_PrintUnformatted(req_arr); cJSON_Delete(req_arr); if (!json_body) { - log_error("iot_dns: cJSON_PrintUnformatted failed for dns_query body"); + IOT_LOGE("iot_dns: cJSON_PrintUnformatted failed for dns_query body"); return OPRT_MALLOC_FAILED; } @@ -126,7 +126,7 @@ int iot_dns_query(const pal_t *pal, const iot_dns_query_request_t *request, response->results = pal->malloc( sizeof(iot_dns_domain_result_t) * request->domain_count); if (!response->results) { - log_error("iot_dns: alloc dns_query results failed (%d domains)", request->domain_count); + IOT_LOGE("iot_dns: alloc dns_query results failed (%d domains)", request->domain_count); cJSON_Delete(root); return OPRT_MALLOC_FAILED; } @@ -185,7 +185,7 @@ int iot_dns_url_config(const pal_t *pal, const iot_dns_url_config_request_t *req cJSON *req_obj = cJSON_CreateObject(); if (!req_obj) { - log_error("iot_dns: cJSON_CreateObject failed for url_config request"); + IOT_LOGE("iot_dns: cJSON_CreateObject failed for url_config request"); return OPRT_MALLOC_FAILED; } @@ -207,7 +207,7 @@ int iot_dns_url_config(const pal_t *pal, const iot_dns_url_config_request_t *req char *json_body = cJSON_PrintUnformatted(req_obj); cJSON_Delete(req_obj); if (!json_body) { - log_error("iot_dns: cJSON_PrintUnformatted failed for url_config body"); + IOT_LOGE("iot_dns: cJSON_PrintUnformatted failed for url_config body"); return OPRT_MALLOC_FAILED; } @@ -229,7 +229,7 @@ int iot_dns_url_config(const pal_t *pal, const iot_dns_url_config_request_t *req if (n > 0) { response->ca_arr = pal->malloc(sizeof(char *) * n); if (!response->ca_arr) { - log_error("iot_dns: alloc caArr failed (%d entries)", n); + IOT_LOGE("iot_dns: alloc caArr failed (%d entries)", n); cJSON_Delete(root); return OPRT_MALLOC_FAILED; } @@ -251,7 +251,7 @@ int iot_dns_url_config(const pal_t *pal, const iot_dns_url_config_request_t *req response->endpoints = pal->malloc(sizeof(iot_dns_endpoint_t) * max_ep); if (!response->endpoints) { - log_error("iot_dns: alloc endpoints failed (%d entries)", max_ep); + IOT_LOGE("iot_dns: alloc endpoints failed (%d entries)", max_ep); cJSON_Delete(root); iot_dns_url_config_response_free(pal, response); return OPRT_MALLOC_FAILED; @@ -326,14 +326,14 @@ int iot_dns_get_ca_cert(const pal_t *pal, const iot_dns_ca_cert_request_t *reque size_t path_len = 62 + host_len + algo_len; char *path = (char *)pal->malloc(path_len); if (!path) { - log_error("iot_dns: alloc ca-cert path failed (%u bytes)", (unsigned)path_len); + IOT_LOGE("iot_dns: alloc ca-cert path failed (%u bytes)", (unsigned)path_len); return OPRT_MALLOC_FAILED; } int sn = snprintf(path, path_len, "/api/v1/ca-certificate?host=%s&port=%u&public_key_algorithm=%s", request->target_host, target_port, algo); if (sn < 0 || (size_t)sn >= path_len) { - log_error("iot_dns: build ca-cert path failed: ret %d, cap %u", sn, (unsigned)path_len); + IOT_LOGE("iot_dns: build ca-cert path failed: ret %d, cap %u", sn, (unsigned)path_len); pal->free(path); return OPRT_COMMUNICATION_ERROR; } @@ -354,7 +354,7 @@ int iot_dns_get_ca_cert(const pal_t *pal, const iot_dns_ca_cert_request_t *reque cJSON_Delete(root); if (!response->ca_certificate) { - log_error("iot_dns: alloc ca_certificate failed"); + IOT_LOGE("iot_dns: alloc ca_certificate failed"); return OPRT_MALLOC_FAILED; } return OPRT_OK; diff --git a/modules/iot-client/src/iot_dp.c b/modules/iot-client/src/iot_dp.c index 2a5933c..d1014a1 100644 --- a/modules/iot-client/src/iot_dp.c +++ b/modules/iot-client/src/iot_dp.c @@ -110,7 +110,7 @@ static struct iot_dp_context *dp_ensure_context(iot_client_t *client) ctx->loose = true; ctx->mutex = pal->mutex_create(); if (!ctx->mutex) { - log_error("dp: mutex creation failed"); + IOT_LOGE("dp: mutex creation failed"); return NULL; /* dp_storage is inline; nothing to free */ } client->dp = ctx; @@ -126,7 +126,7 @@ static char *dp_base64_encode(const pal_t *pal, const uint8_t *data, size_t len) if (olen == 0) olen = 1; char *out = (char *)pal->malloc(olen); if (!out) { - log_error("dp: base64 encode alloc failed (%zu bytes)", olen); + IOT_LOGE("dp: base64 encode alloc failed (%zu bytes)", olen); return NULL; } if (mbedtls_base64_encode((unsigned char *)out, olen, &olen, src, len) != 0) { @@ -188,11 +188,11 @@ static iot_dp_entry_t *dp_parse_schema(const pal_t *pal, const char *schema, siz cJSON *root = cJSON_Parse(schema); if (!root) { - log_warn("dp: schema parse failed -> loose mode"); + IOT_LOGW("dp: schema parse failed -> loose mode"); return NULL; } if (!cJSON_IsArray(root)) { - log_warn("dp: schema is not a JSON array -> loose mode"); + IOT_LOGW("dp: schema is not a JSON array -> loose mode"); cJSON_Delete(root); return NULL; } @@ -204,7 +204,7 @@ static iot_dp_entry_t *dp_parse_schema(const pal_t *pal, const char *schema, siz iot_dp_entry_t *entries = (iot_dp_entry_t *)pal->malloc(sizeof(iot_dp_entry_t) * (size_t)n); if (!entries) { - log_error("dp: alloc schema entries failed (%d DPs)", n); + IOT_LOGE("dp: alloc schema entries failed (%d DPs)", n); cJSON_Delete(root); return NULL; } @@ -297,7 +297,7 @@ void iot_dp_rebuild(iot_client_t *client) dp_entries_free(pal, old_entries, old_count); /* Use the local copy, not ctx->loose, which is shared state read here without * the lock. */ - log_info("dp: registry rebuilt (%zu DPs, %s)", new_count, loose ? "loose" : "schema"); + IOT_LOGI("dp: registry rebuilt (%zu DPs, %s)", new_count, loose ? "loose" : "schema"); } /* ============================================================================ @@ -354,7 +354,7 @@ static int dp_store(const pal_t *pal, iot_dp_entry_t *e, const iot_dp_value_t *v case IOT_DP_TYPE_STRING: { char *copy = pal_strdup(pal, v->value.string ? v->value.string : ""); if (!copy) { - log_error("dp: string value alloc failed for dp %u", (unsigned)e->id); + IOT_LOGE("dp: string value alloc failed for dp %u", (unsigned)e->id); return OPRT_MALLOC_FAILED; } if (e->str_buf) pal->free(e->str_buf); @@ -367,7 +367,7 @@ static int dp_store(const pal_t *pal, iot_dp_entry_t *e, const iot_dp_value_t *v if (v->value.raw.len > 0) { copy = (uint8_t *)pal->malloc(v->value.raw.len); if (!copy) { - log_error("dp: raw value alloc failed for dp %u (%zu bytes)", (unsigned)e->id, v->value.raw.len); + IOT_LOGE("dp: raw value alloc failed for dp %u (%zu bytes)", (unsigned)e->id, v->value.raw.len); return OPRT_MALLOC_FAILED; } memcpy(copy, v->value.raw.data, v->value.raw.len); @@ -685,7 +685,7 @@ static int dp_publish_report(iot_client_t *client, char *json) const pal_t *pal = client->pal; size_t jl = strlen(json); if (jl + DP_PV23_OVERHEAD > DP_MQTT_MAX_PAYLOAD) { - log_error("dp: report payload too large (%zu bytes, max %d) — split via iot_dp_report", + IOT_LOGE("dp: report payload too large (%zu bytes, max %d) — split via iot_dp_report", jl, DP_MQTT_MAX_PAYLOAD - DP_PV23_OVERHEAD); pal->free(json); return OPRT_DP_PAYLOAD_TOO_LARGE; @@ -820,7 +820,7 @@ int iot_dp_validate_json(iot_client_t *client, const char *dp_state_json) cJSON *root = cJSON_Parse(dp_state_json); if (!root) { - log_warn("dp: validate JSON parse failed"); + IOT_LOGW("dp: validate JSON parse failed"); return OPRT_DP_SCHEMA_PARSE_FAILED; } cJSON *dps = cJSON_GetObjectItem(root, "dps"); @@ -838,7 +838,7 @@ int iot_dp_validate_json(iot_client_t *client, const char *dp_state_json) if (id < 0 || id > 255) { rt = OPRT_DP_INVALID_ID; break; } rt = dp_apply_json(ctx, pal, (uint8_t)id, kv, /*dirty*/false, /*check_only*/true); if (rt != OPRT_OK) { - log_warn("dp: validate dp %d failed (%d)", id, rt); + IOT_LOGW("dp: validate dp %d failed (%d)", id, rt); break; } } @@ -857,7 +857,7 @@ int iot_dp_restore_json(iot_client_t *client, const char *dp_state_json) cJSON *root = cJSON_Parse(dp_state_json); if (!root) { - log_warn("dp: restore JSON parse failed"); + IOT_LOGW("dp: restore JSON parse failed"); return OPRT_DP_SCHEMA_PARSE_FAILED; } cJSON *dps = cJSON_GetObjectItem(root, "dps"); @@ -873,7 +873,7 @@ int iot_dp_restore_json(iot_client_t *client, const char *dp_state_json) int id = atoi(kv->string); if (id < 0 || id > 255) continue; int rt = dp_apply_json(ctx, pal, (uint8_t)id, kv, /*dirty*/false, /*check_only*/false); - if (rt != OPRT_OK) log_warn("dp: restore dp %d skipped (%d)", id, rt); + if (rt != OPRT_OK) IOT_LOGW("dp: restore dp %d skipped (%d)", id, rt); } pal->mutex_unlock(ctx->mutex); @@ -890,7 +890,7 @@ int iot_dp_schema_check_update(iot_client_t *client) if (!client) return OPRT_INVALID_PARAMETER; const pal_t *pal = client->pal; if (client->schema_id[0] == '\0') { - log_warn("dp: schema_id is empty; cannot check for schema update"); + IOT_LOGW("dp: schema_id is empty; cannot check for schema update"); return OPRT_INVALID_PARAMETER; } @@ -912,7 +912,7 @@ int iot_dp_schema_check_update(iot_client_t *client) schema_newest_response_t resp = {0}; int rt = atop_schema_newest_get(pal, &req, &resp); if (rt != OPRT_OK) { - log_warn("dp: schema newest get failed (%d)", rt); + IOT_LOGW("dp: schema newest get failed (%d)", rt); atop_schema_newest_response_free(pal, &resp); return rt; } @@ -924,7 +924,7 @@ int iot_dp_schema_check_update(iot_client_t *client) char *new_schema = pal_strdup(pal, resp.schema); if (!new_schema) { - log_error("dp: schema copy alloc failed"); + IOT_LOGE("dp: schema copy alloc failed"); atop_schema_newest_response_free(pal, &resp); return OPRT_MALLOC_FAILED; } @@ -1046,7 +1046,7 @@ bool iot_dp_dispatch_downlink(iot_client_t *client, const char *topic, size_t to if (rt == OPRT_OK) { if (nchanged < sizeof(changed)) changed[nchanged++] = (uint8_t)id; } else { - log_warn("dp: downlink dp %d rejected (%d)", id, rt); + IOT_LOGW("dp: downlink dp %d rejected (%d)", id, rt); } } dp_cb = ctx->dp_cb; @@ -1076,7 +1076,7 @@ bool iot_dp_dispatch_downlink(iot_client_t *client, const char *topic, size_t to } pal->free(snap); } else if (dp_cb && nchanged > 0) { - log_warn("dp: callback snapshot alloc failed; skipped %zu downlink callbacks", nchanged); + IOT_LOGW("dp: callback snapshot alloc failed; skipped %zu downlink callbacks", nchanged); } if (nchanged > 0) dp_fire_save(client); } diff --git a/modules/iot-client/src/iot_internal.h b/modules/iot-client/src/iot_internal.h index 885b138..6301fec 100644 --- a/modules/iot-client/src/iot_internal.h +++ b/modules/iot-client/src/iot_internal.h @@ -90,12 +90,34 @@ const pal_t *get_default_pal(void); #endif /* Module-tagged dispatch into the global log facade. Tag is folded - * into the format string at compile time, so the log handler stays - * tag-agnostic and the call site reads exactly like printf(). */ -#define log_error(fmt, ...) log_emit(LOG_ERROR, "[iot] " fmt, ##__VA_ARGS__) -#define log_warn(fmt, ...) log_emit(LOG_WARN, "[iot] " fmt, ##__VA_ARGS__) -#define log_info(fmt, ...) log_emit(LOG_INFO, "[iot] " fmt, ##__VA_ARGS__) -#define log_debug(fmt, ...) log_emit(LOG_DEBUG, "[iot] " fmt, ##__VA_ARGS__) + * into the format string at compile time, so the dispatch stays + * tag-agnostic and the call site reads exactly like a printf call. + * Ceilings: the SDK-wide AGENTIC_KIT_LOG_LEVEL gates once in log.h; + * AGENTIC_KIT_IOT_LOG_LEVEL (defaults to that; iot_client_config_defaults.h) + * optionally lowers just this module further -- never raises. + * Names follow the module-prefix convention of the other modules + * (TAI_LOG*, TUYA_BLE_HAL_LOG*); the old unprefixed log_error family + * read like facade API and leaked into integrator-facing samples. */ +#if AGENTIC_KIT_IOT_LOG_LEVEL >= 1 +#define IOT_LOGE(fmt, ...) log_tag_error("iot", fmt, ##__VA_ARGS__) +#else +#define IOT_LOGE(...) ((void)0) +#endif +#if AGENTIC_KIT_IOT_LOG_LEVEL >= 2 +#define IOT_LOGW(fmt, ...) log_tag_warn("iot", fmt, ##__VA_ARGS__) +#else +#define IOT_LOGW(...) ((void)0) +#endif +#if AGENTIC_KIT_IOT_LOG_LEVEL >= 3 +#define IOT_LOGI(fmt, ...) log_tag_info("iot", fmt, ##__VA_ARGS__) +#else +#define IOT_LOGI(...) ((void)0) +#endif +#if AGENTIC_KIT_IOT_LOG_LEVEL >= 4 +#define IOT_LOGD(fmt, ...) log_tag_debug("iot", fmt, ##__VA_ARGS__) +#else +#define IOT_LOGD(...) ((void)0) +#endif /** * @brief Duplicate a string using PAL's allocator. diff --git a/modules/iot-client/src/iot_on_boarding.c b/modules/iot-client/src/iot_on_boarding.c index e193cc3..9b77f6e 100644 --- a/modules/iot-client/src/iot_on_boarding.c +++ b/modules/iot-client/src/iot_on_boarding.c @@ -38,14 +38,14 @@ static void activate_context_destroy(void); static void parse_activation_message(const pal_t *pal, const char *json_str, activation_message_t *message) { cJSON *root = cJSON_Parse(json_str); if (!root) { - log_error("Failed to parse activation response JSON"); + IOT_LOGE("Failed to parse activation response JSON"); return; } // Get data object cJSON *data = cJSON_GetObjectItem(root, "data"); if (!data) { - log_error("Activation response missing 'data' field"); + IOT_LOGE("Activation response missing 'data' field"); cJSON_Delete(root); return; } @@ -55,7 +55,7 @@ static void parse_activation_message(const pal_t *pal, const char *json_str, act if (https_url && cJSON_IsString(https_url)) { message->https_url = pal_strdup(pal, https_url->valuestring); if (message->https_url) { - log_info("Activation httpsUrl: %s", message->https_url); + IOT_LOGI("Activation httpsUrl: %s", message->https_url); } } @@ -78,13 +78,13 @@ static void parse_activation_message(const pal_t *pal, const char *json_str, act } else if (strcmp(r, "WEAZ") == 0) { message->region = WEAZ; } else { - log_error("Invalid region: %s", r); + IOT_LOGE("Invalid region: %s", r); cJSON_Delete(root); return; } - log_info("on boarding region: %s", r); + IOT_LOGI("on boarding region: %s", r); } else { - log_error("Activation response missing 'region' field"); + IOT_LOGE("Activation response missing 'region' field"); cJSON_Delete(root); return; } @@ -94,7 +94,7 @@ static void parse_activation_message(const pal_t *pal, const char *json_str, act if (token && cJSON_IsString(token)) { strncpy(message->token, token->valuestring, sizeof(message->token) - 1); message->token[sizeof(message->token) - 1] = '\0'; - log_info("Activation token received"); + IOT_LOGI("Activation token received"); } cJSON_Delete(root); @@ -105,15 +105,17 @@ static void internal_message_callback(const char *topic, size_t topic_len, const uint8_t *payload, size_t payload_len, void *user_data) { (void)user_data; - log_info(">>> internal_message_callback called! topic_len=%u, payload_len=%u", (unsigned)topic_len, (unsigned)payload_len); + (void)topic; /* topic/topic_len feed only the trace below; keep */ + (void)topic_len; /* -Wextra quiet when the module ceiling drops it. */ + IOT_LOGI(">>> internal_message_callback called! topic_len=%u, payload_len=%u", (unsigned)topic_len, (unsigned)payload_len); if (!g_activate_ctx) { - log_warn("g_activate_ctx is NULL!"); + IOT_LOGW("g_activate_ctx is NULL!"); return; } - log_info("Received activation message on topic: %.*s", (int)topic_len, topic); - log_debug("Base64 encoded payload (%u bytes)", (unsigned)payload_len); + IOT_LOGI("Received activation message on topic: %.*s", (int)topic_len, topic); + IOT_LOGD("Base64 encoded payload (%u bytes)", (unsigned)payload_len); const pal_t *pal = g_activate_ctx->pal; @@ -132,17 +134,17 @@ static void internal_message_callback(const char *topic, size_t topic_len, if (base64_decoded) { ret = mbedtls_base64_decode(base64_decoded, decoded_len, &decoded_len, payload, payload_len); if (ret == 0) { - log_info("Base64 decoded: %u bytes", (unsigned)decoded_len); + IOT_LOGI("Base64 decoded: %u bytes", (unsigned)decoded_len); final_payload = base64_decoded; final_len = decoded_len; } else { - log_warn("Base64 decode failed (ret=%d), using raw payload", ret); + IOT_LOGW("Base64 decode failed (ret=%d), using raw payload", ret); pal->free(base64_decoded); base64_decoded = NULL; } } } else { - log_debug("Base64 decode size check failed (ret=%d), assuming raw data", ret); + IOT_LOGD("Base64 decode size check failed (ret=%d), assuming raw data", ret); } } @@ -156,17 +158,17 @@ static void internal_message_callback(const char *topic, size_t topic_len, (const uint8_t *)authkey, decrypted, &decrypted_len); if (ret == 0) { - log_info("Decrypted payload (%u bytes): %.*s", (unsigned)decrypted_len, (int)decrypted_len, decrypted); + IOT_LOGI("Decrypted payload (%u bytes): %.*s", (unsigned)decrypted_len, (int)decrypted_len, decrypted); final_payload = decrypted; final_len = decrypted_len; } else { - log_warn("Failed to decrypt payload (ret=%d), using base64 decoded data", ret); + IOT_LOGW("Failed to decrypt payload (ret=%d), using base64 decoded data", ret); pal->free(decrypted); decrypted = NULL; } } } else { - log_debug("No authkey provided or authkey too short, storing decoded payload"); + IOT_LOGD("No authkey provided or authkey too short, storing decoded payload"); } activation_message_t message = {0}; @@ -192,7 +194,7 @@ static void internal_message_callback(const char *topic, size_t topic_len, } else { g_activate_ctx->message->https_url = NULL; } - log_info("Activation message stored in context"); + IOT_LOGI("Activation message stored in context"); } } @@ -211,7 +213,7 @@ static void internal_message_callback(const char *topic, size_t topic_len, static int activate_device(const pal_t *pal, on_boarding_config_t *on_boarding, const activation_message_t *act_msg, on_boarding_response_t *response) { if (!on_boarding || !response) { - log_error("Invalid parameters for on boarding"); + IOT_LOGE("Invalid parameters for on boarding"); return OPRT_INVALID_PARAMETER; } @@ -277,34 +279,34 @@ static int activate_device(const pal_t *pal, on_boarding_config_t *on_boarding, request.cacert = on_boarding->cacert; request.cert_bundle_attach = on_boarding->cert_bundle_attach; - log_info("Sending activation request with:"); - log_info(" - Token: [%zu chars, prefix=%.4s...]", + IOT_LOGI("Sending activation request with:"); + IOT_LOGI(" - Token: [%zu chars, prefix=%.4s...]", request.token ? strlen(request.token) : 0, (request.token && strlen(request.token) >= 4) ? request.token : "----"); - log_info(" - Software Version: %s", request.sw_ver); - log_info(" - Product Key: %s", request.product_key); - log_info(" - Protocol Version: %s", request.pv); - log_info(" - Baseline Version: %s", request.bv); - log_info(" - UUID: %s", request.uuid); + IOT_LOGI(" - Software Version: %s", request.sw_ver); + IOT_LOGI(" - Product Key: %s", request.product_key); + IOT_LOGI(" - Protocol Version: %s", request.pv); + IOT_LOGI(" - Baseline Version: %s", request.bv); + IOT_LOGI(" - UUID: %s", request.uuid); if (request.host) { - log_info(" - Server: %s:%d", request.host, request.port > 0 ? request.port : 443); + IOT_LOGI(" - Server: %s:%d", request.host, request.port > 0 ? request.port : 443); } else { - log_info(" - Server: (default)"); + IOT_LOGI(" - Server: (default)"); } if (request.devid) { - log_info(" - Device ID: %s", request.devid); + IOT_LOGI(" - Device ID: %s", request.devid); } if (request.modules) { - log_info(" - Modules: %s", request.modules); + IOT_LOGI(" - Modules: %s", request.modules); } if (request.feature) { - log_info(" - Feature: %s", request.feature); + IOT_LOGI(" - Feature: %s", request.feature); } if (request.skill_param) { - log_info(" - Skill Param: %s", request.skill_param); + IOT_LOGI(" - Skill Param: %s", request.skill_param); } if (request.firmware_key) { - log_info(" - Firmware Key: (set)"); + IOT_LOGI(" - Firmware Key: (set)"); } // Send activation request @@ -312,31 +314,31 @@ static int activate_device(const pal_t *pal, on_boarding_config_t *on_boarding, int ret = atop_activate_request(pal, &request, &activate_response); if (ret == OPRT_OK) { - log_info("✓ Activation successful!"); + IOT_LOGI("✓ Activation successful!"); - log_info("Device ID: %s", activate_response.devid); + IOT_LOGI("Device ID: %s", activate_response.devid); } else { - log_error("✗ Activation request failed with error code: %d", ret); + IOT_LOGE("✗ Activation request failed with error code: %d", ret); // Map error codes to readable messages switch (ret) { case OPRT_INVALID_PARAMETER: - log_error(" - Invalid parameters"); + IOT_LOGE(" - Invalid parameters"); break; case OPRT_COMMUNICATION_ERROR: - log_error(" - Communication error"); + IOT_LOGE(" - Communication error"); break; case OPRT_ATOP_BUSINESS_ERROR: /* e.g. an expired/used pairing token -- the cloud's errorCode * and errorMsg are logged by the envelope layer just above. */ - log_error(" - Rejected by the cloud (see errorCode above)"); + IOT_LOGE(" - Rejected by the cloud (see errorCode above)"); break; case OPRT_TLS_HANDSHAKE_FAILED: - log_error(" - TLS handshake failed"); + IOT_LOGE(" - TLS handshake failed"); break; default: - log_error(" - Unknown error"); + IOT_LOGE(" - Unknown error"); break; } } @@ -398,7 +400,7 @@ static int __token_to_region(const char *token, iot_region_t *region) } else if (strcmp(prefix, "SG") == 0) { *region = SG; } else { - log_error("Unknown region prefix in token: %s", prefix); + IOT_LOGE("Unknown region prefix in token: %s", prefix); return OPRT_INVALID_PARAMETER; } return OPRT_OK; @@ -408,20 +410,20 @@ int on_boarding_with_token(const pal_t *pal, on_boarding_config_t *on_boarding, const char *token, on_boarding_response_t *response) { if (!on_boarding || !token || token[0] == '\0' || !response) { - log_error("Invalid parameters for on_boarding_with_token"); + IOT_LOGE("Invalid parameters for on_boarding_with_token"); return OPRT_INVALID_PARAMETER; } size_t token_len = strlen(token); if (token_len < 7) { - log_error("Token too short: need at least 7 chars (2 region + token + 4 secret)"); + IOT_LOGE("Token too short: need at least 7 chars (2 region + token + 4 secret)"); return OPRT_INVALID_PARAMETER; } iot_region_t region; int ret = __token_to_region(token, ®ion); if (ret != OPRT_OK) { - log_error("Failed to parse region from token prefix"); + IOT_LOGE("Failed to parse region from token prefix"); return ret; } @@ -438,7 +440,7 @@ int on_boarding_with_token(const pal_t *pal, on_boarding_config_t *on_boarding, act_msg.env = on_boarding->env; act_msg.https_url = iot_region_to_host(region, on_boarding->env); - log_info("on_boarding_with_token: region=%d env=%d", + IOT_LOGI("on_boarding_with_token: region=%d env=%d", region, on_boarding->env); return activate_device(pal, on_boarding, &act_msg, response); @@ -450,7 +452,7 @@ static int activate_context_init(const pal_t *pal) { } g_activate_ctx = (activate_context_t *)pal->malloc(sizeof(activate_context_t)); if (!g_activate_ctx) { - log_error("Failed to allocate activate context"); + IOT_LOGE("Failed to allocate activate context"); return OPRT_MALLOC_FAILED; } g_activate_ctx->received_activation_message = false; @@ -462,7 +464,7 @@ static int activate_context_init(const pal_t *pal) { static void activate_context_destroy() { if (!g_activate_ctx) { - log_error("Activate context is not initialized"); + IOT_LOGE("Activate context is not initialized"); return; } const pal_t *pal = g_activate_ctx->pal; @@ -476,18 +478,18 @@ static void activate_context_destroy() { int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, on_boarding_response_t *response) { if (!on_boarding || !response) { - log_error("Invalid parameters for on boarding"); + IOT_LOGE("Invalid parameters for on boarding"); return OPRT_INVALID_PARAMETER; } if (g_on_boarding_in_progress) { - log_error("on_boarding_with_qrcode: already in progress"); + IOT_LOGE("on_boarding_with_qrcode: already in progress"); return OPRT_NOT_SUPPORTED; } g_on_boarding_in_progress = true; if (activate_context_init(pal) != 0) { - log_error("Failed to initialize activate context"); + IOT_LOGE("Failed to initialize activate context"); return OPRT_MALLOC_FAILED; } @@ -507,7 +509,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, // clientId: acon_{uuid} client_id = (char *)pal->malloc(6 + uuid_len); if (!client_id) { - log_error("Failed to allocate client_id buffer (%zu bytes)", 6 + uuid_len); + IOT_LOGE("Failed to allocate client_id buffer (%zu bytes)", 6 + uuid_len); ret = OPRT_MALLOC_FAILED; goto end; } @@ -517,7 +519,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, // username: acon_{uuid}|pv=2.3 username = (char *)pal->malloc(14 + uuid_len); if (!username) { - log_error("Failed to allocate username buffer (%zu bytes)", 14 + uuid_len); + IOT_LOGE("Failed to allocate username buffer (%zu bytes)", 14 + uuid_len); ret = OPRT_MALLOC_FAILED; goto end; } @@ -527,7 +529,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, // password: first 16 chars of MD5(authkey) char password[17]; if (iot_md5_password(on_boarding->authkey, password) != 0) { - log_error("Failed to compute MQTT auth password (MD5)"); + IOT_LOGE("Failed to compute MQTT auth password (MD5)"); ret = OPRT_COMMUNICATION_ERROR; goto end; } @@ -535,15 +537,15 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, // subscribe topic: d/ai/{uuid} subscribe_topic = (char *)pal->malloc(6 + uuid_len); if (!subscribe_topic) { - log_error("Failed to allocate subscribe_topic buffer (%zu bytes)", 6 + uuid_len); + IOT_LOGE("Failed to allocate subscribe_topic buffer (%zu bytes)", 6 + uuid_len); ret = OPRT_MALLOC_FAILED; goto end; } sn = snprintf(subscribe_topic, 6 + uuid_len, "d/ai/%s", on_boarding->uuid); if (sn < 0 || (size_t)sn >= 6 + uuid_len) { ret = OPRT_COMMUNICATION_ERROR; goto end; } - log_info("Device activation starting..."); - log_info(" subscribe_topic: %s", subscribe_topic); + IOT_LOGI("Device activation starting..."); + IOT_LOGI(" subscribe_topic: %s", subscribe_topic); // Query MQTT endpoint from IoT DNS service @@ -565,7 +567,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, iot_dns_url_config_response_t dns_resp = {0}; int dns_ret = iot_dns_url_config(pal, &dns_req, &dns_resp); if (dns_ret != OPRT_OK) { - log_error("Failed to query IoT DNS for MQTT endpoint: %d", dns_ret); + IOT_LOGE("Failed to query IoT DNS for MQTT endpoint: %d", dns_ret); ret = OPRT_COMMUNICATION_ERROR; goto end; } @@ -578,7 +580,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, } } if (!mqtt_addr) { - log_error("IoT DNS returned no %s endpoint", mqtt_dns_key); + IOT_LOGE("IoT DNS returned no %s endpoint", mqtt_dns_key); iot_dns_url_config_response_free(pal, &dns_resp); ret = OPRT_INVALID_RESULT; goto end; @@ -588,7 +590,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, size_t broker_url_len = strlen(scheme) + 3 + strlen(mqtt_addr) + 1; broker_url = (char *)pal->malloc(broker_url_len); if (!broker_url) { - log_error("Failed to allocate broker URL buffer (%zu bytes)", broker_url_len); + IOT_LOGE("Failed to allocate broker URL buffer (%zu bytes)", broker_url_len); iot_dns_url_config_response_free(pal, &dns_resp); ret = OPRT_MALLOC_FAILED; goto end; @@ -599,7 +601,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, ret = OPRT_COMMUNICATION_ERROR; goto end; } - log_info("MQTT broker URL from DNS: %s", broker_url); + IOT_LOGI("MQTT broker URL from DNS: %s", broker_url); mqtt_tls_config_t tls_config = {0}; if (!on_boarding->mqtt_disable_tls) { @@ -608,7 +610,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, } else if (on_boarding->cert_bundle_attach) { tls_config.cert_bundle_attach = on_boarding->cert_bundle_attach; } else { - log_warn("MQTT TLS without CA certificate - server verification disabled"); + IOT_LOGW("MQTT TLS without CA certificate - server verification disabled"); } tls_config.verify_peer = false; } @@ -629,14 +631,14 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, client = mqtt_client_create_with_config(&mqtt_config); iot_dns_url_config_response_free(pal, &dns_resp); if (!client) { - log_error("Failed to create MQTT client for activation"); + IOT_LOGE("Failed to create MQTT client for activation"); ret = OPRT_MALLOC_FAILED; goto end; } // Connect to broker if (mqtt_client_connect(client) != 0) { - log_error("Failed to connect to MQTT broker"); + IOT_LOGE("Failed to connect to MQTT broker"); mqtt_client_destroy(client); client = NULL; ret = OPRT_COMMUNICATION_ERROR; @@ -645,7 +647,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, // Subscribe to activation topic if (mqtt_client_subscribe(client) != 0) { - log_error("Failed to subscribe to activation topic"); + IOT_LOGE("Failed to subscribe to activation topic"); mqtt_client_disconnect(client); mqtt_client_destroy(client); client = NULL; @@ -653,7 +655,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, goto end; } - log_info("Subscribed to %s, waiting for activation message...", subscribe_topic); + IOT_LOGI("Subscribed to %s, waiting for activation message...", subscribe_topic); // Wait for activation message uint32_t elapsed_ms = 0; @@ -662,7 +664,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, uint32_t timeout_ms = on_boarding->timeout_ms > 0 ? on_boarding->timeout_ms : 0xFFFFFFFF; while (!g_activate_ctx->received_activation_message && elapsed_ms < timeout_ms) { if (mqtt_client_process(client, poll_interval_ms) != 0) { - log_warn("MQTT process returned error"); + IOT_LOGW("MQTT process returned error"); } elapsed_ms += poll_interval_ms; } @@ -671,7 +673,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, client = NULL; if (elapsed_ms >= timeout_ms) { - log_error("Timeout waiting for activation message"); + IOT_LOGE("Timeout waiting for activation message"); ret = OPRT_COMMUNICATION_ERROR; goto end; } @@ -679,7 +681,7 @@ int on_boarding_with_qrcode(const pal_t *pal, on_boarding_config_t *on_boarding, g_activate_ctx->message->env = on_boarding->env; ret = activate_device(pal, on_boarding, g_activate_ctx->message, response); if (ret != OPRT_OK) { - log_error("Failed to activate device: %d", ret); + IOT_LOGE("Failed to activate device: %d", ret); } end: pal->free(client_id); diff --git a/modules/iot-client/src/iot_ota.c b/modules/iot-client/src/iot_ota.c index 415e4cc..1b29906 100644 --- a/modules/iot-client/src/iot_ota.c +++ b/modules/iot-client/src/iot_ota.c @@ -67,7 +67,7 @@ int iot_ota_check_upgrade(iot_client_t *client, int channel, ota_upgrade_response_t resp = {0}; int rt = atop_upgrade_get(client->pal, &req, &resp); if (rt != OPRT_OK) { - log_error("atop_upgrade_get failed: %d", rt); + IOT_LOGE("atop_upgrade_get failed: %d", rt); return rt; } diff --git a/modules/iot-client/src/iot_ota_verify.c b/modules/iot-client/src/iot_ota_verify.c index 2c90a0d..7546f7c 100644 --- a/modules/iot-client/src/iot_ota_verify.c +++ b/modules/iot-client/src/iot_ota_verify.c @@ -152,7 +152,7 @@ int iot_ota_verify_init(iot_client_t *client, mbedtls_sha256_init(&ctx->digest.sha256); ret = mbedtls_sha256_starts(&ctx->digest.sha256, 0); if (ret != 0) { - log_error("sha256 starts failed: -0x%04x", -ret); + IOT_LOGE("sha256 starts failed: -0x%04x", -ret); ctx_free(ctx); return ret; } @@ -160,7 +160,7 @@ int iot_ota_verify_init(iot_client_t *client, mbedtls_md5_init(&ctx->digest.md5); ret = mbedtls_md5_starts(&ctx->digest.md5); if (ret != 0) { - log_error("md5 starts failed: -0x%04x", -ret); + IOT_LOGE("md5 starts failed: -0x%04x", -ret); ctx_free(ctx); return ret; } @@ -184,7 +184,7 @@ int iot_ota_verify_update(iot_ota_verify_ctx_t *ctx, ret = mbedtls_md5_update(&ctx->digest.md5, data, len); } if (ret != 0) { - log_error("digest update failed: -0x%04x", -ret); + IOT_LOGE("digest update failed: -0x%04x", -ret); return ret; } return OPRT_OK; @@ -203,7 +203,7 @@ int iot_ota_verify_finish(iot_ota_verify_ctx_t *ctx) uint8_t sha[32]; ret = mbedtls_sha256_finish(&ctx->digest.sha256, sha); if (ret != 0) { - log_error("sha256 finish failed: -0x%04x", -ret); + IOT_LOGE("sha256 finish failed: -0x%04x", -ret); ctx_free(ctx); return ret; } @@ -218,7 +218,7 @@ int iot_ota_verify_finish(iot_ota_verify_ctx_t *ctx) ret = mbedtls_md_hmac(md_info, ctx->key, ctx->key_len, (const uint8_t *)sha_hex, OTA_SHA256_HEX_LEN, mac); if (ret != 0) { - log_error("hmac failed: -0x%04x", -ret); + IOT_LOGE("hmac failed: -0x%04x", -ret); ctx_free(ctx); return ret; } @@ -227,7 +227,7 @@ int iot_ota_verify_finish(iot_ota_verify_ctx_t *ctx) uint8_t md5[16]; ret = mbedtls_md5_finish(&ctx->digest.md5, md5); if (ret != 0) { - log_error("md5 finish failed: -0x%04x", -ret); + IOT_LOGE("md5 finish failed: -0x%04x", -ret); ctx_free(ctx); return ret; } diff --git a/modules/iot-client/src/mqtt.c b/modules/iot-client/src/mqtt.c index 831fe86..1958d84 100644 --- a/modules/iot-client/src/mqtt.c +++ b/modules/iot-client/src/mqtt.c @@ -83,7 +83,7 @@ static int32_t transport_send(NetworkContext_t *pNetworkContext, int r = tls_write(pNetworkContext->tls, (const uint8_t *)pBuffer, bytesToSend, 30000 /* 30s */); if (r != TLS_OK) { - log_error("TLS write error"); + IOT_LOGE("TLS write error"); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytesToSend; @@ -98,7 +98,7 @@ static int32_t transport_send(NetworkContext_t *pNetworkContext, return 0; } if (bytes_sent < 0) { - log_error("TCP send failed: %d", bytes_sent); + IOT_LOGE("TCP send failed: %d", bytes_sent); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytes_sent; @@ -124,7 +124,7 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, // no-data to TLS_ERR_AGAIN, so 0 here always means the peer closed. // Must surface as an error: returning 0 would look like an idle poll // and the dead link would only be noticed at the keepalive timeout. - log_error("TLS read error/closed (n=%d)", n); + IOT_LOGE("TLS read error/closed (n=%d)", n); return OPRT_COMMUNICATION_ERROR; } return n; // >0 bytes @@ -139,7 +139,7 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, return OPRT_OK; } if (bytes_received < 0) { - log_error("TCP recv failed: %d", bytes_received); + IOT_LOGE("TCP recv failed: %d", bytes_received); return OPRT_COMMUNICATION_ERROR; } if (bytes_received == 0) { @@ -148,7 +148,7 @@ static int32_t transport_recv(NetworkContext_t *pNetworkContext, * an idle poll, so a dropped link would go unnoticed until the * 60 s keepalive expired. This branch used to be the asymmetric * one; only mqtt_disable_tls=true builds ever reached it. */ - log_error("TCP peer closed the connection"); + IOT_LOGE("TCP peer closed the connection"); return OPRT_COMMUNICATION_ERROR; } return (int32_t)bytes_received; @@ -185,10 +185,10 @@ static void *connect_to_broker(mqtt_client *client) { void *handle = client->pal->tcp_connect(client->broker_host, (uint16_t)client->broker_port, AGENTIC_KIT_MQTT_CONNECT_TIMEOUT_MS); if (!handle) { - log_error("Failed to connect to broker %s:%d", client->broker_host, client->broker_port); + IOT_LOGE("Failed to connect to broker %s:%d", client->broker_host, client->broker_port); return NULL; } - log_info("TCP connection established to %s:%d", client->broker_host, client->broker_port); + IOT_LOGI("TCP connection established to %s:%d", client->broker_host, client->broker_port); return handle; } @@ -197,7 +197,7 @@ static int connect_to_broker_tls(NetworkContext_t *network_ctx, const char *host const char *cacert, tls_cert_bundle_attach_fn cert_bundle_attach) { bool has_cacert = (cacert && cacert[0] != '\0'); if (!has_cacert && !cert_bundle_attach) { - log_warn("No CA certificate provided - server verification disabled"); + IOT_LOGW("No CA certificate provided - server verification disabled"); } tls_config_t cfg = { @@ -215,11 +215,11 @@ static int connect_to_broker_tls(NetworkContext_t *network_ctx, const char *host network_ctx->tls = tls_connect(&cfg); if (!network_ctx->tls) { - log_error("Failed to establish TLS connection to %s:%d", host, port); + IOT_LOGE("Failed to establish TLS connection to %s:%d", host, port); return OPRT_TLS_HANDSHAKE_FAILED; } network_ctx->use_tls = true; - log_info("TLS connection established to %s:%d", host, port); + IOT_LOGI("TLS connection established to %s:%d", host, port); return OPRT_OK; } @@ -238,17 +238,17 @@ static void mqtt_event_callback(MQTTContext_t *pMqttContext, MQTTDeserializedInfo_t *pDeserializedInfo) { NetworkContext_t *pNetworkContext = (NetworkContext_t *)pMqttContext->transportInterface.pNetworkContext; if (!pNetworkContext || !pNetworkContext->client) { - log_warn("mqtt_event_callback: invalid network context"); + IOT_LOGW("mqtt_event_callback: invalid network context"); return; } mqtt_client *client = pNetworkContext->client; - log_debug("MQTT event callback: packet type = 0x%02X", pPacketInfo->type); + IOT_LOGD("MQTT event callback: packet type = 0x%02X", pPacketInfo->type); if ((pPacketInfo->type & 0xF0U) == MQTT_PACKET_TYPE_PUBLISH) { MQTTPublishInfo_t *pPublish = pDeserializedInfo->pPublishInfo; - log_info("Received PUBLISH: topic=%.*s, payload_len=%u", + IOT_LOGI("Received PUBLISH: topic=%.*s, payload_len=%u", (int)pPublish->topicNameLength, pPublish->pTopicName, (unsigned)pPublish->payloadLength); if (client->message_callback) { client->message_callback(pPublish->pTopicName, pPublish->topicNameLength, @@ -262,41 +262,41 @@ static void mqtt_event_callback(MQTTContext_t *pMqttContext, // Skip packet ID (2 bytes), get first return code uint8_t return_code = pPacketInfo->pRemainingData[2]; if (return_code == 0x80) { - log_error("Received SUBACK with failure code 0x%02X", return_code); + IOT_LOGE("Received SUBACK with failure code 0x%02X", return_code); client->suback_status = return_code; } else { - log_info("Received SUBACK with granted QoS %d", return_code); + IOT_LOGI("Received SUBACK with granted QoS %d", return_code); client->suback_status = 0; } } else { - log_warn("Received SUBACK with invalid length"); + IOT_LOGW("Received SUBACK with invalid length"); client->suback_status = 0x80; } } else if (pPacketInfo->type == MQTT_PACKET_TYPE_PUBACK) { - log_debug("Received PUBACK"); + IOT_LOGD("Received PUBACK"); } } // Create MQTT client with full configuration mqtt_client *mqtt_client_create_with_config(const mqtt_client_config_t *config) { if (!config) { - log_error("Invalid parameters for MQTT client creation"); + IOT_LOGE("Invalid parameters for MQTT client creation"); return NULL; } if (!config->pal) { - log_error("mqtt_client_create_with_config: config->pal is NULL"); + IOT_LOGE("mqtt_client_create_with_config: config->pal is NULL"); return NULL; } if (!config->broker_url || !config->client_id || !config->password || !config->subscribe_topic) { - log_error("Invalid parameters for MQTT client creation"); + IOT_LOGE("Invalid parameters for MQTT client creation"); return NULL; } const pal_t *pal = config->pal; mqtt_client *client = (mqtt_client *)pal->malloc(sizeof(mqtt_client)); if (!client) { - log_error("Failed to allocate memory for MQTT client"); + IOT_LOGE("Failed to allocate memory for MQTT client"); return NULL; } memset(client, 0, sizeof(mqtt_client)); @@ -304,7 +304,7 @@ mqtt_client *mqtt_client_create_with_config(const mqtt_client_config_t *config) // Parse broker URL if (parse_broker_url(config->broker_url, client->broker_host, &client->broker_port) != 0) { - log_error("Failed to parse broker URL: %s", config->broker_url); + IOT_LOGE("Failed to parse broker URL: %s", config->broker_url); client->pal->free(client); return NULL; } @@ -316,13 +316,13 @@ mqtt_client *mqtt_client_create_with_config(const mqtt_client_config_t *config) if (client->use_tls) { if (config->tls_config && config->tls_config->cacert && config->tls_config->cacert[0] != '\0') { client->cacert = config->tls_config->cacert; - log_info("TLS enabled with CA cert PEM"); + IOT_LOGI("TLS enabled with CA cert PEM"); } else { client->cacert = NULL; if (config->tls_config && config->tls_config->cert_bundle_attach) { - log_info("TLS enabled with platform cert bundle"); + IOT_LOGI("TLS enabled with platform cert bundle"); } else { - log_warn("TLS enabled without CA certificate - server verification disabled"); + IOT_LOGW("TLS enabled without CA certificate - server verification disabled"); } } if (config->tls_config) @@ -356,7 +356,7 @@ mqtt_client *mqtt_client_create_with_config(const mqtt_client_config_t *config) client->transport.send = transport_send; client->transport.recv = transport_recv; client->transport.pNetworkContext = &client->network_context; - log_info("MQTT client created for broker %s:%d (TLS: %s), clientId: %s, username: %s", + IOT_LOGI("MQTT client created for broker %s:%d (TLS: %s), clientId: %s, username: %s", client->broker_host, client->broker_port, client->use_tls ? "yes" : "no", client->client_id, client->username); return client; @@ -427,7 +427,7 @@ int mqtt_client_connect(mqtt_client *client) { if (!client->buffer) { client->buffer = client->pal->malloc(AGENTIC_KIT_MQTT_MAX_PACKET_SIZE); if (!client->buffer) { - log_error("Failed to allocate MQTT buffer (%u bytes)", AGENTIC_KIT_MQTT_MAX_PACKET_SIZE); + IOT_LOGE("Failed to allocate MQTT buffer (%u bytes)", AGENTIC_KIT_MQTT_MAX_PACKET_SIZE); return OPRT_MALLOC_FAILED; } client->fixed_buffer.pBuffer = client->buffer; @@ -459,7 +459,7 @@ int mqtt_client_connect(mqtt_client *client) { MQTTStatus_t status = MQTT_Init(&client->mqtt_context, &client->transport, mqtt_get_time_ms, mqtt_event_callback, &client->fixed_buffer); if (status != MQTTSuccess) { - log_error("MQTT_Init failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_Init failed: %s (%d)", MQTT_Status_strerror(status), status); return mqtt_abort_connect(client); } @@ -470,10 +470,10 @@ int mqtt_client_connect(mqtt_client *client) { client->incoming_publish_records, MQTT_QOS_RECORD_COUNT); if (status != MQTTSuccess) { - log_error("MQTT_InitStatefulQoS failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_InitStatefulQoS failed: %s (%d)", MQTT_Status_strerror(status), status); return mqtt_abort_connect(client); } - log_info("QoS1 and QoS2 support initialized"); + IOT_LOGI("QoS1 and QoS2 support initialized"); // Setup CONNECT packet MQTTConnectInfo_t connect_info = {0}; @@ -486,24 +486,24 @@ int mqtt_client_connect(mqtt_client *client) { connect_info.pPassword = client->password; connect_info.passwordLength = strlen(client->password); - log_info("MQTT CONNECT packet:"); - log_info(" clientId : [%u] %s", (unsigned)connect_info.clientIdentifierLength, connect_info.pClientIdentifier); - log_info(" username : [%u] %s", (unsigned)connect_info.userNameLength, connect_info.pUserName); - log_info(" password : [%u] ****", (unsigned)connect_info.passwordLength); - log_info(" keepAlive: %u s, cleanSession: %d", connect_info.keepAliveSeconds, connect_info.cleanSession); + IOT_LOGI("MQTT CONNECT packet:"); + IOT_LOGI(" clientId : [%u] %s", (unsigned)connect_info.clientIdentifierLength, connect_info.pClientIdentifier); + IOT_LOGI(" username : [%u] %s", (unsigned)connect_info.userNameLength, connect_info.pUserName); + IOT_LOGI(" password : [%u] ****", (unsigned)connect_info.passwordLength); + IOT_LOGI(" keepAlive: %u s, cleanSession: %d", connect_info.keepAliveSeconds, connect_info.cleanSession); bool sessionPresent = false; status = MQTT_Connect(&client->mqtt_context, &connect_info, NULL, AGENTIC_KIT_MQTT_SEND_TIMEOUT_MS, &sessionPresent); if (status != MQTTSuccess) { - log_error("MQTT_Connect failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_Connect failed: %s (%d)", MQTT_Status_strerror(status), status); return mqtt_abort_connect(client); } client->connected = true; client->link_dead = false; - log_info("Successfully connected to MQTT broker"); + IOT_LOGI("Successfully connected to MQTT broker"); return OPRT_OK; } @@ -529,11 +529,11 @@ int mqtt_client_subscribe(mqtt_client *client) { mqtt_note_transport_state(client, status); if (status != MQTTSuccess) { - log_error("MQTT_Subscribe failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_Subscribe failed: %s (%d)", MQTT_Status_strerror(status), status); return OPRT_COMMUNICATION_ERROR; } - log_info("Subscribe request sent for topic: %s, waiting for SUBACK...", client->subscribe_topic); + IOT_LOGI("Subscribe request sent for topic: %s, waiting for SUBACK...", client->subscribe_topic); int retries = 50; while (retries-- > 0) { @@ -541,21 +541,21 @@ int mqtt_client_subscribe(mqtt_client *client) { mqtt_note_transport_state(client, status); if (status == MQTTSuccess) { if (client->suback_status == 0) { - log_info("Successfully subscribed to topic: %s", client->subscribe_topic); + IOT_LOGI("Successfully subscribed to topic: %s", client->subscribe_topic); return OPRT_OK; } if (client->suback_status > 0) { - log_error("Subscription rejected by broker: code 0x%02X", client->suback_status); + IOT_LOGE("Subscription rejected by broker: code 0x%02X", client->suback_status); return OPRT_COMMUNICATION_ERROR; } } else if (status != MQTTNeedMoreBytes) { - log_error("MQTT_ProcessLoop failed while waiting for SUBACK: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_ProcessLoop failed while waiting for SUBACK: %s (%d)", MQTT_Status_strerror(status), status); return OPRT_COMMUNICATION_ERROR; } usleep(100000); } - log_error("Timeout waiting for SUBACK"); + IOT_LOGE("Timeout waiting for SUBACK"); return OPRT_COMMUNICATION_ERROR; } @@ -582,11 +582,11 @@ int mqtt_client_publish(mqtt_client *client, const char *topic, mqtt_note_transport_state(client, status); if (status != MQTTSuccess) { - log_error("MQTT_Publish failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_Publish failed: %s (%d)", MQTT_Status_strerror(status), status); return OPRT_COMMUNICATION_ERROR; } - log_debug("Published message to topic: %s", topic); + IOT_LOGD("Published message to topic: %s", topic); return OPRT_OK; } @@ -605,7 +605,7 @@ int mqtt_client_process(mqtt_client *client, uint32_t timeout_ms) { mqtt_note_transport_state(client, status); if (status != MQTTSuccess && status != MQTTNeedMoreBytes) { - log_error("MQTT_ProcessLoop failed: %s (%d)", MQTT_Status_strerror(status), status); + IOT_LOGE("MQTT_ProcessLoop failed: %s (%d)", MQTT_Status_strerror(status), status); return OPRT_COMMUNICATION_ERROR; } @@ -640,7 +640,7 @@ void mqtt_client_disconnect(mqtt_client *client) { client->connected = false; - log_info("Disconnected from MQTT broker"); + IOT_LOGI("Disconnected from MQTT broker"); } // Destroy client diff --git a/modules/iot-client/test/iot_atop_call_test.c b/modules/iot-client/test/iot_atop_call_test.c index e2e45e5..424184b 100644 --- a/modules/iot-client/test/iot_atop_call_test.c +++ b/modules/iot-client/test/iot_atop_call_test.c @@ -28,6 +28,7 @@ #include "iot_atop.h" #include "iot_client.h" #include "iot_internal.h" +#include "test_log.h" #include "log.h" #include @@ -476,35 +477,10 @@ static int test_unknown_api_reaches_the_cloud(void) * HTTP_DO_NOT_USE_CUSTOM_CONFIG is not set. Nothing else in this suite notices * if that wiring goes away, because every other case fits the buffer. */ +/* The capture itself lives in the compile-time sink (test_log.h): the + * window appends each dispatched line here -- va_copy/truncation + * discipline included -- and keeps stderr readable. */ static char http_log_capture[8192]; -static size_t http_log_capture_len; - -static void http_capture_handler(log_level_t level, const char *fmt, va_list args) -{ - /* va_copy is mandatory, not tidiness: vsnprintf() below consumes `args`, and - * handing a consumed va_list to log_default_handler() -- which vfprintf()s - * it again -- is undefined behaviour (C11 7.16.1). It happened to work on - * macOS/arm64 and produced parameter-shifted garbage on Linux/x86-64, where - * a bogus "host:port" sent a test off connecting to nowhere until the CI - * timeout killed it. */ - va_list copy; - va_copy(copy, args); - char line[1024]; - int n = vsnprintf(line, sizeof(line), fmt, copy); - va_end(copy); - /* vsnprintf returns the length it WOULD have written, so it exceeds - * the buffer on a truncated message -- copying `n` reads past `line`. - * Routed lines do get long: one coreHTTP parse error interpolates up to - * a whole response buffer. */ - size_t len = (n > 0 && (size_t)n < sizeof(line)) ? (size_t)n : sizeof(line) - 1; - if (n > 0 && http_log_capture_len + len + 1 < sizeof(http_log_capture)) { - memcpy(http_log_capture + http_log_capture_len, line, len); - http_log_capture_len += len; - http_log_capture[http_log_capture_len++] = '\n'; - http_log_capture[http_log_capture_len] = '\0'; - } - log_default_handler(level, fmt, args); -} static int test_oversized_response_is_explained(void) { @@ -516,11 +492,9 @@ static int test_oversized_response_is_explained(void) .data = body }; iot_atop_response_t resp = {0}; - http_log_capture[0] = '\0'; - http_log_capture_len = 0; - log_set_handler(http_capture_handler); + test_log_capture_begin(http_log_capture, sizeof http_log_capture); int rt = iot_atop_call(&g_client, &req, &resp); - log_set_handler(NULL); + test_log_capture_end(); int result = 0; if (rt == OPRT_OK) { diff --git a/modules/iot-client/test/log_config/agentic_kit_config.h b/modules/iot-client/test/log_config/agentic_kit_config.h new file mode 100644 index 0000000..d6a2b4c --- /dev/null +++ b/modules/iot-client/test/log_config/agentic_kit_config.h @@ -0,0 +1,26 @@ +/* + * agentic_kit_config.h -- test-build override for the iot-client test + * executables (and the tuya-ble test executable, which compiles the ble + * sources into itself and links the iot test library), picked up by + * common/log.h's override search. + * + * The SDK's log dispatch is decided HERE, at compile time: every TU of + * the build -- SDK sources included -- routes through test_log_sink. + * Capture/quiet/count are modes inside that one function (test_log.h), + * not a runtime handler swap. Integrator-facing docs: + * docs-site/docs/guides/compile-time-knobs.md. + * + * The prototype is spelled with plain int (log_level_t's underlying + * type) and guarded extern "C" so this works from C++ TUs without + * pulling log.h back in recursively. + */ +#ifdef __cplusplus +extern "C" { +#endif +void test_log_sink(int level, const char *tag, const char *fmt, ...); +#ifdef __cplusplus +} +#endif + +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + test_log_sink(level, tag, fmt, ##__VA_ARGS__) diff --git a/modules/iot-client/test/mqtt_test.c b/modules/iot-client/test/mqtt_test.c index e50d40b..10fc64a 100644 --- a/modules/iot-client/test/mqtt_test.c +++ b/modules/iot-client/test/mqtt_test.c @@ -5,12 +5,12 @@ #include #include #include -#include /* capture_log_handler takes a va_list */ #include "mqtt.h" #include "iot_client.h" #include "iot_internal.h" #include "log.h" +#include "test_log.h" #define TEST_CLIENT_ID "mqtt_test_client" #define TEST_USERNAME "test_user" @@ -390,51 +390,10 @@ static int test_connect_auth_fail(void) * MQTTServerRefused and no way to tell "wrong password" from "device unbound in * the cloud" -- which is the whole reason the wiring exists. */ +/* The capture itself lives in the compile-time sink (test_log.h): the + * window appends each dispatched line here -- va_copy/truncation + * discipline included -- and keeps stderr readable. */ static char log_capture[4096]; -static size_t log_capture_len; -static log_fn_t saved_log_fn; - -static void capture_log_handler(log_level_t level, const char *fmt, va_list args) -{ - /* va_copy is mandatory, not tidiness: vsnprintf() below consumes `args`, and - * handing a consumed va_list to log_default_handler() -- which vfprintf()s - * it again -- is undefined behaviour (C11 7.16.1). It happened to work on - * macOS/arm64 and produced parameter-shifted garbage on Linux/x86-64, where - * a bogus "host:port" sent a test off connecting to nowhere until the CI - * timeout killed it. */ - va_list copy; - va_copy(copy, args); - char line[512]; - int n = vsnprintf(line, sizeof(line), fmt, copy); - va_end(copy); - /* vsnprintf returns the length it WOULD have written, so it exceeds - * the buffer on a truncated message -- copying `n` reads past `line`. - * Routed lines do get long: one coreHTTP parse error interpolates up to - * a whole response buffer. */ - size_t len = (n > 0 && (size_t)n < sizeof(line)) ? (size_t)n : sizeof(line) - 1; - if (n > 0 && log_capture_len + len + 1 < sizeof(log_capture)) { - memcpy(log_capture + log_capture_len, line, len); - log_capture_len += len; - log_capture[log_capture_len++] = '\n'; - log_capture[log_capture_len] = '\0'; - } - /* Keep the default output too, so a failing run is still readable. */ - log_default_handler(level, fmt, args); -} - -static void capture_start(void) -{ - log_capture[0] = '\0'; - log_capture_len = 0; - saved_log_fn = capture_log_handler; - log_set_handler(capture_log_handler); -} - -static void capture_stop(void) -{ - log_set_handler(NULL); - (void)saved_log_fn; -} static int test_connack_reason_is_logged(void) { @@ -453,9 +412,9 @@ static int test_connack_reason_is_logged(void) return -1; } - capture_start(); + test_log_capture_begin(log_capture, sizeof log_capture); int ret = mqtt_client_connect(c); - capture_stop(); + test_log_capture_end(); int result = 0; if (ret == 0) { @@ -471,7 +430,7 @@ static int test_connack_reason_is_logged(void) } /* And the SDK's own line names the status symbolically. This must match the * message too, not just the enum name: coreMQTT itself logs "MQTT connection - * failed with status = MQTTServerRefused", captured by the same handler, so a + * failed with status = MQTTServerRefused", captured by the same window, so a * bare search for the enum name passes even with mqtt.c back on a raw "%d". */ if (strstr(log_capture, "MQTT_Connect failed: MQTTServerRefused") == NULL) { printf(" the SDK's own log line lost the symbolic status name\n"); diff --git a/modules/iot-client/test/test_log.c b/modules/iot-client/test/test_log.c new file mode 100644 index 0000000..312b39d --- /dev/null +++ b/modules/iot-client/test/test_log.c @@ -0,0 +1,120 @@ +/* + * test_log.c -- the test build's log destination. See test_log.h. + * + * The dispatch itself is decided where it should be: in + * log_config/agentic_kit_config.h, at compile time. This file is just + * the target it names -- one plain variadic function with a mode. + * + * Twin: modules/rtc-tcp-client/test/test_log.c. The two differ only in + * this banner and the tai variant's test_log_env_default() -- keep the + * sink bodies in sync. + */ +#include "test_log.h" + +#include +#include +#include + +static test_log_mode_t g_mode = TEST_LOG_PASSTHROUGH; + +static char *g_cap; +static size_t g_cap_left; +static test_log_mode_t g_saved_mode = TEST_LOG_PASSTHROUGH; + +static unsigned g_count; + +void test_log_set_mode(test_log_mode_t mode) +{ + g_mode = mode; +} + +void test_log_capture_begin(char *buf, size_t cap) +{ + g_saved_mode = g_mode; + g_cap = buf; + g_cap_left = cap; + buf[0] = '\0'; + g_mode = TEST_LOG_CAPTURE; +} + +void test_log_capture_end(void) +{ + g_mode = g_saved_mode; +} + +void test_log_count_reset(void) +{ + g_count = 0; +} + +unsigned test_log_count_get(void) +{ + return g_count; +} + +/* Rebuild the tagged format the macro layer normally folds together + * ("[tag] " + fmt): captured lines keep the "[tag] " prefix tests + * strstr() for, and the va_list stays unconsumed for the caller. */ +static void build_tagged(char *out, size_t cap, const char *tag, const char *fmt) +{ + snprintf(out, cap, "[%s] %s", tag ? tag : "", fmt ? fmt : ""); +} + +void test_log_sink(log_level_t level, const char *tag, const char *fmt, ...) +{ + va_list args; + + switch (g_mode) { + case TEST_LOG_QUIET: + return; + case TEST_LOG_COUNT: + g_count++; + return; + case TEST_LOG_CAPTURE: { + /* va_copy discipline: vsnprintf() below consumes the va_list, so + * it gets a copy -- the passthrough afterwards needs the original + * (a consumed va_list re-vfprintf()ed is UB, C11 7.16.1; it + * happened to work on macOS/arm64 and produced parameter-shifted + * garbage on Linux/x86-64). vsnprintf returns the length it + * WOULD have written, so it exceeds the buffer on a truncated + * message: clamp before memcpy or it reads past `line`. The + * buffers must stay comfortably larger than any fmt template: + * build_tagged() truncates BEFORE formatting, so an overlong + * template would silently lose its tail here (routed templates + * do get long -- one coreHTTP parse error interpolates up to a + * whole response buffer). */ + va_start(args, fmt); + char tagged[1024]; + build_tagged(tagged, sizeof tagged, tag, fmt); + va_list copy; + va_copy(copy, args); + char line[1024]; + int n = vsnprintf(line, sizeof line, tagged, copy); + va_end(copy); + size_t len = (n > 0 && (size_t)n < sizeof line) ? (size_t)n : sizeof line - 1; + if (n > 0 && g_cap && len + 2 < g_cap_left) { + memcpy(g_cap, line, len); + g_cap += len; + *g_cap++ = '\n'; + *g_cap = '\0'; + g_cap_left -= len + 1; + } + /* Keep the default output too when the window opened on + * passthrough, so a failing run stays readable; a window opened + * on quiet (a suite that opted into silence) stays silent. */ + if (g_saved_mode == TEST_LOG_PASSTHROUGH) { + log_emit_valist(level, tagged, args); + } + va_end(args); + return; + } + default: + break; + } + + va_start(args, fmt); + char tagged[512]; + build_tagged(tagged, sizeof tagged, tag, fmt); + log_emit_valist(level, tagged, args); + va_end(args); +} diff --git a/modules/iot-client/test/test_log.h b/modules/iot-client/test/test_log.h new file mode 100644 index 0000000..3b97d93 --- /dev/null +++ b/modules/iot-client/test/test_log.h @@ -0,0 +1,47 @@ +/* + * test_log.h -- this binary's single log destination, fixed at compile + * time: the log_config/agentic_kit_config.h next door remaps + * AGENTIC_KIT_LOG to test_log_sink, so every TU of the test build -- + * SDK sources included -- dispatches into the one function below. + * + * What used to vary at runtime via log_set_handler() install/uninstall + * is now a mode inside that one sink. Modes: + * PASSTHROUGH default stderr output, unchanged shape + * CAPTURE append lines to the registered buffer, keep stderr + * (a failing run stays readable) + * QUIET swallow everything + * COUNT count lines, no output + * + * The tuya-ble test executable shares this sink: it compiles the ble + * sources into itself and links the iot test library that carries it. + */ +#ifndef IOT_TEST_LOG_H +#define IOT_TEST_LOG_H + +#include + +#include "log.h" + +typedef enum { + TEST_LOG_PASSTHROUGH, + TEST_LOG_CAPTURE, + TEST_LOG_QUIET, + TEST_LOG_COUNT, +} test_log_mode_t; + +void test_log_set_mode(test_log_mode_t mode); + +/* Capture window: lines append to buf (one per line, NUL-terminated, + * truncation-safe) until test_log_capture_end() restores the mode that + * was active before the window opened. */ +void test_log_capture_begin(char *buf, size_t cap); +void test_log_capture_end(void); + +/* COUNT mode's counter. */ +void test_log_count_reset(void); +unsigned test_log_count_get(void); + +/* The AGENTIC_KIT_LOG dispatch target for every TU of this test build. */ +void test_log_sink(log_level_t level, const char *tag, const char *fmt, ...); + +#endif /* IOT_TEST_LOG_H */ diff --git a/modules/rtc-tcp-client/include/tai_config_defaults.h b/modules/rtc-tcp-client/include/tai_config_defaults.h index f448894..57f1e09 100644 --- a/modules/rtc-tcp-client/include/tai_config_defaults.h +++ b/modules/rtc-tcp-client/include/tai_config_defaults.h @@ -5,8 +5,8 @@ * * It pulls common/log.h FIRST: that header is where integrator overrides * (agentic_kit_config.h on the include path, -D, AGENTIC_KIT_USER_CONFIG) - * are applied -- so overrides win over every default below. Not a public - * API header. + * are applied and the SDK-wide log ceiling defaults -- so overrides win + * over every default below. Not a public API header. */ #ifndef AGENTIC_KIT_TAI_CONFIG_DEFAULTS_H @@ -29,6 +29,24 @@ * kilobyte sizing. */ +/* Optional per-module log ceiling for rtc-tcp-client. Defaults to the + * SDK-wide ceiling and can only LOWER this module below it: the effective + * ceiling is the smaller of the two, so a value above AGENTIC_KIT_LOG_LEVEL + * is clamped to it right below. Gates the TAI_LOG* vocabulary and the + * packet-log formatter (tai_log_packet) where they are defined (src/). */ +#ifndef AGENTIC_KIT_TAI_LOG_LEVEL +#define AGENTIC_KIT_TAI_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif +/* Lower-only, enforced ONCE here: block-level gates (the packet-log + * formatter, the flood branch, the call sites in tai_client.c) key on + * this knob directly, so clamp it to the SDK-wide ceiling to make a + * too-high value a true no-op -- without this, module > global would + * compile those blocks back in behind dead dispatches. */ +#if AGENTIC_KIT_TAI_LOG_LEVEL > AGENTIC_KIT_LOG_LEVEL +#undef AGENTIC_KIT_TAI_LOG_LEVEL +#define AGENTIC_KIT_TAI_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif + /* Sample 1-in-N media MIDDLE frames to INFO (tai_pkt_log.c). All * non-sampled MIDDLE frames are dropped (no log line). Set to 0 to disable * sampling, in which case every MIDDLE frame logs at DEBUG instead. */ diff --git a/modules/rtc-tcp-client/include/tuya_ai.h b/modules/rtc-tcp-client/include/tuya_ai.h index ee4a0a6..6d0451c 100644 --- a/modules/rtc-tcp-client/include/tuya_ai.h +++ b/modules/rtc-tcp-client/include/tuya_ai.h @@ -411,21 +411,20 @@ int tai_send_mcp_response(tai_ctx_t *ctx, const char *json_rpc_response); /* ========================================================================= * Logging * - * The SDK emits log messages through the global log facade. Two - * filters apply: + * The SDK emits log messages through the global log facade. The filters + * are compile-time: the SDK-wide AGENTIC_KIT_LOG_LEVEL (default 4 = DEBUG; + * gated once in common/log.h), optionally lowered for this module alone by + * AGENTIC_KIT_TAI_LOG_LEVEL (defaults to the SDK-wide value; cannot exceed + * it). Messages above their ceiling are optimised away at build time; + * below it, a line emits unconditionally -- there is no runtime level knob. * - * 1. Compile-time maximum (TAI_LOG_LEVEL, default 4 = DEBUG). - * Messages above this level are optimised away at build time. - * 2. Runtime level, set via tai_set_log_level(). Default: TAI_LOG_INFO. - * - * These thin inlines exist for source-compatibility with callers; new - * code should prefer log_set_level() / log_get_level() directly. - * - * Valid level values are TAI_LOG_ERROR (1) through TAI_LOG_DEBUG (4). - * Use 0 to disable all logging at runtime. + * Quiet a build with -DAGENTIC_KIT_LOG_LEVEL=N (valid values: 0 = none, + * then TAI_LOG_ERROR (1) .. TAI_LOG_DEBUG (4)); quiet only rtc-tcp-client + * with -DAGENTIC_KIT_TAI_LOG_LEVEL=N. The destination is + * decided at compile time as well: define AGENTIC_KIT_LOG (see log.h) + * to route every line into your own macro — a function target can + * reuse the default output via log_emit_valist(). * ========================================================================= */ -static inline void tai_set_log_level(int level) { log_set_level(level); } -static inline int tai_get_log_level(void) { return log_get_level(); } #ifdef __cplusplus } diff --git a/modules/rtc-tcp-client/src/tai_client.c b/modules/rtc-tcp-client/src/tai_client.c index fc877a1..67517fc 100644 --- a/modules/rtc-tcp-client/src/tai_client.c +++ b/modules/rtc-tcp-client/src/tai_client.c @@ -111,8 +111,9 @@ static inline void ctx_io_close(tai_ctx_t *ctx) /* Decode + log an outgoing application packet. Separated from send_app * so the stack-allocated attr array only exists during the log call. */ +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 static void log_send_packet(tai_ctx_t *ctx, - const uint8_t *app_bytes, size_t app_len) + const uint8_t *app_bytes, size_t app_len) { uint8_t pkt_type; tai_attr_t attrs[AGENTIC_KIT_TAI_MAX_ATTRS]; @@ -126,6 +127,7 @@ static void log_send_packet(tai_ctx_t *ctx, pkt_type, attrs, attr_count, payload, payload_len); } } +#endif /* ========================================================================= * Scatter-gather streaming send (§6) @@ -289,8 +291,9 @@ static int send_app_sg(tai_ctx_t *ctx, size_t hdr_len, * tai_connect (sig_len=0). */ static int send_app(tai_ctx_t *ctx, const uint8_t *app_bytes, size_t app_len) { - if (log_get_level() >= LOG_INFO) - log_send_packet(ctx, app_bytes, app_len); +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 + log_send_packet(ctx, app_bytes, app_len); +#endif return send_app_sg(ctx, /*hdr_len=*/0, app_bytes, app_len); } @@ -490,8 +493,9 @@ int tai_connect(tai_ctx_t *ctx) TAI_LOGE(ctx->pal, TAG, "send ClientHello failed: %d", rc); tai_disconnect(ctx); return rc; } - if (log_get_level() >= LOG_INFO) - log_send_packet(ctx, ctx->tx_ctrl_buf, (size_t)app_len); +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 + log_send_packet(ctx, ctx->tx_ctrl_buf, (size_t)app_len); +#endif /* 5. SessionNew */ app_len = tai_proto_build_session_new(ctx, diff --git a/modules/rtc-tcp-client/src/tai_internal.h b/modules/rtc-tcp-client/src/tai_internal.h index 369b95a..ad223af 100644 --- a/modules/rtc-tcp-client/src/tai_internal.h +++ b/modules/rtc-tcp-client/src/tai_internal.h @@ -25,48 +25,47 @@ /* ========================================================================= * Logging macros — routed through the global log facade. * - * TAI_LOG_LEVEL (compile-time) is the maximum level compiled in. Messages - * above this level are removed at build time with zero runtime cost. + * Two ceilings, both compile-time. The SDK-wide AGENTIC_KIT_LOG_LEVEL + * (gated once in common/log.h) bounds everything; the per-module + * AGENTIC_KIT_TAI_LOG_LEVEL (defaults to it; tai_config_defaults.h) + * optionally lowers just this module further and can never raise a line + * above the SDK-wide ceiling. Messages above their ceiling are removed + * at build time with zero runtime cost. * 0 = none, 1 = error, 2 = +warn, 3 = +info, 4 = +debug (default) * - * Runtime filtering happens inside log_emit() via log_set_level(). - * The tai_set_log_level()/tai_get_log_level() inlines in tuya_ai.h remain - * available as thin wrappers for backwards compatibility. + * There is no runtime level. To quiet a build, compile with a lower + * ceiling; to change where lines go, define AGENTIC_KIT_LOG and take over + * the dispatch itself (see log.h). * * The leading `pal` argument is preserved for source-compatibility with * existing call sites; it is unused at the dispatch layer. `tag` must be * a string literal — it is concatenated into the format string so the * log facade itself stays tag-agnostic. * ========================================================================= */ -#ifndef TAI_LOG_LEVEL -# define TAI_LOG_LEVEL 4 -#endif - -#define TAI_LOG_(pal, lvl, tag, fmt, ...) \ - do { (void)(pal); log_emit((lvl), "[" tag "] " fmt, ##__VA_ARGS__); } while (0) -#if TAI_LOG_LEVEL >= 1 -# define TAI_LOGE(pal, tag, fmt, ...) TAI_LOG_(pal, LOG_ERROR, tag, fmt, ##__VA_ARGS__) +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 1 +#define TAI_LOGE(pal, tag, fmt, ...) \ + do { (void)(pal); log_tag_error(tag, fmt, ##__VA_ARGS__); } while (0) #else -# define TAI_LOGE(pal, tag, ...) ((void)(pal)) +#define TAI_LOGE(...) ((void)0) #endif - -#if TAI_LOG_LEVEL >= 2 -# define TAI_LOGW(pal, tag, fmt, ...) TAI_LOG_(pal, LOG_WARN, tag, fmt, ##__VA_ARGS__) +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 2 +#define TAI_LOGW(pal, tag, fmt, ...) \ + do { (void)(pal); log_tag_warn(tag, fmt, ##__VA_ARGS__); } while (0) #else -# define TAI_LOGW(pal, tag, ...) ((void)(pal)) +#define TAI_LOGW(...) ((void)0) #endif - -#if TAI_LOG_LEVEL >= 3 -# define TAI_LOGI(pal, tag, fmt, ...) TAI_LOG_(pal, LOG_INFO, tag, fmt, ##__VA_ARGS__) +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 +#define TAI_LOGI(pal, tag, fmt, ...) \ + do { (void)(pal); log_tag_info(tag, fmt, ##__VA_ARGS__); } while (0) #else -# define TAI_LOGI(pal, tag, ...) ((void)(pal)) +#define TAI_LOGI(...) ((void)0) #endif - -#if TAI_LOG_LEVEL >= 4 -# define TAI_LOGD(pal, tag, fmt, ...) TAI_LOG_(pal, LOG_DEBUG, tag, fmt, ##__VA_ARGS__) +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 4 +#define TAI_LOGD(pal, tag, fmt, ...) \ + do { (void)(pal); log_tag_debug(tag, fmt, ##__VA_ARGS__); } while (0) #else -# define TAI_LOGD(pal, tag, ...) ((void)(pal)) +#define TAI_LOGD(...) ((void)0) #endif /* ========================================================================= @@ -86,6 +85,7 @@ * the worker processes at most one max-size frame per recv pass. */ #define TAI_RX_BUF_SIZE (AGENTIC_KIT_TAI_MAX_FRAGMENT_PAYLOAD + 37U) + /* ========================================================================= * Attribute type codes (Appendix A) * ========================================================================= */ @@ -447,11 +447,18 @@ int tai_crypto_derive_keys(uint8_t proto_ver, /* * tai_pkt_log.c */ +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 void tai_log_packet(uint8_t proto_ver, int is_send, uint8_t pkt_type, const tai_attr_t *attrs, int attr_count, const uint8_t *payload, size_t payload_len); +#else +/* Packet diagnostics are INFO/DEBUG only. Remove the call at its boundary so + * a lower SDK-wide ceiling also removes argument evaluation and the formatter + * implementation/string table from the object file. */ +#define tai_log_packet(...) ((void)0) +#endif /* * tai_protocol.c diff --git a/modules/rtc-tcp-client/src/tai_pkt_log.c b/modules/rtc-tcp-client/src/tai_pkt_log.c index 741e163..86655bb 100644 --- a/modules/rtc-tcp-client/src/tai_pkt_log.c +++ b/modules/rtc-tcp-client/src/tai_pkt_log.c @@ -29,6 +29,11 @@ #define TAG "pkt" +/* Packet diagnostics have only INFO and DEBUG outcomes. Keep the entire + * formatter out of lower-ceiling builds so its calls, work and strings vanish + * together; tai_internal.h removes the corresponding call sites. */ +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 + /* AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N default & docs: include/tai_config_defaults.h. */ /* snprintf into buf at *pos; advances pos. Bails (returning 0) if there is @@ -467,6 +472,57 @@ static void put_payload(char *buf, size_t cap, size_t *pos, * gracefully when this fills. */ #define TAI_LOG_BUF_SIZE 1024 +/* Format one already-classified Packet. The caller selects the compile-time + * sink (INFO or DEBUG); this helper owns no level policy. */ +static const char *format_packet_log(char buf[TAI_LOG_BUF_SIZE], + uint8_t proto_ver, + int is_send, + uint8_t pkt_type, + const tai_attr_t *attrs, int attr_count, + const uint8_t *payload, size_t payload_len, + int is_media, uint8_t stream_flag, + uint32_t order, uint32_t sample_idx) +{ + const char *dir; + if (is_send) { + dir = "send"; + } else { + switch (pkt_type) { + case TAI_PKT_AUDIO: dir = "receive-audio"; break; + case TAI_PKT_VIDEO: dir = "receive-video"; break; + case TAI_PKT_IMAGE: dir = "receive-image"; break; + default: dir = "receive"; break; + } + } + + /* Written strictly sequentially via bput() and NUL-terminated at buf[pos] + * below, so no zero-init is needed (avoids a memset on the log hot path). */ + size_t cap = TAI_LOG_BUF_SIZE - 1; + size_t pos = 0; + + bput(buf, cap, &pos, "{\"packet-type\":\"%s\"", pkt_kebab(pkt_type)); + + if (is_media && stream_flag != 0xFF) + bput(buf, cap, &pos, ",\"order\":%u", (unsigned)order); + + if (sample_idx) + bput(buf, cap, &pos, ",\"sample-every\":%u,\"sample-idx\":%u", + (unsigned)AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N, (unsigned)sample_idx); + + if (!is_send) + bput(buf, cap, &pos, ",\"payload-len\":%zu", payload_len); + + bput(buf, cap, &pos, ",\"attributes\":"); + put_attrs(buf, cap, &pos, attrs, attr_count); + + bput(buf, cap, &pos, ",\"payload\":"); + put_payload(buf, cap, &pos, proto_ver, pkt_type, payload, payload_len); + + bput(buf, cap, &pos, "}"); + buf[pos] = '\0'; + return dir; +} + void tai_log_packet(uint8_t proto_ver, int is_send, uint8_t pkt_type, @@ -477,7 +533,7 @@ void tai_log_packet(uint8_t proto_ver, if (pkt_type == TAI_PKT_PING || pkt_type == TAI_PKT_PONG) return; - /* --- Decide effective log level / volume for media streams ---------- + /* --- Apply the compile-time media log policy ------------------------- * * Non-media packets always log at INFO. Media packets (audio / video * / image) have much higher rates and need filtering: @@ -491,8 +547,11 @@ void tai_log_packet(uint8_t proto_ver, * A per-direction "order" counter lets operators stitch sampled * output back into a continuous timeline; it resets on START / * ONE_SHOT and advances on every subsequent media frame. + * + * The preprocessor selects the MIDDLE path below. There is no runtime + * log-level variable or level comparison: runtime decisions inspect only + * Packet data (Packet type / Stream flag) and the sampling counter. */ - int log_level = TAI_LOG_INFO; uint32_t sample_idx = 0; /* non-zero => sampled middle frame */ int is_media = (pkt_type == TAI_PKT_AUDIO || pkt_type == TAI_PKT_VIDEO || @@ -519,61 +578,39 @@ void tai_log_packet(uint8_t proto_ver, } if (stream_flag == TAI_STREAM_MIDDLE) { - if (AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N > 0) { - /* Sample every N-th; drop the rest entirely. */ - static uint32_t sample_counter = 0; - uint32_t n = ++sample_counter; - if ((n % AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N) != 0) - return; /* dropped -- no log line */ - sample_idx = n; /* keep at INFO, mark sampled */ - } else { - /* Flood mode: developer opt-in. All MIDDLE at DEBUG. */ - log_level = TAI_LOG_DEBUG; - } +#if AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N > 0 + /* Sampling mode is compiled as INFO-only. Drop non-sampled + * MIDDLE Packets before formatting. */ + static uint32_t sample_counter = 0; + uint32_t n = ++sample_counter; + if ((n % AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N) != 0) + return; + sample_idx = n; +#elif AGENTIC_KIT_TAI_LOG_LEVEL >= 4 + /* Flood mode exists only in a DEBUG build. The DEBUG sink and + * formatter call disappear entirely at lower ceilings. */ + char debug_buf[TAI_LOG_BUF_SIZE]; + const char *debug_dir = format_packet_log( + debug_buf, proto_ver, is_send, pkt_type, + attrs, attr_count, payload, payload_len, + is_media, stream_flag, order, 0); + log_tag_debug(TAG, "%s: %s", debug_dir, debug_buf); + return; +#else + /* Sampling disabled and DEBUG not compiled: no MIDDLE log. */ + return; +#endif } } - /* Runtime filter: bail before doing any expensive formatting. */ - if (log_get_level() < log_level) return; - - const char *dir; - if (is_send) { - dir = "send"; - } else { - switch (pkt_type) { - case TAI_PKT_AUDIO: dir = "receive-audio"; break; - case TAI_PKT_VIDEO: dir = "receive-video"; break; - case TAI_PKT_IMAGE: dir = "receive-image"; break; - default: dir = "receive"; break; - } - } - - /* Written strictly sequentially via bput() and NUL-terminated at buf[pos] - * below, so no zero-init is needed (avoids a memset on the log hot path). */ - char buf[TAI_LOG_BUF_SIZE]; - size_t cap = TAI_LOG_BUF_SIZE - 1; - size_t pos = 0; - - bput(buf, cap, &pos, "{\"packet-type\":\"%s\"", pkt_kebab(pkt_type)); - - if (is_media && stream_flag != 0xFF) - bput(buf, cap, &pos, ",\"order\":%u", (unsigned)order); - - if (sample_idx) - bput(buf, cap, &pos, ",\"sample-every\":%u,\"sample-idx\":%u", - (unsigned)AGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N, (unsigned)sample_idx); - - if (!is_send) - bput(buf, cap, &pos, ",\"payload-len\":%zu", payload_len); - - bput(buf, cap, &pos, ",\"attributes\":"); - put_attrs(buf, cap, &pos, attrs, attr_count); - - bput(buf, cap, &pos, ",\"payload\":"); - put_payload(buf, cap, &pos, proto_ver, pkt_type, payload, payload_len); - - bput(buf, cap, &pos, "}"); - buf[pos] = '\0'; - - log_emit(log_level, "[" TAG "] %s: %s", dir, buf); + /* Non-media Packets, media boundaries and sampled MIDDLE Packets are an + * INFO-only compile-time path. */ + char info_buf[TAI_LOG_BUF_SIZE]; + const char *info_dir = format_packet_log( + info_buf, proto_ver, is_send, pkt_type, + attrs, attr_count, payload, payload_len, + is_media, stream_flag, order, sample_idx); + log_tag_info(TAG, "%s: %s", info_dir, info_buf); } + +#endif /* AGENTIC_KIT_TAI_LOG_LEVEL >= 3 */ diff --git a/modules/rtc-tcp-client/src/tai_transport.c b/modules/rtc-tcp-client/src/tai_transport.c index 705274b..36e0965 100644 --- a/modules/rtc-tcp-client/src/tai_transport.c +++ b/modules/rtc-tcp-client/src/tai_transport.c @@ -130,6 +130,7 @@ int tai_frame_encode(uint8_t frag_flag, uint16_t sequence, const pal_t *pal, uint8_t *out_buf, size_t out_size) { + (void)pal; /* read only by the TAI_LOG* traces below */ if (!out_buf || !payload) return TAI_ERR_ARGS; size_t frame_len = 5 + payload_len + sig_len; @@ -199,6 +200,7 @@ int tai_frame_verify(const uint8_t *raw_frame, size_t frame_len, const uint8_t sign_key[32], const pal_t *pal) { + (void)pal; /* read only by the TAI_LOG* traces below */ if (sig_len == 0) return TAI_OK; if (!raw_frame || frame_len < (size_t)(5 + sig_len)) return TAI_ERR_PROTO; diff --git a/modules/rtc-tcp-client/test/log_config/agentic_kit_config.h b/modules/rtc-tcp-client/test/log_config/agentic_kit_config.h new file mode 100644 index 0000000..e6cffd2 --- /dev/null +++ b/modules/rtc-tcp-client/test/log_config/agentic_kit_config.h @@ -0,0 +1,24 @@ +/* + * agentic_kit_config.h -- test-build override for the rtc-tcp-client + * (TAI) test executables, picked up by common/log.h's override search. + * + * The SDK's log dispatch is decided HERE, at compile time: every TU of + * the build -- SDK sources included -- routes through test_log_sink. + * Capture/quiet/count are modes inside that one function (test_log.h), + * not a runtime handler swap. Integrator-facing docs: + * docs-site/docs/guides/compile-time-knobs.md. + * + * The prototype is spelled with plain int (log_level_t's underlying + * type) and guarded extern "C" so this works from C++ TUs without + * pulling log.h back in recursively. + */ +#ifdef __cplusplus +extern "C" { +#endif +void test_log_sink(int level, const char *tag, const char *fmt, ...); +#ifdef __cplusplus +} +#endif + +#define AGENTIC_KIT_LOG(level, tag, fmt, ...) \ + test_log_sink(level, tag, fmt, ##__VA_ARGS__) diff --git a/modules/rtc-tcp-client/test/tai_pal_loopback.c b/modules/rtc-tcp-client/test/tai_pal_loopback.c index cec298a..521c1d5 100644 --- a/modules/rtc-tcp-client/test/tai_pal_loopback.c +++ b/modules/rtc-tcp-client/test/tai_pal_loopback.c @@ -495,25 +495,6 @@ static int lb_thread_join(void *handle) return rc == 0 ? 0 : -1; } -/* ========================================================================= - * Logging -- suppressed by default. Set TAI_LOOPBACK_VERBOSE=1 to enable. - * ========================================================================= */ -static void lb_log(log_level_t level, const char *fmt, va_list args) -{ - static int checked = 0, enabled = 0; - if (!checked) { - const char *v = getenv("TAI_LOOPBACK_VERBOSE"); - enabled = (v && v[0] && v[0] != '0'); - checked = 1; - } - if (!enabled) return; - static const char *lvl[] = { "?", "E", "W", "I", "D" }; - const char *l = (level >= 1 && level <= 4) ? lvl[level] : "?"; - char msg[1024]; - vsnprintf(msg, sizeof msg, fmt ? fmt : "", args); - fprintf(stderr, "[TAI/%s] %s\n", l, msg); -} - /* ========================================================================= * Factory * ========================================================================= */ @@ -537,6 +518,5 @@ static const pal_t g_loopback_pal = { const pal_t *tai_pal_loopback(void) { lb_init_once(); - log_set_handler(lb_log); return &g_loopback_pal; } diff --git a/modules/rtc-tcp-client/test/test_core.c b/modules/rtc-tcp-client/test/test_core.c index ed16ebe..b80af0f 100644 --- a/modules/rtc-tcp-client/test/test_core.c +++ b/modules/rtc-tcp-client/test/test_core.c @@ -17,6 +17,7 @@ /* Pull in the full internal header so we can call internal functions */ #include "../src/tai_internal.h" #include "rng.h" +#include "test_log.h" /* ------------------------------------------------------------------------- * Minimal stub PAL. TLS and crypto are no longer PAL responsibilities -- @@ -54,6 +55,7 @@ static const pal_t g_stub_pal = { * Test framework * ------------------------------------------------------------------------- */ static int g_pass = 0, g_fail = 0; +static int g_log_arg_evals = 0; #define CHECK(expr) \ do { \ @@ -69,6 +71,47 @@ static int g_pass = 0, g_fail = 0; #define TEST(name) do { printf(" %-50s", name); } while (0) #define PASS() do { printf("OK\n"); } while (0) +static size_t counted_zero(void) +{ + g_log_arg_evals++; + return 0; +} + +static void test_compile_time_log_ceiling(void) +{ + TEST("compile-time log ceiling gates packet logs"); + + g_log_arg_evals = 0; + test_log_count_reset(); + test_log_set_mode(TEST_LOG_COUNT); + + TAI_LOGI((g_log_arg_evals++, &g_stub_pal), "test", "compiled log"); + tai_log_packet(TAI_VER_21, 1, TAI_PKT_SESSION_CLOSE, + NULL, 0, NULL, counted_zero()); + +#if AGENTIC_KIT_TAI_LOG_LEVEL >= 3 + CHECK(g_log_arg_evals == 2); + CHECK(test_log_count_get() == 2); +#else + CHECK(g_log_arg_evals == 0); + CHECK(test_log_count_get() == 0); +#endif + +#if AGENTIC_KIT_LOG_LEVEL >= 3 && AGENTIC_KIT_TAI_LOG_LEVEL < 3 + /* Module ceiling lowered below a still-open SDK-wide ceiling (built as + * tai_log_module_zero_tests): the module vocabulary above is compiled + * out by the TAI knob, while the facade itself still dispatches -- a + * per-module knob only lowers its own module, it is not a second + * global gate. */ + log_tag_info("test", "module ceiling does not touch the facade"); + CHECK(g_log_arg_evals == 0); + CHECK(test_log_count_get() == 1); +#endif + + test_log_set_mode(TEST_LOG_PASSTHROUGH); + PASS(); +} + /* ------------------------------------------------------------------------- * 1. Varint encode/decode * ------------------------------------------------------------------------- */ @@ -558,6 +601,7 @@ int main(void) test_crypto(); test_proto_client_hello(); test_hmac_sg(); + test_compile_time_log_ceiling(); printf("\n=== Results: %d passed, %d failed ===\n", g_pass, g_fail); return g_fail ? 1 : 0; diff --git a/modules/rtc-tcp-client/test/test_integration.c b/modules/rtc-tcp-client/test/test_integration.c index 7368b5e..4380963 100644 --- a/modules/rtc-tcp-client/test/test_integration.c +++ b/modules/rtc-tcp-client/test/test_integration.c @@ -21,6 +21,7 @@ #include "../src/tai_internal.h" #include "tai_pal_loopback.h" +#include "test_log.h" /* ========================================================================= * Test harness @@ -2027,6 +2028,10 @@ static void test_disconnect_latency(void) int main(void) { printf("=== Tuya AI -- integration tests (loopback PAL) ===\n"); + /* Logging is suppressed by default; set TAI_LOOPBACK_VERBOSE=1 to + * enable -- the gate the loopback PAL's old lb_log handler applied, + * now a mode default of this build's compile-time sink. */ + test_log_env_default(); pthread_mutex_init(&g_st.mtx, NULL); test_text_query(); diff --git a/modules/rtc-tcp-client/test/test_log.c b/modules/rtc-tcp-client/test/test_log.c new file mode 100644 index 0000000..4ed0d02 --- /dev/null +++ b/modules/rtc-tcp-client/test/test_log.c @@ -0,0 +1,127 @@ +/* + * test_log.c -- the test build's log destination. See test_log.h. + * + * The dispatch itself is decided where it should be: in + * log_config/agentic_kit_config.h, at compile time. This file is just + * the target it names -- one plain variadic function with a mode. + * + * Twin: modules/iot-client/test/test_log.c. The two differ only in + * this banner and the tai variant's test_log_env_default() -- keep the + * sink bodies in sync. + */ +#include "test_log.h" + +#include +#include +#include +#include + +static test_log_mode_t g_mode = TEST_LOG_PASSTHROUGH; + +static char *g_cap; +static size_t g_cap_left; +static test_log_mode_t g_saved_mode = TEST_LOG_PASSTHROUGH; + +static unsigned g_count; + +void test_log_set_mode(test_log_mode_t mode) +{ + g_mode = mode; +} + +void test_log_capture_begin(char *buf, size_t cap) +{ + g_saved_mode = g_mode; + g_cap = buf; + g_cap_left = cap; + buf[0] = '\0'; + g_mode = TEST_LOG_CAPTURE; +} + +void test_log_capture_end(void) +{ + g_mode = g_saved_mode; +} + +void test_log_count_reset(void) +{ + g_count = 0; +} + +unsigned test_log_count_get(void) +{ + return g_count; +} + +void test_log_env_default(void) +{ + const char *v = getenv("TAI_LOOPBACK_VERBOSE"); + g_mode = (v && v[0] && v[0] != '0') ? TEST_LOG_PASSTHROUGH : TEST_LOG_QUIET; +} + +/* Rebuild the tagged format the macro layer normally folds together + * ("[tag] " + fmt): captured lines keep the "[tag] " prefix tests + * strstr() for, and the va_list stays unconsumed for the caller. */ +static void build_tagged(char *out, size_t cap, const char *tag, const char *fmt) +{ + snprintf(out, cap, "[%s] %s", tag ? tag : "", fmt ? fmt : ""); +} + +void test_log_sink(log_level_t level, const char *tag, const char *fmt, ...) +{ + va_list args; + + switch (g_mode) { + case TEST_LOG_QUIET: + return; + case TEST_LOG_COUNT: + g_count++; + return; + case TEST_LOG_CAPTURE: { + /* va_copy discipline: vsnprintf() below consumes the va_list, so + * it gets a copy -- the passthrough afterwards needs the original + * (a consumed va_list re-vfprintf()ed is UB, C11 7.16.1; it + * happened to work on macOS/arm64 and produced parameter-shifted + * garbage on Linux/x86-64). vsnprintf returns the length it + * WOULD have written, so it exceeds the buffer on a truncated + * message: clamp before memcpy or it reads past `line`. The + * buffers must stay comfortably larger than any fmt template: + * build_tagged() truncates BEFORE formatting, so an overlong + * template would silently lose its tail here (routed templates + * do get long -- one coreHTTP parse error interpolates up to a + * whole response buffer). */ + va_start(args, fmt); + char tagged[1024]; + build_tagged(tagged, sizeof tagged, tag, fmt); + va_list copy; + va_copy(copy, args); + char line[1024]; + int n = vsnprintf(line, sizeof line, tagged, copy); + va_end(copy); + size_t len = (n > 0 && (size_t)n < sizeof line) ? (size_t)n : sizeof line - 1; + if (n > 0 && g_cap && len + 2 < g_cap_left) { + memcpy(g_cap, line, len); + g_cap += len; + *g_cap++ = '\n'; + *g_cap = '\0'; + g_cap_left -= len + 1; + } + /* Keep the default output too when the window opened on + * passthrough, so a failing run stays readable; a window opened + * on quiet (a suite that opted into silence) stays silent. */ + if (g_saved_mode == TEST_LOG_PASSTHROUGH) { + log_emit_valist(level, tagged, args); + } + va_end(args); + return; + } + default: + break; + } + + va_start(args, fmt); + char tagged[512]; + build_tagged(tagged, sizeof tagged, tag, fmt); + log_emit_valist(level, tagged, args); + va_end(args); +} diff --git a/modules/rtc-tcp-client/test/test_log.h b/modules/rtc-tcp-client/test/test_log.h new file mode 100644 index 0000000..5dbda3a --- /dev/null +++ b/modules/rtc-tcp-client/test/test_log.h @@ -0,0 +1,48 @@ +/* + * test_log.h -- this binary's single log destination, fixed at compile + * time: the log_config/agentic_kit_config.h next door remaps + * AGENTIC_KIT_LOG to test_log_sink, so every TU of the test build -- + * SDK sources included -- dispatches into the one function below. + * + * What used to vary at runtime via log_set_handler() install/uninstall + * is now a mode inside that one sink. Modes: + * PASSTHROUGH default stderr output, unchanged shape + * CAPTURE append lines to the registered buffer, keep stderr + * (a failing run stays readable) + * QUIET swallow everything + * COUNT count lines, no output + */ +#ifndef TAI_TEST_LOG_H +#define TAI_TEST_LOG_H + +#include + +#include "log.h" + +typedef enum { + TEST_LOG_PASSTHROUGH, + TEST_LOG_CAPTURE, + TEST_LOG_QUIET, + TEST_LOG_COUNT, +} test_log_mode_t; + +void test_log_set_mode(test_log_mode_t mode); + +/* Capture window: lines append to buf (one per line, NUL-terminated, + * truncation-safe) until test_log_capture_end() restores the mode that + * was active before the window opened. */ +void test_log_capture_begin(char *buf, size_t cap); +void test_log_capture_end(void); + +/* COUNT mode's counter. */ +void test_log_count_reset(void); +unsigned test_log_count_get(void); + +/* Integration-suite default: quiet unless TAI_LOOPBACK_VERBOSE is set + * (and not "0") -- the gate tai_pal_loopback's lb_log used to apply. */ +void test_log_env_default(void); + +/* The AGENTIC_KIT_LOG dispatch target for every TU of this test build. */ +void test_log_sink(log_level_t level, const char *tag, const char *fmt, ...); + +#endif /* TAI_TEST_LOG_H */ diff --git a/modules/rtc-tcp-client/test/test_pkt_log_compile_matrix.cmake b/modules/rtc-tcp-client/test/test_pkt_log_compile_matrix.cmake new file mode 100644 index 0000000..68d3dc1 --- /dev/null +++ b/modules/rtc-tcp-client/test/test_pkt_log_compile_matrix.cmake @@ -0,0 +1,143 @@ +cmake_minimum_required(VERSION 3.20) + +foreach(required_var SOURCE_DIR OUTPUT_DIR C_COMPILER) + if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "") + message(FATAL_ERROR "${required_var} is required") + endif() +endforeach() + +set(pkt_log_source + "${SOURCE_DIR}/modules/rtc-tcp-client/src/tai_pkt_log.c") +set(include_args + "-I${SOURCE_DIR}/modules/rtc-tcp-client/include" + "-I${SOURCE_DIR}/modules/rtc-tcp-client/src" + "-I${SOURCE_DIR}/common" + "-I${SOURCE_DIR}/pal" + "-I${SOURCE_DIR}/third_party/mbedtls/include" +) + +file(REMOVE_RECURSE "${OUTPUT_DIR}") +file(MAKE_DIRECTORY "${OUTPUT_DIR}") + +# Preprocess AND compile one configuration, then check the sinks that +# survived. `assert_level` is the ceiling tai_pkt_log.c should have acted +# under, whichever knob (global or per-module) supplied it. +function(run_pkt_log_combo stem) + set(preprocessed "${OUTPUT_DIR}/${stem}.i") + set(object_file "${OUTPUT_DIR}/${stem}.o") + + execute_process( + COMMAND "${C_COMPILER}" + -E -P + ${extra_defines} + ${include_args} + "${pkt_log_source}" + -o "${preprocessed}" + RESULT_VARIABLE preprocess_rc + OUTPUT_VARIABLE preprocess_stdout + ERROR_VARIABLE preprocess_stderr + ) + if(NOT preprocess_rc EQUAL 0) + message(FATAL_ERROR + "preprocess failed for ${stem}:\n" + "${preprocess_stdout}${preprocess_stderr}") + endif() + + # Compile the exact preprocessed translation unit too: the string/sink + # assertions below must not pass merely because an invalid branch was + # removed textually. + execute_process( + COMMAND "${C_COMPILER}" -c "${preprocessed}" -o "${object_file}" + RESULT_VARIABLE compile_rc + OUTPUT_VARIABLE compile_stdout + ERROR_VARIABLE compile_stderr + ) + if(NOT compile_rc EQUAL 0) + message(FATAL_ERROR + "compile failed for ${stem}:\n" + "${compile_stdout}${compile_stderr}") + endif() + + file(READ "${preprocessed}" source) + string(FIND "${source}" "tai_log_packet(uint8_t" function_pos) + string(FIND "${source}" "packet-type" formatter_pos) + string(REGEX MATCH "log_emit\\([\t\r\n ]*3[\t\r\n ]*," info_sink "${source}") + string(REGEX MATCH "log_emit\\([\t\r\n ]*4[\t\r\n ]*," debug_sink "${source}") + + if(assert_level LESS 3) + if(NOT function_pos EQUAL -1 OR NOT formatter_pos EQUAL -1) + message(FATAL_ERROR + "${stem}: formatter survived below INFO") + endif() + if(NOT "${info_sink}" STREQUAL "" OR NOT "${debug_sink}" STREQUAL "") + message(FATAL_ERROR + "${stem}: log sink survived below INFO") + endif() + else() + if(function_pos EQUAL -1 OR formatter_pos EQUAL -1) + message(FATAL_ERROR + "${stem}: INFO formatter is missing") + endif() + if("${info_sink}" STREQUAL "") + message(FATAL_ERROR + "${stem}: INFO sink is missing") + endif() + + if(assert_level EQUAL 4 AND sample_n EQUAL 0) + if("${debug_sink}" STREQUAL "") + message(FATAL_ERROR + "${stem}: DEBUG flood sink is missing") + endif() + elseif(NOT "${debug_sink}" STREQUAL "") + message(FATAL_ERROR + "${stem}: unexpected DEBUG sink") + endif() + endif() +endfunction() + +# Dimension 1: the SDK-wide ceiling drives the compile-out (the per-module +# knob defaults to it when not defined). +foreach(level 0 2 3 4) + foreach(sample_n 0 50) + set(stem "tai_pkt_log_level_${level}_sample_${sample_n}") + set(assert_level "${level}") + set(extra_defines + "-DAGENTIC_KIT_LOG_LEVEL=${level}" + "-DAGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N=${sample_n}") + run_pkt_log_combo("${stem}") + endforeach() +endforeach() + +# Dimension 2: the per-module ceiling drives the SAME compile-out with the +# SDK-wide ceiling pinned open at 4 -- AGENTIC_KIT_TAI_LOG_LEVEL must +# behave exactly as the global level did, without touching anything else. +foreach(tai_level 0 2 3 4) + foreach(sample_n 0 50) + set(stem "tai_pkt_log_global4_tai_${tai_level}_sample_${sample_n}") + set(assert_level "${tai_level}") + set(extra_defines + "-DAGENTIC_KIT_LOG_LEVEL=4" + "-DAGENTIC_KIT_TAI_LOG_LEVEL=${tai_level}" + "-DAGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N=${sample_n}") + run_pkt_log_combo("${stem}") + endforeach() +endforeach() + +# Dimension 3: the module ceiling is LOWER-ONLY -- a value above the +# SDK-wide ceiling is clamped to it in tai_config_defaults.h, so these +# builds must compile exactly as the lower global ceiling alone would: +# no formatter at 2, no flood branch at 3, nothing resurrected. +foreach(over "2;3;0" "2;4;50" "3;4;0") + list(GET over 0 global_level) + list(GET over 1 tai_level) + list(GET over 2 sample_n) + set(stem "tai_pkt_log_over_global${global_level}_tai${tai_level}_sample_${sample_n}") + set(assert_level "${global_level}") + set(extra_defines + "-DAGENTIC_KIT_LOG_LEVEL=${global_level}" + "-DAGENTIC_KIT_TAI_LOG_LEVEL=${tai_level}" + "-DAGENTIC_KIT_TAI_LOG_MEDIA_SAMPLE_N=${sample_n}") + run_pkt_log_combo("${stem}") +endforeach() + +message(STATUS "tai_pkt_log compile-time matrix passed") diff --git a/modules/tuya-ble/include/tuya_ble_config_defaults.h b/modules/tuya-ble/include/tuya_ble_config_defaults.h new file mode 100644 index 0000000..06d0a60 --- /dev/null +++ b/modules/tuya-ble/include/tuya_ble_config_defaults.h @@ -0,0 +1,36 @@ +/* + * tuya_ble_config_defaults.h -- tuya-ble build-time knobs: the + * AGENTIC_KIT_TUYA_BLE_* #ifndef defaults, nothing else. Non-knob module + * data stays where it was (protocol geometry and port-API sizing in + * include/tuya_ble_prov.h -- see the header comment there for why those + * are not knobs). + * + * It pulls common/log.h FIRST: that header is where integrator overrides + * (agentic_kit_config.h on the include path, -D, AGENTIC_KIT_USER_CONFIG) + * are applied -- so overrides win over every knob below. Not a public + * API header. + */ + +#ifndef AGENTIC_KIT_TUYA_BLE_CONFIG_DEFAULTS_H +#define AGENTIC_KIT_TUYA_BLE_CONFIG_DEFAULTS_H + +#include "log.h" + +/* Optional per-module log ceiling. Defaults to the SDK-wide ceiling and can + * only LOWER tuya-ble below it: the effective ceiling is the smaller of the + * two, so a value above AGENTIC_KIT_LOG_LEVEL is clamped to it right below. + * Gates the SDK-internal TUYA_BLE_HAL_LOG* binding in src/tuya_ble_internal.h. + * The ceiling applies at the level a line actually emits at, so LOGI (which + * dispatches at debug) needs 4, not 3. */ +#ifndef AGENTIC_KIT_TUYA_BLE_LOG_LEVEL +#define AGENTIC_KIT_TUYA_BLE_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif +/* Lower-only, enforced ONCE here: the HAL rebind in src/tuya_ble_internal.h + * keys on this knob directly (including the HEXDUMP body), so clamp it to + * the SDK-wide ceiling to make a too-high value a true no-op. */ +#if AGENTIC_KIT_TUYA_BLE_LOG_LEVEL > AGENTIC_KIT_LOG_LEVEL +#undef AGENTIC_KIT_TUYA_BLE_LOG_LEVEL +#define AGENTIC_KIT_TUYA_BLE_LOG_LEVEL AGENTIC_KIT_LOG_LEVEL +#endif + +#endif /* AGENTIC_KIT_TUYA_BLE_CONFIG_DEFAULTS_H */ diff --git a/modules/tuya-ble/include/tuya_ble_prov.h b/modules/tuya-ble/include/tuya_ble_prov.h index a614187..fd5cfac 100644 --- a/modules/tuya-ble/include/tuya_ble_prov.h +++ b/modules/tuya-ble/include/tuya_ble_prov.h @@ -9,15 +9,17 @@ extern "C" { #include #include -/* Protocol geometry and port-API sizing, not build knobs -- tuya-ble has no - * compile-time knobs today. The lengths and encryption-mode bytes below are +/* Protocol geometry and port-API sizing, not build knobs -- tuya-ble's only + * compile-time knob today is the per-module log ceiling + * AGENTIC_KIT_TUYA_BLE_LOG_LEVEL (include/tuya_ble_config_defaults.h). The + * lengths and encryption-mode bytes below are * fixed by the Tuya BLE pairing / big-data protocol. TUYA_BLE_RX_BUF_SIZE / * TUYA_BLE_TX_BUF_SIZE / TUYA_BLE_TX_QUEUE_DEPTH additionally size the public * tuya_ble_prov_state_t that ports embed and size their own buffers against, * making them part of the port API surface: changing one changes a struct * layout ports must recompile and re-size against, not something a build-time - * override can do. When the first real tuya-ble knob appears it gets its own - * include/tuya_ble_config_defaults.h named AGENTIC_KIT_TUYA_BLE_*, anchored to + * override can do. Further knobs land beside it in that file, named + * AGENTIC_KIT_TUYA_BLE_*, anchored to * the common/log.h override pickup like every other defaults file. */ #define TUYA_BLE_SSID_MAX_LEN 64 #define TUYA_BLE_PASSWORD_MAX_LEN 64 diff --git a/modules/tuya-ble/src/tuya_ble_bigdata.c b/modules/tuya-ble/src/tuya_ble_bigdata.c index c512a5e..4f773e8 100644 --- a/modules/tuya-ble/src/tuya_ble_bigdata.c +++ b/modules/tuya-ble/src/tuya_ble_bigdata.c @@ -143,25 +143,25 @@ int tuya_ble_bigdata_on_downlink(tuya_ble_prov_state_t *state, uint16_t flag = ((uint16_t)payload[0] << 8) | payload[1]; uint16_t subcmd = ((uint16_t)payload[2] << 8) | payload[3]; if (flag & BIGDATA_FLAG_SEGMENTED) { - log_emit(LOG_WARN, "[ble] bigdata: segmented downlink unsupported"); + TUYA_BLE_HAL_LOGW("bigdata: segmented downlink unsupported"); return -1; } if (subcmd == TUYA_BLE_SUB_NCFG_STAT) { return send_netcfg_status(state); } if (subcmd != TUYA_BLE_SUB_WIFI_LIST) { - log_emit(LOG_WARN, "[ble] bigdata: unsupported subcommand 0x%04x", subcmd); + TUYA_BLE_HAL_LOGW("bigdata: unsupported subcommand 0x%04x", subcmd); return -1; } if (state->wifi_scan_pending) { - log_emit(LOG_WARN, "[ble] bigdata: WiFi scan already pending"); + TUYA_BLE_HAL_LOGW("bigdata: WiFi scan already pending"); return send_empty_list(state); } char ccode[3]; uint16_t count = parse_count_and_ccode(payload + 4, len - 4, ccode); if (state->cfg.wifi_scan_request == NULL) { - log_emit(LOG_WARN, "[ble] bigdata: no WiFi scan provider"); + TUYA_BLE_HAL_LOGW("bigdata: no WiFi scan provider"); return send_empty_list(state); } @@ -171,7 +171,7 @@ int tuya_ble_bigdata_on_downlink(tuya_ble_prov_state_t *state, if (state->cfg.wifi_scan_request(count, ccode, state->wifi_scan_token, state->cfg.wifi_scan_ctx) != 0) { state->wifi_scan_pending = false; - log_emit(LOG_WARN, "[ble] bigdata: WiFi scan start failed"); + TUYA_BLE_HAL_LOGW("bigdata: WiFi scan start failed"); return send_empty_list(state); } return 0; diff --git a/modules/tuya-ble/src/tuya_ble_internal.h b/modules/tuya-ble/src/tuya_ble_internal.h index 9cec3f3..ed9dbd1 100644 --- a/modules/tuya-ble/src/tuya_ble_internal.h +++ b/modules/tuya-ble/src/tuya_ble_internal.h @@ -3,23 +3,44 @@ #include "tuya_ble_prov.h" #include "log.h" +/* tuya-ble's build-time knobs live in include/tuya_ble_config_defaults.h + * (AGENTIC_KIT_TUYA_BLE_*; currently one -- the per-module log ceiling + * below). That file includes common/log.h first, since log.h is where + * integrator overrides are picked up. */ + #include -/* tuya-ble has no build-time knobs today -- why the constants in - * tuya_ble_prov.h are not knobs is documented there. When the first one - * appears it lands in include/tuya_ble_config_defaults.h as - * AGENTIC_KIT_TUYA_BLE_*; that file must include common/log.h first, since - * log.h is where integrator overrides are picked up. */ +#include "tuya_ble_config_defaults.h" -/* Shared SDK-internal binding of the HAL log macros onto the PAL log facade: - * every module diagnostic carries the "[ble] " prefix. */ +/* Shared SDK-internal binding of the HAL log macros onto the global log + * facade: every module diagnostic carries the "[ble] " prefix. The + * ceiling is min(AGENTIC_KIT_LOG_LEVEL, AGENTIC_KIT_TUYA_BLE_LOG_LEVEL): + * the SDK-wide gate applies once in log.h, and the module knob (default = + * that) only lowers this module further -- never raises. The ceiling + * applies at the level a line actually emits at, so LOGI (dispatched at + * debug) needs 4, not 3. */ #undef TUYA_BLE_HAL_LOGI #undef TUYA_BLE_HAL_LOGW #undef TUYA_BLE_HAL_LOGE #undef TUYA_BLE_HAL_HEXDUMP -#define TUYA_BLE_HAL_LOGI(fmt, ...) log_emit(LOG_DEBUG, "[ble] " fmt, ##__VA_ARGS__) -#define TUYA_BLE_HAL_LOGW(fmt, ...) log_emit(LOG_WARN, "[ble] " fmt, ##__VA_ARGS__) -#define TUYA_BLE_HAL_LOGE(fmt, ...) log_emit(LOG_ERROR, "[ble] " fmt, ##__VA_ARGS__) +#if AGENTIC_KIT_TUYA_BLE_LOG_LEVEL >= 4 +#define TUYA_BLE_HAL_LOGI(fmt, ...) log_tag_debug("ble", fmt, ##__VA_ARGS__) +#else +#define TUYA_BLE_HAL_LOGI(fmt, ...) +#endif +#if AGENTIC_KIT_TUYA_BLE_LOG_LEVEL >= 2 +#define TUYA_BLE_HAL_LOGW(fmt, ...) log_tag_warn("ble", fmt, ##__VA_ARGS__) +#else +#define TUYA_BLE_HAL_LOGW(fmt, ...) +#endif +#if AGENTIC_KIT_TUYA_BLE_LOG_LEVEL >= 1 +#define TUYA_BLE_HAL_LOGE(fmt, ...) log_tag_error("ble", fmt, ##__VA_ARGS__) +#else +#define TUYA_BLE_HAL_LOGE(fmt, ...) +#endif +/* HEXDUMP formats every byte before dispatching; below the ceiling + * the whole body and its arguments are compiled out. */ +#if AGENTIC_KIT_TUYA_BLE_LOG_LEVEL >= 4 #define TUYA_BLE_HAL_HEXDUMP(buf, len) \ do { \ const uint8_t *p_ = (const uint8_t *)(buf); \ @@ -31,8 +52,11 @@ p_[i_]); \ } \ if (o_) hex_[o_ - 1] = '\0'; \ - log_emit(LOG_DEBUG, "[ble] HEX(%u): %s", (unsigned)n_, hex_); \ + log_tag_debug("ble", "HEX(%u): %s", (unsigned)n_, hex_); \ } while (0) +#else +#define TUYA_BLE_HAL_HEXDUMP(...) ((void)0) +#endif /* Scan tokens are nonzero and never reused across invalidations: bump and * skip the zero wrap. */ diff --git a/modules/tuya-ble/src/tuya_ble_prov.c b/modules/tuya-ble/src/tuya_ble_prov.c index 6cb8495..57445f6 100644 --- a/modules/tuya-ble/src/tuya_ble_prov.c +++ b/modules/tuya-ble/src/tuya_ble_prov.c @@ -304,6 +304,10 @@ int tuya_ble_prov_send_frame(tuya_ble_prov_state_t *state, uint16_t cmd, static void handle_dev_info_req(tuya_ble_prov_state_t *state, const uint8_t *data, uint16_t data_len) { + /* data/data_len feed only the protocol trace; keep -Wextra quiet when + * the module ceiling compiles it out. */ + (void)data; + (void)data_len; TUYA_BLE_HAL_LOGI("[PROTO] FRM_QRY_DEV_INFO_REQ, data_len=%d (pkt_len=%u)", data_len, state->peer_pkt_len); TUYA_BLE_HAL_HEXDUMP(data, data_len > 32 ? 32 : data_len); diff --git a/modules/tuya-ble/test/test_prov.c b/modules/tuya-ble/test/test_prov.c index 45180e5..7a9f799 100644 --- a/modules/tuya-ble/test/test_prov.c +++ b/modules/tuya-ble/test/test_prov.c @@ -1,6 +1,7 @@ #include "tuya_ble_prov.h" #include "iot_client.h" #include "log.h" +#include "test_log.h" #include #include @@ -214,7 +215,9 @@ int test_wire(void); int main(void) { EXPECT_TRUE(iot_init_default() == 0); - log_set_level(LOG_ERROR); + /* No runtime log level anymore -- keep this suite quiet by setting + * the compile-time sink's mode. */ + test_log_set_mode(TEST_LOG_QUIET); EXPECT_TRUE(test_init_rejects_invalid_args() == 0); EXPECT_TRUE(test_wifi_list_capability_advertisement() == 0); EXPECT_TRUE(test_init_builds_adv_and_rsp_data() == 0); diff --git a/modules/tuya-ble/test/test_wire.c b/modules/tuya-ble/test/test_wire.c index d63eb98..9169752 100644 --- a/modules/tuya-ble/test/test_wire.c +++ b/modules/tuya-ble/test/test_wire.c @@ -3,6 +3,7 @@ #include "tuya_ble_prov.h" #include "tuya_ble_bigdata.h" #include "log.h" +#include "test_log.h" #include "mbedtls/aes.h" #include "mbedtls/md5.h" #include @@ -335,15 +336,6 @@ static int pending_credentials_are_not_replaced(void) } static unsigned transport_log_flags; -static void capture_transport_log(log_level_t level, const char *fmt, va_list args) -{ - char message[256]; - vsnprintf(message, sizeof(message), fmt, args); - if (level != LOG_WARN || !strstr(message, "[ble] [TRSMITR]")) return; - if (strstr(message, "out-of-order")) transport_log_flags |= 1; - if (strstr(message, "RX transfer expired")) transport_log_flags |= 2; - if (strstr(message, "TX stalled")) transport_log_flags |= 4; -} static int transport_warnings_reach_log_facade(void) { @@ -353,10 +345,12 @@ static int transport_warnings_reach_log_facade(void) CHECK(!setup(&state, &peer, key)); uint8_t first[] = {0, 10, 0x40, 0xaa}; uint8_t skipped[] = {2, 0xbb}; - log_level_t saved_level = log_get_level(); + /* Capture the window, then scan it with the filter the old in-handler + * pass applied: the three phrases below exist only in [TRSMITR] + * warns, so substring hits are warn hits. */ + char captured[4096]; transport_log_flags = 0; - log_set_level(LOG_WARN); - log_set_handler(capture_transport_log); + test_log_capture_begin(captured, sizeof captured); int start = tuya_ble_prov_on_data(&state, first, sizeof(first)); int invalid = tuya_ble_prov_on_data(&state, skipped, sizeof(skipped)); int restart = tuya_ble_prov_on_data(&state, first, sizeof(first)); @@ -364,8 +358,10 @@ static int transport_warnings_reach_log_facade(void) peer.busy = 1; int queued = tuya_ble_prov_send_frame(&state, 0x001e, NULL, 0, TUYA_BLE_ENCRYPTION_MODE_NONE); int stalled = tuya_ble_prov_tick(&state, 20000); - log_set_handler(NULL); - log_set_level(saved_level); + test_log_capture_end(); + if (strstr(captured, "out-of-order")) transport_log_flags |= 1; + if (strstr(captured, "RX transfer expired")) transport_log_flags |= 2; + if (strstr(captured, "TX stalled")) transport_log_flags |= 4; CHECK(!start && invalid < 0 && !restart && !expired && !queued && stalled < 0); CHECK(transport_log_flags == 7); return 0; diff --git a/pal/pal_config_defaults.h b/pal/pal_config_defaults.h index df752ce..fe5f464 100644 --- a/pal/pal_config_defaults.h +++ b/pal/pal_config_defaults.h @@ -5,8 +5,9 @@ * * pal_freertos.c includes this file directly. It pulls common/log.h FIRST: * that header is where integrator overrides (agentic_kit_config.h on the - * include path, -D, AGENTIC_KIT_USER_CONFIG) are applied -- so overrides - * win over every default below. Not a public API header. + * include path, -D, AGENTIC_KIT_USER_CONFIG) are applied and the SDK-wide + * log ceiling defaults -- so overrides win over every default below. Not a + * public API header. */ #ifndef AGENTIC_KIT_PAL_CONFIG_DEFAULTS_H diff --git a/pal/pal_freertos.c b/pal/pal_freertos.c index afc456f..5057de7 100644 --- a/pal/pal_freertos.c +++ b/pal/pal_freertos.c @@ -89,14 +89,14 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int gai_rc = getaddrinfo(host, port_str, &hints, &res); if (gai_rc != 0 || !res) { - log_emit(LOG_ERROR, "[pal] getaddrinfo(%s:%u) failed: %d", + log_tag_error("pal", "getaddrinfo(%s:%u) failed: %d", host, (unsigned)port, gai_rc); return NULL; } int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd < 0) { - log_emit(LOG_ERROR, "[pal] socket() failed: errno=%d", errno); + log_tag_error("pal", "socket() failed: errno=%d", errno); freeaddrinfo(res); return NULL; } @@ -108,7 +108,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int rc = connect(fd, res->ai_addr, res->ai_addrlen); if (rc != 0 && errno != EINPROGRESS) { - log_emit(LOG_ERROR, "[pal] connect(%s:%u) failed: errno=%d", + log_tag_error("pal", "connect(%s:%u) failed: errno=%d", host, (unsigned)port, errno); close(fd); freeaddrinfo(res); @@ -132,7 +132,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m sel = select(fd + 1, NULL, &wfds, NULL, &tv); } while (sel < 0 && errno == EINTR); if (sel <= 0) { /* timeout (0) or error (<0) */ - log_emit(LOG_ERROR, "[pal] connect(%s:%u) %s (errno=%d)", + log_tag_error("pal", "connect(%s:%u) %s (errno=%d)", host, (unsigned)port, sel == 0 ? "timed out" : "select failed", errno); close(fd); @@ -142,7 +142,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int soerr = 0; socklen_t sl = sizeof(soerr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &soerr, &sl) != 0 || soerr != 0) { - log_emit(LOG_ERROR, "[pal] connect(%s:%u) failed: errno=%d", + log_tag_error("pal", "connect(%s:%u) failed: errno=%d", host, (unsigned)port, soerr); close(fd); freeaddrinfo(res); @@ -159,7 +159,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m fr_tcp_t *h = (fr_tcp_t *)pvPortMalloc(sizeof(fr_tcp_t)); if (!h) { - log_emit(LOG_ERROR, "[pal] tcp handle alloc failed"); + log_tag_error("pal", "tcp handle alloc failed"); close(fd); return NULL; } @@ -195,7 +195,7 @@ static int pal_tcp_send(void *handle, const uint8_t *buf, size_t len, if (n == 0) return 0; if (errno == EAGAIN || errno == EWOULDBLOCK) return PAL_ERR_AGAIN; - log_emit(LOG_ERROR, "[pal] tcp_send failed: errno=%d", errno); + log_tag_error("pal", "tcp_send failed: errno=%d", errno); return PAL_ERR_NET; } @@ -225,7 +225,7 @@ static int pal_tcp_recv(void *handle, uint8_t *buf, size_t buf_len, if (n == 0) return 0; /* EOF */ if (errno == EAGAIN || errno == EWOULDBLOCK) return PAL_ERR_AGAIN; - log_emit(LOG_ERROR, "[pal] tcp_recv failed: errno=%d", errno); + log_tag_error("pal", "tcp_recv failed: errno=%d", errno); return PAL_ERR_NET; } diff --git a/pal/pal_posix.c b/pal/pal_posix.c index 890f619..0409cb0 100644 --- a/pal/pal_posix.c +++ b/pal/pal_posix.c @@ -55,14 +55,14 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int gai_rc = getaddrinfo(host, port_str, &hints, &res); if (gai_rc != 0 || !res) { - log_emit(LOG_ERROR, "[pal] getaddrinfo(%s:%u) failed: %s", + log_tag_error("pal", "getaddrinfo(%s:%u) failed: %s", host, (unsigned)port, gai_strerror(gai_rc)); return NULL; } int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd < 0) { - log_emit(LOG_ERROR, "[pal] socket() failed: %s", strerror(errno)); + log_tag_error("pal", "socket() failed: %s", strerror(errno)); freeaddrinfo(res); return NULL; } @@ -73,7 +73,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int rc = connect(fd, res->ai_addr, res->ai_addrlen); if (rc != 0 && errno != EINPROGRESS) { - log_emit(LOG_ERROR, "[pal] connect(%s:%u) failed: %s", + log_tag_error("pal", "connect(%s:%u) failed: %s", host, (unsigned)port, strerror(errno)); close(fd); freeaddrinfo(res); @@ -97,7 +97,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m sel = select(fd + 1, NULL, &wfds, NULL, &tv); } while (sel < 0 && errno == EINTR); if (sel <= 0) { /* timeout (0) or error (<0) */ - log_emit(LOG_ERROR, "[pal] connect(%s:%u) %s", host, (unsigned)port, + log_tag_error("pal", "connect(%s:%u) %s", host, (unsigned)port, sel == 0 ? "timed out" : strerror(errno)); close(fd); freeaddrinfo(res); @@ -106,7 +106,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m int soerr = 0; socklen_t sl = sizeof(soerr); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &soerr, &sl) != 0 || soerr != 0) { - log_emit(LOG_ERROR, "[pal] connect(%s:%u) failed: %s", + log_tag_error("pal", "connect(%s:%u) failed: %s", host, (unsigned)port, strerror(soerr)); close(fd); freeaddrinfo(res); @@ -128,7 +128,7 @@ static void *pal_tcp_connect(const char *host, uint16_t port, uint32_t timeout_m posix_tcp_t *h = (posix_tcp_t *)malloc(sizeof(posix_tcp_t)); if (!h) { - log_emit(LOG_ERROR, "[pal] tcp handle alloc failed"); + log_tag_error("pal", "tcp handle alloc failed"); close(fd); return NULL; } @@ -164,7 +164,7 @@ static int pal_tcp_send(void *handle, const uint8_t *buf, size_t len, if (n == 0) return 0; if (errno == EAGAIN || errno == EWOULDBLOCK) return PAL_ERR_AGAIN; - log_emit(LOG_ERROR, "[pal] tcp_send failed: %s (errno=%d)", strerror(errno), errno); + log_tag_error("pal", "tcp_send failed: %s (errno=%d)", strerror(errno), errno); return PAL_ERR_NET; } @@ -194,7 +194,7 @@ static int pal_tcp_recv(void *handle, uint8_t *buf, size_t buf_len, if (n == 0) return 0; /* EOF */ if (errno == EAGAIN || errno == EWOULDBLOCK) return PAL_ERR_AGAIN; - log_emit(LOG_ERROR, "[pal] tcp_recv failed: %s (errno=%d)", strerror(errno), errno); + log_tag_error("pal", "tcp_recv failed: %s (errno=%d)", strerror(errno), errno); return PAL_ERR_NET; }