背景
- 本文的源码来自于Amazon的AVS C++ SDK,AVS C++ SDK 源码链接
- 本文具体分析文件的路径:
文件名称 | 文件路径 |
---|---|
ConsoleLogger.h | avs-device-sdk/AVSCommon/Utils/include/AVSCommon/Utils/Logger |
ConsoleLogger.c | AVSCommon/Utils/src/Logger |
- 本文只是针对单例这一设计模式在这一文件中的应用,对于其它内容的代码会在Logger完整分析的文章中进行详细说明
代码分析
ConsoleLogger 类声明
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file 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.
*/
#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LOGGER_CONSOLELOGGER_H_
#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LOGGER_CONSOLELOGGER_H_
#include "AVSCommon/Utils/Logger/Logger.h"
#include "AVSCommon/Utils/Logger/LoggerUtils.h"
#include "AVSCommon/Utils/Logger/LogStringFormatter.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace utils {
namespace logger {
/**
* A very simple (e.g. not asynchronous) @c Logger that logs to console.
*
* Inheriting @c std::ios_base::Init ensures that the standard iostreams objects are properly initialized before @c
* ConsoleLogger uses them.
*/
class ConsoleLogger
: public Logger
, private std::ios_base::Init {
public:
/**
* Return the one and only @c ConsoleLogger instance.
*
* @return The one and only @c ConsoleLogger instance.
*/
static std::shared_ptr<Logger> instance();
void emit(Level level, std::chrono::system_clock::time_point time, const char* threadMoniker, const char* text)
override;
private:
/**
* Constructor.
*/
ConsoleLogger();
/// Mutex to serialize writing to cout.
std::shared_ptr<std::mutex> m_coutMutex;
/// Object to format log strings correctly.
LogStringFormatter m_logFormatter;
};
/**
* Return the singleton instance of @c ConsoleLogger.
*
* @return The singleton instance of @c ConsoleLogger.
*/
std::shared_ptr<Logger> getConsoleLogger();
} // namespace logger
} // namespace utils
} // namespace avsCommon
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LOGGER_CONSOLELOGGER_H_
在上面示例代码中,可以看到ConsleLogger的构造函数的声明的访问属性为private,也就是不能通过ConsoleLogger进行对象的实例化.
另外在public区域声明了: static std::shared_ptr instance(); 用于获取ConsoleLogger的实例.
最后声明了一个全局的函数std::shared_ptr getConsoleLogger(); 进行该实例对象的获取.
ConsoleLogger 类方法的实现
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file 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.
*/
#include <cstdio>
#include <ctime>
#include <iostream>
#include <mutex>
#include "AVSCommon/Utils/CoutMutex.h"
#include "AVSCommon/Utils/Logger/ConsoleLogger.h"
#include "AVSCommon/Utils/Logger/LoggerUtils.h"
#include "AVSCommon/Utils/Logger/ThreadMoniker.h"
namespace alexaClientSDK {
namespace avsCommon {
namespace utils {
namespace logger {
/// Configuration key for DefaultLogger settings
static const std::string CONFIG_KEY_DEFAULT_LOGGER = "consoleLogger";
std::shared_ptr<Logger> ConsoleLogger::instance() {
static std::shared_ptr<Logger> singleConsoleLogger = std::shared_ptr<ConsoleLogger>(new ConsoleLogger);
return singleConsoleLogger;
}
void ConsoleLogger::emit(
Level level,
std::chrono::system_clock::time_point time,
const char* threadMoniker,
const char* text) {
if (m_coutMutex) {
std::lock_guard<std::mutex> lock(*m_coutMutex);
std::cout << m_logFormatter.format(level, time, threadMoniker, text) << std::endl;
}
}
ConsoleLogger::ConsoleLogger() : Logger(Level::UNKNOWN), m_coutMutex{getCoutMutex()} {
#ifdef DEBUG
setLevel(Level::DEBUG9);
#else
setLevel(Level::INFO);
#endif // DEBUG
init(configuration::ConfigurationNode::getRoot()[CONFIG_KEY_DEFAULT_LOGGER]);
}
std::shared_ptr<Logger> getConsoleLogger() {
return ConsoleLogger::instance();
}
} // namespace logger
} // namespace utils
} // namespace avsCommon
} // namespace alexaClientSDK
以上代码,在ConsoleLogger::instance() 方法new了一个static的ConsoleLogger对象指针,然后返回该对象指针.
而在 getConsoleLogger的定义中直接调用了ConsoleLogger::instance();进行实例对象的获取.
看到这里你可能会有如下疑问?
- ConsoleLogger::instance为什么可以直接调用?不用实例化创建对象后进行访问吗?
- 在重复调用ConsoleLogger::instance()情况下,ConsoleLogger会被多次实例化?
解答以上2点的疑问,主要是需要解释下在C++中,由static修饰类方法 和 static修饰类进行对象的实例化.
- static修饰类方法
当某一类的方法由static进行修饰时,该方法的所有权为该类也就是说该方法可以直接通过类进行访问而不是一定需要通过类创建的实例化对象进行访问. - static修饰类进行对象的实例化
static std::shared_ptr<Logger> singleConsoleLogger = std::shared_ptr<ConsoleLogger>(new ConsoleLogger);
在C或者C++,由static 修饰进行函数作用域内变量的定义时,该变量只在第一次调用该函数时进行变量的初始化,在这里类的实例化也是一样,而且该实例化对象的生命周期是直到程序结束.
所以在这里singleConsoleLogger 并不会被反复创建.
总结
在不需要让外部进行类的实例化且该类只能有唯一一个对象时,我们就可以采用单例这一设计模式.实质上是对static这一修饰符特性的应用.
另外本文中涉及到C++11相关特性的语法,会在后续的文章中持续补充.