如何在 websocketpp 中使用自定义记录器?

Posted

技术标签:

【中文标题】如何在 websocketpp 中使用自定义记录器?【英文标题】:How to use custom logger with websocketpp? 【发布时间】:2020-06-23 18:29:17 【问题描述】:

我正在使用 websocketpp 创建遥测服务器,并遵循示例 here。我的应用程序将作为启动时启动的 linux 守护程序运行,因此我将无法将日志写入标准输出。因此,我想使用spdlog 添加一个客户记录器,并了解它可以根据this 页面上的内容来完成。看起来我需要使用websocketpp::log::stub 接口来创建我自己的客户记录器。问题是,关于日志记录的文档非常有限,我不确定从哪里开始以及如何将其合并到上面链接的遥测服务器示例的上下文中。我不确定在定义服务器时如何指定记录器:typedef websocketpp::server<websocketpp::config::asio> server;

如何扩展stub 类,以及如何使用此客户记录器初始化我的服务器?

我能找到的唯一示例代码在此线程here 中,但根据链接的评论,此代码在V 0.3.x+ 之后不再相关。

【问题讨论】:

【参考方案1】:

对于任何想要使用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;

【讨论】:

如何更改“myLogger”类以接受 spdlog 的各种参数(日志名称/ID、接收器...)?我可以将它们作为模板参数提供吗?【参考方案2】:

构建自定义记录器有两个步骤。首先,编写一个具有适当接口的策略类,然后创建一个使用该策略的自定义配置。

编写策略类websocketpp::log::stub 是一个最小的实现,它实际上不做任何事情(它主要用于在单元测试中存根日志),但它演示并记录了日志类需要实现的接口.日志记录类不需要是websocketpp::log::stub 的子类。您可以查看websocketpp/logger/* 文件夹中的其他示例。尤其是 syslog 记录器,作为输出到标准输出以外的其他内容的日志记录策略示例,可能会很有趣。

要设置自定义配置,您将创建一个配置类。它可以是独立的,也可以是其中一个标准的子类,例如websocketpp::config::asio,它只是覆盖了少量的东西。例如,您的配置可能只会覆盖记录器。创建后,您会将配置类传递给端点模板参数,而不是 websocketpp::config::asio

有关您可以通过此配置系统在编译时覆盖的内容的更多详细信息,请参见https://docs.websocketpp.org/reference_8config.html。此页面上有一个示例显示了替换默认记录器的自定义配置(以及其他更改)。

【讨论】:

以上是关于如何在 websocketpp 中使用自定义记录器?的主要内容,如果未能解决你的问题,请参考以下文章

websocketpp最简单的服务器

如何使用 websocket websocketpp 发送和接收消息?

websocketpp 最简单的客户端 一

如何以 websocketpp::connection_hdl 为键实现 boost::unordered_map?

如何使用 jsDoc 在自定义组件中记录此关键字?

如何在 Haskell 中访问没有记录语法的自定义数据类型的字段?