如何将自定义记录器与 websocketpp 一起使用?
How to use custom logger with websocketpp?
我正在使用 websocketpp 创建一个遥测服务器,并遵循 here. My application will be running as a linux daemon which starts on boot, and therefore I won't be able to write logs to standard out. I would therefore like to add a customer logger using spdlog, and understand that it can be done based on what's on this 页面的示例。看来我需要使用 websocketpp::log::stub
界面来创建我自己的客户记录器。问题是,关于日志记录的文档非常有限,我不确定从哪里开始以及如何将其合并到上面链接的遥测服务器示例的上下文中。我不确定在定义服务器时如何指定记录器:typedef websocketpp::server<websocketpp::config::asio> server;
.
如何扩展 stub
class,以及如何使用此客户记录器初始化我的服务器?
我能找到的唯一示例代码在此线程 here 中,但根据链接的评论,此代码在 V 0.3.x+
之后不再相关。
构建自定义记录器有两个步骤。首先,使用适当的接口编写策略 class,然后创建使用该策略的自定义配置。
编写策略 class websocketpp::log::stub
是一个最小的实现,实际上不做任何事情(它主要用于在单元测试中清除日志记录)但它演示并记录了日志 class 需要实现的接口。日志 class 不需要是 websocketpp::log::stub
的子 class。您可以查看 websocketpp/logger/*
文件夹中的其他示例。 syslog 记录器作为输出到标准输出以外的东西的日志记录策略的示例可能特别有趣。
要设置自定义配置,您将创建一个配置 class。它可以是独立的,也可以是标准之一的子class,例如websocketpp::config::asio
,它只会覆盖一小部分内容。例如,您的配置可能只会覆盖记录器。创建后,您会将配置 class 传递到端点模板参数而不是 websocketpp::config::asio
.
有关您可以在编译时通过此配置系统覆盖的内容的更多详细信息,请参见 https://docs.websocketpp.org/reference_8config.html。此页面上有一个示例显示了替换默认记录器(以及其他更改)的自定义配置。
对于任何想要使用 spdlog 作为客户记录器的示例代码的人,这里是:
创建一个新文件 customerLogger.hpp
,内容如下:
#pragma once
#include <websocketpp/logger/basic.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
#include "spdlog/logger.h"
#include "spdlog/sinks/rotating_file_sink.h"
namespace websocketpp {
namespace log {
/// Basic logger that outputs to syslog
template <typename concurrency, typename names>
class myLogger : public basic<concurrency, names> {
public:
typedef basic<concurrency, names> base;
/// Construct the logger
/**
* @param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Construct the logger
/**
* @param channels A set of channels to statically enable
* @param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(level channels, channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(channels, hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Write a string message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, std::string const & msg) {
write(channel, msg.c_str());
}
/// Write a cstring message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(base::m_lock);
if (!this->dynamic_test(channel)) { return; }
if (m_channel_type_hint == channel_type_hint::access) {
m_logger->info(msg);
} else {
if (channel == elevel::devel) {
m_logger->debug(msg);
} else if (channel == elevel::library) {
m_logger->debug(msg);
} else if (channel == elevel::info) {
m_logger->info(msg);
} else if (channel == elevel::warn) {
m_logger->warn(msg);
} else if (channel == elevel::rerror) {
m_logger->error(msg);
} else if (channel == elevel::fatal) {
m_logger->critical(msg);
}
}
}
private:
std::shared_ptr<spdlog::logger> m_logger;
typedef typename base::scoped_lock_type scoped_lock_type;
channel_type_hint::value m_channel_type_hint;
};
} // log
} // websocketpp
接下来,创建另一个文件,customConfig.hpp
,其中包含以下内容:
#pragma once
#include "./customLogger.hpp"
#include <websocketpp/logger/syslog.hpp>
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
// Custom server config based on bundled asio config
struct my_config : public websocketpp::config::asio {
// Replace default stream logger with the custom logger
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::elevel> elog_type;
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::alevel> alog_type;
};
typedef websocketpp::server<my_config> my_server;
最后,当您想要创建服务器时,您只需执行 my_server endpoint;
我正在使用 websocketpp 创建一个遥测服务器,并遵循 here. My application will be running as a linux daemon which starts on boot, and therefore I won't be able to write logs to standard out. I would therefore like to add a customer logger using spdlog, and understand that it can be done based on what's on this 页面的示例。看来我需要使用 websocketpp::log::stub
界面来创建我自己的客户记录器。问题是,关于日志记录的文档非常有限,我不确定从哪里开始以及如何将其合并到上面链接的遥测服务器示例的上下文中。我不确定在定义服务器时如何指定记录器:typedef websocketpp::server<websocketpp::config::asio> server;
.
如何扩展 stub
class,以及如何使用此客户记录器初始化我的服务器?
我能找到的唯一示例代码在此线程 here 中,但根据链接的评论,此代码在 V 0.3.x+
之后不再相关。
构建自定义记录器有两个步骤。首先,使用适当的接口编写策略 class,然后创建使用该策略的自定义配置。
编写策略 class websocketpp::log::stub
是一个最小的实现,实际上不做任何事情(它主要用于在单元测试中清除日志记录)但它演示并记录了日志 class 需要实现的接口。日志 class 不需要是 websocketpp::log::stub
的子 class。您可以查看 websocketpp/logger/*
文件夹中的其他示例。 syslog 记录器作为输出到标准输出以外的东西的日志记录策略的示例可能特别有趣。
要设置自定义配置,您将创建一个配置 class。它可以是独立的,也可以是标准之一的子class,例如websocketpp::config::asio
,它只会覆盖一小部分内容。例如,您的配置可能只会覆盖记录器。创建后,您会将配置 class 传递到端点模板参数而不是 websocketpp::config::asio
.
有关您可以在编译时通过此配置系统覆盖的内容的更多详细信息,请参见 https://docs.websocketpp.org/reference_8config.html。此页面上有一个示例显示了替换默认记录器(以及其他更改)的自定义配置。
对于任何想要使用 spdlog 作为客户记录器的示例代码的人,这里是:
创建一个新文件 customerLogger.hpp
,内容如下:
#pragma once
#include <websocketpp/logger/basic.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
#include "spdlog/logger.h"
#include "spdlog/sinks/rotating_file_sink.h"
namespace websocketpp {
namespace log {
/// Basic logger that outputs to syslog
template <typename concurrency, typename names>
class myLogger : public basic<concurrency, names> {
public:
typedef basic<concurrency, names> base;
/// Construct the logger
/**
* @param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Construct the logger
/**
* @param channels A set of channels to statically enable
* @param hint A channel type specific hint for how to construct the logger
*/
myLogger<concurrency,names>(level channels, channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(channels, hint), m_channel_type_hint(hint) {
auto max_size = 1048576 * 5;
auto max_files = 3;
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt> ("/var/logs/my_logger.log", max_size, max_files);
m_logger = std::make_shared<spdlog::logger>("my_logger", rotating_sink);
m_logger->flush_on(spdlog::level::info);
m_logger->set_level(spdlog::level::level_enum::info);
}
/// Write a string message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, std::string const & msg) {
write(channel, msg.c_str());
}
/// Write a cstring message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(base::m_lock);
if (!this->dynamic_test(channel)) { return; }
if (m_channel_type_hint == channel_type_hint::access) {
m_logger->info(msg);
} else {
if (channel == elevel::devel) {
m_logger->debug(msg);
} else if (channel == elevel::library) {
m_logger->debug(msg);
} else if (channel == elevel::info) {
m_logger->info(msg);
} else if (channel == elevel::warn) {
m_logger->warn(msg);
} else if (channel == elevel::rerror) {
m_logger->error(msg);
} else if (channel == elevel::fatal) {
m_logger->critical(msg);
}
}
}
private:
std::shared_ptr<spdlog::logger> m_logger;
typedef typename base::scoped_lock_type scoped_lock_type;
channel_type_hint::value m_channel_type_hint;
};
} // log
} // websocketpp
接下来,创建另一个文件,customConfig.hpp
,其中包含以下内容:
#pragma once
#include "./customLogger.hpp"
#include <websocketpp/logger/syslog.hpp>
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
// Custom server config based on bundled asio config
struct my_config : public websocketpp::config::asio {
// Replace default stream logger with the custom logger
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::elevel> elog_type;
typedef websocketpp::log::myLogger<concurrency_type, websocketpp::log::alevel> alog_type;
};
typedef websocketpp::server<my_config> my_server;
最后,当您想要创建服务器时,您只需执行 my_server endpoint;