blob: af450944c5394e2852a2ccc21de9ee65280cce84 [file] [log] [blame]
/*
* Copyright (c) 2016, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the logging related functions.
*/
#include "logging.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/string.hpp"
/*
* Verify debug uart dependency.
*
* It is reasonable to only enable the debug uart and not enable logs to the DEBUG uart.
*/
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
#error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
#endif
#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
#endif
#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
#endif
#ifdef __cplusplus
extern "C" {
#endif
static void Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
{
ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString);
logString.Append(" ");
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
#endif
#if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
{
static const char *const kLevelStrings[] = {
"NONE", "CRIT", "WARN", "NOTE", "INFO", "DEBG",
};
uint16_t index = ((aLogLevel >= 0) && (aLogLevel < static_cast<int>(OT_ARRAY_LENGTH(kLevelStrings))))
? static_cast<uint16_t>(aLogLevel)
: 0;
logString.Append("[%s]", kLevelStrings[index]);
}
#endif
#if OPENTHREAD_CONFIG_LOG_PREPEND_REGION
{
static const char *const kRegionStrings[] = {
"-------", "API----", "MLE----", "ARP----", "N-DATA-", "ICMP---", "IP6----", "TCP----",
"MAC----", "MEM----", "NCP----", "MESH-CP", "DIAG---", "PLAT---", "COAP---", "CLI----",
"CORE---", "UTIL---", "BBR----", "MLR----", "DUA----", "BR-----", "SRP----", "DNS----",
};
uint16_t index = (aLogRegion < OT_ARRAY_LENGTH(kRegionStrings)) ? static_cast<uint16_t>(aLogRegion) : 0;
logString.Append("-%s-: ", kRegionStrings[index]);
}
#elif OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
logString.Append(": ");
#endif
logString.AppendVarArgs(aFormat, aArgs);
otPlatLog(aLogLevel, aLogRegion, "%s" OPENTHREAD_CONFIG_LOG_SUFFIX, logString.AsCString());
#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
exit:
return;
#endif
}
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_CRIT, aRegion, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_WARN, aRegion, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
void _otLogNote(otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_NOTE, aRegion, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_INFO, aRegion, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_DEBG, aRegion, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_MAC
void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...)
{
va_list args;
VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
va_start(args, aFormat);
Log(aLogLevel, OT_LOG_REGION_MAC, aFormat, args);
va_end(args);
exit:
return;
}
void otDumpMacFrame(otLogLevel aLogLevel, const char *aId, const void *aBuf, const size_t aLength)
{
constexpr uint8_t kFixedStringPart = 10; // strlen(" seqno=000")
constexpr uint8_t kSeqnoIdx = 2; // index of the sequence number within aBuf
size_t idLength = strlen(aId) + kFixedStringPart + 1; // allow for the '\0' character
char newId[25];
VerifyOrExit(idLength <= sizeof(newId));
snprintf(newId, idLength, "%s seqno=%03u", aId, static_cast<const uint8_t *>(aBuf)[kSeqnoIdx]);
otDump(aLogLevel, OT_LOG_REGION_MAC, newId, aBuf, aLength);
exit:
return;
}
#endif
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
void otLogCertMeshCoP(const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_MESH_COP, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_OTNS_ENABLE
void otLogOtns(const char *aFormat, ...)
{
va_list args;
va_start(args, aFormat);
Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_CORE, aFormat, args);
va_end(args);
}
#endif
#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
static void otLogDump(otLogLevel aLogLevel, otLogRegion aRegion, const char *aFormat, ...)
{
va_list args;
VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
va_start(args, aFormat);
Log(aLogLevel, aRegion, aFormat, args);
va_end(args);
exit:
return;
}
static constexpr uint8_t kStringLineLength = 80;
static constexpr uint8_t kDumpBytesPerLine = 16;
static void DumpLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const uint8_t *aBytes, const size_t aLength)
{
ot::String<kStringLineLength> string;
string.Append("|");
for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
{
if (i < aLength)
{
string.Append(" %02X", aBytes[i]);
}
else
{
string.Append(" ..");
}
if (!((i + 1) % 8))
{
string.Append(" |");
}
}
string.Append(" ");
for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
{
char c = '.';
if (i < aLength)
{
char byteAsChar = static_cast<char>(0x7f & aBytes[i]);
if (isprint(byteAsChar))
{
c = byteAsChar;
}
}
string.Append("%c", c);
}
otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
}
void otDump(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aId, const void *aBuf, const size_t aLength)
{
constexpr uint8_t kWidth = 72;
constexpr uint8_t kFixedStringPart = 10; // strlen("[ len=000]")
size_t idLen = strlen(aId) + kFixedStringPart;
ot::String<kStringLineLength> string;
VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
for (size_t i = 0; i < (kWidth - idLen) / 2; i++)
{
string.Append("=");
}
string.Append("[%s len=%03u]", aId, static_cast<unsigned>(aLength));
for (size_t i = 0; i < kWidth - idLen - (kWidth - idLen) / 2; i++)
{
string.Append("=");
}
otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
for (size_t i = 0; i < aLength; i += kDumpBytesPerLine)
{
DumpLine(aLogLevel, aLogRegion, static_cast<const uint8_t *>(aBuf) + i,
OT_MIN((aLength - i), static_cast<size_t>(kDumpBytesPerLine)));
}
string.Clear();
for (size_t i = 0; i < kWidth; i++)
{
string.Append("-");
}
otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
exit:
return;
}
#else // OPENTHREAD_CONFIG_LOG_PKT_DUMP
void otDump(otLogLevel, otLogRegion, const char *, const void *, const size_t)
{
}
#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NONE
/* this provides a stub, in case something uses the function */
void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
{
OT_UNUSED_VARIABLE(aLogLevel);
OT_UNUSED_VARIABLE(aLogRegion);
OT_UNUSED_VARIABLE(aFormat);
}
#endif
OT_TOOL_WEAK void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
{
otPlatLog(aLogLevel, aLogRegion, "%s", aLogLine);
}
#ifdef __cplusplus
}
#endif