blob: d6b22f7c6c5a6bb1feea460ca027af7e6224961e [file] [log] [blame]
/*
* Copyright (c) 2020-2023 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file defines macros, constants, and interfaces for a
* platform-independent logging interface for the chip SDK.
*
* chip SDK clients may choose, at compile time, among Android,
* C Standard I/O, or external (platform- and integrator-defined)
* logging style implementations that will be invoked when any of
* the following preprocessor symbols are asserted:
*
* - #CHIP_ERROR_LOGGING
* - #CHIP_PROGRESS_LOGGING
* - #CHIP_DETAIL_LOGGING
* - #CHIP_AUTOMATION_LOGGING
*
*/
#pragma once
#include <lib/core/CHIPConfig.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/EnforceFormat.h>
#include <lib/support/VerificationMacrosNoLogging.h>
#include <lib/support/logging/Constants.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG && defined(CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE)
#include CHIP_SYSTEM_CONFIG_PLATFORM_LOG_INCLUDE
#endif
#if CHIP_PW_TOKENIZER_LOGGING
#include "pw_tokenizer/tokenize.h"
#endif
/**
* @namespace chip::Logging
*
* @brief
* This namespace includes all interfaces within chip for shared
* logging support.
*
* The interfaces include macros, constants, and functions for a
* platform-independent logging interface for the chip SDK.
*
* chip SDK clients may choose, at compile time, among Android,
* C Standard I/O, or external (platform- and integrator-defined)
* logging style implementations that will be invoked when any of
* the following preprocessor symbols are asserted:
*
* - #CHIP_ERROR_LOGGING
* - #CHIP_PROGRESS_LOGGING
* - #CHIP_DETAIL_LOGGING
* - #CHIP_AUTOMATION_LOGGING
*
*/
namespace chip {
namespace Logging {
// Log redirection
using LogRedirectCallback_t = void (*)(const char * module, uint8_t category, const char * msg, va_list args);
#if CHIP_ERROR_LOGGING
/**
* @def ChipLogError(MOD, MSG, ...)
*
* @brief
* Log a chip message for the specified module in the 'Error'
* category.
*
*/
#define ChipLogError(MOD, MSG, ...) ChipInternalLog(MOD, ERROR, MSG, ##__VA_ARGS__)
#else // CHIP_ERROR_LOGGING
#define ChipLogError(MOD, MSG, ...) ((void) 0)
#endif // CHIP_ERROR_LOGGING
#if CHIP_PROGRESS_LOGGING
/**
* @def ChipLogProgress(MOD, MSG, ...)
*
* @brief
* Log a chip message for the specified module in the 'Progress'
* category.
*
*/
#define ChipLogProgress(MOD, MSG, ...) ChipInternalLog(MOD, PROGRESS, MSG, ##__VA_ARGS__)
#else // CHIP_PROGRESS_LOGGING
#define ChipLogProgress(MOD, MSG, ...) ((void) 0)
#endif // CHIP_PROGRESS_LOGGING
#if CHIP_DETAIL_LOGGING
/**
* @def ChipLogDetail(MOD, MSG, ...)
*
* @brief
* Log a chip message for the specified module in the 'Detail'
* category.
*
*/
#define ChipLogDetail(MOD, MSG, ...) ChipInternalLog(MOD, DETAIL, MSG, ##__VA_ARGS__)
#else // CHP_DETAIL_LOGGING
#define ChipLogDetail(MOD, MSG, ...) ((void) 0)
#endif // CHIP_DETAIL_LOGGING
#if CHIP_AUTOMATION_LOGGING
/**
* @def ChipLogAutomation(MSG, ...)
*
* @brief
* Log a chip message for the specified module in the 'Automation'
* category.
*
*/
#define ChipLogAutomation(MSG, ...) ChipInternalLog(Automation, AUTOMATION, MSG, ##__VA_ARGS__)
#else // CHIP_AUTOMATION_LOGGING
#define ChipLogAutomation(MOD, MSG, ...) ((void) 0)
#endif // CHIP_AUTOMATION_LOGGING
/**
* @def ChipLogIfFalse(aCondition)
*
* @brief
* This checks for the specified condition, which is expected to
* commonly be true and emits some log, based on configuration, if
* the condition is false.
*
* @note
* Evaluation of @a aCondition is always done, but logging is only enabled when
* #CHIP_CONFIG_ENABLE_CONDITION_LOGGING is enabled. This can be turned on or
* off for each compilation unit by enabling or disabling, as desired,
* #CHIP_CONFIG_ENABLE_CONDITION_LOGGING before ChipLogging.h is included by
* the preprocessor.
*
* Example Usage:
*
* @code
* #define CHIP_CONFIG_ENABLE_CONDITION_LOGGING 1
*
* #include <chip/Support/Logging/ChipLogging.h>
*
* ...
*
* void foo(void)
* {
* CHIP_ERROR err = CHIP_NO_ERROR;
*
* ...
*
* exit:
* ChipLogIfFalse(CHIP_END_OF_TLV == err);
* }
* @endcode
*
* @param[in] aCondition A Boolean expression to be evaluated.
*
* @sa CHIP_CONFIG_ENABLE_TRACE_ON_CHECK_FAILURE
*
*/
#if CHIP_CONFIG_ENABLE_CONDITION_LOGGING && !defined(ChipLogIfFalse)
#define ChipLogIfFalse(aCondition) \
do \
{ \
if (!(aCondition)) \
{ \
ChipLogError(NotSpecified, "Condition Failed (%s) at %s:%d", #aCondition, __FILE__, __LINE__); \
} \
} while (0)
#else // CHIP_CONFIG_ENABLE_CONDITION_LOGGING
#define ChipLogIfFalse(aCondition) IgnoreUnusedVariable(aCondition)
#endif // CHIP_CONFIG_ENABLE_CONDITION_LOGGING
/*
* @brief
* Macro for use in a string formatter for a 64-bit hex print.
* Will split into 2x 32-bit prints to support small printf libraries
*
* Example Usage:
*
* @code
* void foo() {
* uint64_t value = 0x1122334455667788;
* ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value));
* }
* @endcode
*
*/
#define ChipLogFormatX64 "%08" PRIX32 "%08" PRIX32
/*
* @brief
* Macro for use in a printf parameter list for a 64-bit value.
* Will split into MSB/LSB 32-bit values to use only 32-bit formatting.
*
* Example Usage:
*
* @code
* void foo() {
* uint64_t value = 0x1122334455667788;
* ChipLogProgress(Foo, "A 64-bit value: 0x" ChipLogFormatX64, ChipLogValueX64(value));
* }
* @endcode
*
* @param[in] aValue 64-bit value that will be split in 32-bit MSB/LSB part
*/
#define ChipLogValueX64(aValue) static_cast<uint32_t>(aValue >> 32), static_cast<uint32_t>(aValue)
/*
* @brief
* Macro for use in a string formatter for a MEI hex print.
* Will split into 2x 16-bit prints to display both the MEI prefix/suffix
*
* Example Usage:
*
* @code
* void foo() {
* chip::CommandId value = 0x12340001;
* ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value));
* }
* @endcode
*
*/
#define ChipLogFormatMEI "0x%04X_%04X"
/*
* @brief
* Macro for use in a printf parameter list for MEI value.
* Will split into MSB/LSB 16-bit values to separate prefix/suffix.
*
* Example Usage:
*
* @code
* void foo() {
* chip::CommandId value = 0x12340001;
* ChipLogProgress(Foo, "A MEI value: " ChipLogFormatMEI, ChipLogValueMEI(value));
* }
* @endcode
*
* @param[in] aValue "32-bit value that will be split in 16-bit MSB/LSB part
*/
#define ChipLogValueMEI(aValue) static_cast<uint16_t>(aValue >> 16), static_cast<uint16_t>(aValue)
/**
* Logging helpers for exchanges. For now just log the exchange id and whether
* it's an initiator or responder, but eventually we may want to log the peer
* node id as well (especially for the responder case). Some callsites only
* have the exchange id and initiator/responder boolean, not an actual exchange,
* so we want to have a helper for that case too.
*/
#define ChipLogFormatExchangeId "%u%c"
#define ChipLogValueExchangeId(id, isInitiator) id, ((isInitiator) ? 'i' : 'r')
#define ChipLogFormatExchange ChipLogFormatExchangeId
#define ChipLogValueExchange(ec) ChipLogValueExchangeId((ec)->GetExchangeId(), (ec)->IsInitiator())
#define ChipLogValueExchangeIdFromSentHeader(payloadHeader) \
ChipLogValueExchangeId((payloadHeader).GetExchangeID(), (payloadHeader).IsInitiator())
// A received header's initiator boolean is the inverse of the exchange's.
#define ChipLogValueExchangeIdFromReceivedHeader(payloadHeader) \
ChipLogValueExchangeId((payloadHeader).GetExchangeID(), !(payloadHeader).IsInitiator())
/**
* Logging helpers for protocol ids. A protocol id is a (vendor-id,
* protocol-id) pair.
*/
#define ChipLogFormatProtocolId "(%u, %u)"
#define ChipLogValueProtocolId(id) (id).GetVendorId(), (id).GetProtocolId()
/**
* Logging helpers for message counters, so we format them consistently.
*/
#define ChipLogFormatMessageCounter "%" PRIu32
/**
* Logging helpers for message types, so we format them consistently.
*/
#define ChipLogFormatMessageType "0x%x"
/** Logging helpers for scoped node ids, which is a tuple of <NodeId, FabricIndex> */
#define ChipLogFormatScopedNodeId "<" ChipLogFormatX64 ", %d>"
#define ChipLogValueScopedNodeId(id) ChipLogValueX64((id).GetNodeId()), (id).GetFabricIndex()
/**
* CHIP Logging Implementation internals.
*/
#if CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING
#define _CHIP_USE_LOGGING 1
#else
#define _CHIP_USE_LOGGING 0
#endif // CHIP_ERROR_LOGGING || CHIP_PROGRESS_LOGGING || CHIP_DETAIL_LOGGING || CHIP_AUTOMATION_LOGGING
// Log filtering (no-op unless CHIP_LOG_FILTERING is enabled)
#if _CHIP_USE_LOGGING && CHIP_LOG_FILTERING
DLL_EXPORT uint8_t GetLogFilter();
DLL_EXPORT void SetLogFilter(uint8_t category);
bool IsCategoryEnabled(uint8_t category);
#else // _CHIP_USE_LOGGING && CHIP_LOG_FILTERING
inline uint8_t GetLogFilter()
{
return kLogCategory_Max;
}
inline void SetLogFilter(uint8_t category) {}
inline bool IsCategoryEnabled(uint8_t category)
{
return true;
}
#endif // _CHIP_USE_LOGGING && CHIP_LOG_FILTERING
#if _CHIP_USE_LOGGING
// Get the module name associated with a LogModule, or "-" on invalid value.
const char * GetModuleName(LogModule module);
// Log redirection
DLL_EXPORT void SetLogRedirectCallback(LogRedirectCallback_t callback);
static constexpr uint16_t kMaxModuleNameLen = 3;
/* Internal macros mapping upper case definitions to camel case category constants*/
#define CHIP_LOG_CATEGORY_DETAIL chip::Logging::kLogCategory_Detail
#define CHIP_LOG_CATEGORY_PROGRESS chip::Logging::kLogCategory_Progress
#define CHIP_LOG_CATEGORY_ERROR chip::Logging::kLogCategory_Error
#define CHIP_LOG_CATEGORY_AUTOMATION chip::Logging::kLogCategory_Automation
/*
* CHIP Logging Modules Categories filtering implementation.
*
* @brief
* Macro for use to check if given category is enabled for given log module.
*
* Example Usage:
* Let's assume PROGRESS category control for DeviceLayer log module.
*
* Default behavior - category is not modified, so macro returns global category value:
* IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING
*
* Enabling category - category is enabled for module, the category value is ignored and the global value is used:
* #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 1
* IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns CHIP_PROGRESS_LOGGING
*
* Disabling category - category is disabled for module, ignoring global category value:
* #define CHIP_CONFIG_LOG_MODULE_DeviceLayer_PROGRESS 0
* IsModuleCategoryEnabled(DeviceLayer, PROGRESS) returns 0
*
* Algorithm flow:
* 1. IsModuleCategoryEnabled(MOD, CAT) uses MOD and CAT to create strings for category module macro and global category macro,
* and invokes _IsModuleCategoryEnabled1().
* 2. _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) invokes _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) to extract macros
* values.
* 3. _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) uses MOD_CAT to create string for helper macro.
* - If MOD_CAT is 0 the helper macro containing dummy arg is used.
* - If MOD_CAT doesn't exist, empty is used.
* 4. _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) invokes _IsModuleCategoryEnabled4() using different number of arguments
* depending on DUMMY_ARG.
* 5. _IsModuleCategoryEnabled4 output:
* - If category for module was not defined or define 1, the DUMMY_ARG was empty, so returning ARG2 is GLOB_CAT.
* - If category for module was defined 0, the DUMMY_ARG had one argument, so returning ARG2 is 0.
*
*/
#define IsModuleCategoryEnabled(MOD, CAT) _IsModuleCategoryEnabled1(CHIP_CONFIG_LOG_MODULE_##MOD##_##CAT, CHIP_##CAT##_LOGGING)
#define _IsModuleCategoryEnabled1(MOD_CAT, GLOB_CAT) _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT)
#define _IsModuleCategoryEnabled2(MOD_CAT, GLOB_CAT) \
_IsModuleCategoryEnabled3(_IsModuleCategoryEnabled3_DummyArg##MOD_CAT, GLOB_CAT)
#define _IsModuleCategoryEnabled3_DummyArg0 dummyArg,
#define _IsModuleCategoryEnabled3(DUMMY_ARG, GLOB_CAT) _IsModuleCategoryEnabled4(DUMMY_ARG 0, GLOB_CAT)
#define _IsModuleCategoryEnabled4(ARG1, ARG2, ...) (ARG2)
void Log(uint8_t module, uint8_t category, const char * msg, ...) ENFORCE_FORMAT(3, 4);
void LogV(uint8_t module, uint8_t category, const char * msg, va_list args) ENFORCE_FORMAT(3, 0);
#if CHIP_SYSTEM_CONFIG_PLATFORM_LOG
#ifndef ChipPlatformLog
#error "CHIP_SYSTEM_CONFIG_PLATFORM_LOG is enabled but ChipPlatformLog() is not defined"
#endif
#define ChipInternalLog(...) ChipPlatformLog(__VA_ARGS__)
#else // CHIP_SYSTEM_CONFIG_PLATFORM_LOG
#define ChipInternalLog(MOD, CAT, MSG, ...) \
if (CHIP_CONFIG_LOG_MODULE_##MOD && IsModuleCategoryEnabled(MOD, CAT)) \
{ \
ChipInternalLogImpl(MOD, CHIP_LOG_CATEGORY_##CAT, MSG, ##__VA_ARGS__); \
}
#endif // CHIP_SYSTEM_CONFIG_PLATFORM_LOG
#if CHIP_PW_TOKENIZER_LOGGING
void HandleTokenizedLog(uint32_t levels, pw_tokenizer_Token token, pw_tokenizer_ArgTypes, ...);
#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \
do \
{ \
if (chip::Logging::IsCategoryEnabled(CAT)) \
{ \
PW_TOKENIZE_FORMAT_STRING(PW_TOKENIZER_DEFAULT_DOMAIN, UINT32_MAX, MSG, __VA_ARGS__); \
::chip::Logging::HandleTokenizedLog((uint32_t) ((CAT << 8) | chip::Logging::kLogModule_##MOD), _pw_tokenizer_token, \
PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) PW_COMMA_ARGS(__VA_ARGS__)); \
} \
} while (0)
#else // CHIP_PW_TOKENIZER_LOGGING
#define ChipInternalLogImpl(MOD, CAT, MSG, ...) \
do \
{ \
if (chip::Logging::IsCategoryEnabled(CAT)) \
{ \
chip::Logging::Log(chip::Logging::kLogModule_##MOD, CAT, MSG, ##__VA_ARGS__); \
} \
} while (0)
#endif // CHIP_PW_TOKENIZER_LOGGING
#else // _CHIP_USE_LOGGING
inline void SetLogRedirectCallback(LogRedirectCallback_t callback) {}
#endif // _CHIP_USE_LOGGING
} // namespace Logging
} // namespace chip