[logging] add optional `otPlatLogLine()` & use it in NCP/CLI (#5704)

This commit adds a new platform function `otPlatLogLine()`. This
function is optional and if not implemented by platform layer, a
default weak implementation is provided and used by the OpenThread
core using `otPlatLog()`.The new function is used by OpenThread core
when the feature `OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY` is not
enabled (which is the default behavior). In this case, the OT core
itself will prepare a full log line.

This commit also adds implementations of the new platform function for
the NCP and CLI modules.
diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c
index 184cbd7..376e04d 100644
--- a/examples/apps/cli/main.c
+++ b/examples/apps/cli/main.c
@@ -133,13 +133,15 @@
 #if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP
 void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
 {
-    OT_UNUSED_VARIABLE(aLogLevel);
-    OT_UNUSED_VARIABLE(aLogRegion);
-    OT_UNUSED_VARIABLE(aFormat);
-
     va_list ap;
     va_start(ap, aFormat);
     otCliPlatLogv(aLogLevel, aLogRegion, aFormat, ap);
     va_end(ap);
 }
+
+void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
+{
+    otCliPlatLogLine(aLogLevel, aLogRegion, aLogLine);
+}
+
 #endif
diff --git a/include/openthread/cli.h b/include/openthread/cli.h
index 8b64042..5a8f54c 100644
--- a/include/openthread/cli.h
+++ b/include/openthread/cli.h
@@ -165,6 +165,16 @@
 void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs);
 
 /**
+ * Function to write the OpenThread Log to the CLI console.
+ *
+ * @param[in]  aLogLevel   The log level.
+ * @param[in]  aLogRegion  The log region.
+ * @param[in]  aLogLine    A pointer to the log line string.
+ *
+ */
+void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 63b7ed9..6a69a63 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@
  * @note This number versions both OpenThread platform and user APIs.
  *
  */
-#define OPENTHREAD_API_VERSION (39)
+#define OPENTHREAD_API_VERSION (40)
 
 /**
  * @addtogroup api-instance
diff --git a/include/openthread/platform/logging.h b/include/openthread/platform/logging.h
index 3744825..2867660 100644
--- a/include/openthread/platform/logging.h
+++ b/include/openthread/platform/logging.h
@@ -151,6 +151,22 @@
 void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...);
 
 /**
+ * This (optional) platform function outputs a prepared log line.
+ *
+ * This platform function is used by OpenThread core when `OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY` is not enabled
+ * (in this case, the OT core itself will prepare a full log line).
+ *
+ * Note that this function is optional and if not provided by platform layer, a default (weak) implementation is
+ * provided and used by OpenThread core as `otPlatLog(aLogLevel, aLogResion, "%s", aLogLine)`.
+ *
+ * @param[in]  aLogLevel   The log level.
+ * @param[in]  aLogRegion  The log region.
+ * @param[in]  aLogLine    A pointer to a log line string.
+ *
+ */
+void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
+
+/**
  * @}
  *
  */
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 13e7269..184395c 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -4641,6 +4641,18 @@
     return;
 }
 
+extern "C" void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
+{
+    OT_UNUSED_VARIABLE(aLogLevel);
+    OT_UNUSED_VARIABLE(aLogRegion);
+
+    VerifyOrExit(Interpreter::IsInitialized());
+    Interpreter::GetInterpreter().OutputLine(aLogLine);
+
+exit:
+    return;
+}
+
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/core/common/logging.cpp b/src/core/common/logging.cpp
index 7dca102..a582726 100644
--- a/src/core/common/logging.cpp
+++ b/src/core/common/logging.cpp
@@ -390,6 +390,11 @@
 }
 #endif
 
+OT_TOOL_WEAK void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
+{
+    otPlatLog(aLogLevel, aLogRegion, "%s", aLogLine);
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp
index 83a8724..152c6fe 100644
--- a/src/ncp/ncp_base.cpp
+++ b/src/ncp/ncp_base.cpp
@@ -2469,4 +2469,14 @@
     va_end(args);
 }
 
+extern "C" void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
+{
+    ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
+
+    if (ncp != nullptr)
+    {
+        ncp->Log(aLogLevel, aLogRegion, aLogLine);
+    }
+}
+
 #endif // (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_APP)