[cli] fix crash when full logs is on (#5408)

It is possible that log is printed before the Interpreter (UART or
CONSOLE) object is created when FULL LOGS is enabled. In such case, OT
crashes. This commit fixes this bug.
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 1f66809..3613659 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -251,6 +251,8 @@
     {"version", &Interpreter::ProcessVersion},
 };
 
+Interpreter *Interpreter::sInterpreter = nullptr;
+
 Interpreter::Interpreter(Instance *aInstance)
     : mUserCommands(nullptr)
     , mUserCommandsLength(0)
@@ -4679,8 +4681,12 @@
     OT_UNUSED_VARIABLE(aLogLevel);
     OT_UNUSED_VARIABLE(aLogRegion);
 
+    VerifyOrExit(Interpreter::IsInitialized(), OT_NOOP);
+
     Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs);
     Interpreter::GetInterpreter().OutputFormat("\r\n");
+exit:
+    return;
 }
 
 } // namespace Cli
diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp
index 51ae3f5..7b4d9e1 100644
--- a/src/cli/cli.hpp
+++ b/src/cli/cli.hpp
@@ -115,7 +115,20 @@
      * @returns A reference to the interpreter object.
      *
      */
-    static Interpreter &GetInterpreter(void);
+    static Interpreter &GetInterpreter(void)
+    {
+        OT_ASSERT(sInterpreter != nullptr);
+
+        return *sInterpreter;
+    }
+
+    /**
+     * This method returns whether the interpreter is initialized.
+     *
+     * @returns  Whether the interpreter is initialized.
+     *
+     */
+    static bool IsInitialized(void) { return sInterpreter != nullptr; }
 
     /**
      * This method interprets a CLI command.
@@ -236,6 +249,9 @@
      */
     void SetUserCommands(const otCliCommand *aCommands, uint8_t aLength);
 
+protected:
+    static Interpreter *sInterpreter;
+
 private:
     enum
     {
diff --git a/src/cli/cli_console.cpp b/src/cli/cli_console.cpp
index b2f5647..55ef6bf 100644
--- a/src/cli/cli_console.cpp
+++ b/src/cli/cli_console.cpp
@@ -69,18 +69,11 @@
 {
 }
 
-Console *Console::sConsole = nullptr;
-
-Interpreter &Interpreter::GetInterpreter(void)
-{
-    return *Console::sConsole;
-}
-
 void Console::Initialize(otInstance *aInstance, otCliConsoleOutputCallback aCallback, void *aContext)
 {
     Instance *instance = static_cast<Instance *>(aInstance);
 
-    sConsole = new (&sCliConsoleRaw) Console(instance, aCallback, aContext);
+    Interpreter::sInterpreter = new (&sCliConsoleRaw) Console(instance, aCallback, aContext);
 }
 
 Console::Console(Instance *aInstance, otCliConsoleOutputCallback aCallback, void *aContext)
diff --git a/src/cli/cli_uart.cpp b/src/cli/cli_uart.cpp
index d9dd2bb..b07f32f 100644
--- a/src/cli/cli_uart.cpp
+++ b/src/cli/cli_uart.cpp
@@ -105,17 +105,10 @@
 
 static OT_DEFINE_ALIGNED_VAR(sCliUartRaw, sizeof(Uart), uint64_t);
 
-Uart *Uart::sUart = nullptr;
-
-Interpreter &Interpreter::GetInterpreter(void)
-{
-    return *Uart::sUart;
-}
-
 void Uart::Initialize(otInstance *aInstance)
 {
-    Instance *instance = static_cast<Instance *>(aInstance);
-    sUart              = new (&sCliUartRaw) Uart(instance);
+    Instance *instance        = static_cast<Instance *>(aInstance);
+    Interpreter::sInterpreter = new (&sCliUartRaw) Uart(instance);
 }
 
 Uart::Uart(Instance *aInstance)