websocketpp icon indicating copy to clipboard operation
websocketpp copied to clipboard

Set up custom logger, but some log entries still logged with the default logger

Open fferri opened this issue 3 years ago • 5 comments

I've setup a custom logger to use my application's logging facility. Here's what I did, omitting the actual API call to use the application's logger:

namespace websocketpp
{
    namespace log
    {
        template<typename concurrency, typename names>
        class my_logger : public basic<concurrency, names>
        {
        public:
            typedef basic<concurrency, names> base;

            my_logger<concurrency, names>(channel_type_hint::value hint = channel_type_hint::access)
                    : basic<concurrency, names>(hint), m_channel_type_hint(hint)
            {
            }

            my_logger<concurrency, names>(level channels, channel_type_hint::value hint = channel_type_hint::access)
                    : basic<concurrency, names>(channels, hint), m_channel_type_hint(hint)
            {
            }

            void write(level channel, std::string const &msg)
            {
                write(channel, msg.c_str());
            }

            void write(level channel, char const *msg)
            {
                scoped_lock_type lock(base::m_lock);
                if(!this->dynamic_test(channel)) return;
                // custom logging, e.g.: std::cout with specific format:
                std::cout << "[MY_LOGGER]" << ...
            }

        private:
            typedef typename base::scoped_lock_type scoped_lock_type;
            channel_type_hint::value m_channel_type_hint;
        };
    } // log
} // websocketpp

struct my_config : public websocketpp::config::asio
{
    typedef websocketpp::log::my_logger<concurrency_type, websocketpp::log::elevel> elog_type;
    typedef websocketpp::log::my_logger<concurrency_type, websocketpp::log::alevel> alog_type;
};

typedef websocketpp::server<my_config> my_server;
typedef my_server::connection_type my_connection;

// etc...

In the console I see my custom logger is used, but sometimes the default logger is used:

[2022-01-14 15:24:23] [info] asio listen error: asio.system:48 (Address already in use) [MY_LOGGER] handle_read_frame error: websocketpp.transport:7 (End of File) [MY_LOGGER] handle_read_frame error: asio.system:54 (Connection reset by peer) [2022-01-14 16:14:12] [info] asio async_write error: asio.system:32 (Broken pipe) [MY_LOGGER] handle_write_frame error: websocketpp.transport:2 (Underlying Transport Error) [MY_LOGGER] handle_read_frame error: websocketpp.transport:7 (End of File) [MY_LOGGER] handle_read_frame error: asio.system:54 (Connection reset by peer) [2022-01-14 16:14:17] [info] asio async_write error: asio.system:32 (Broken pipe) [MY_LOGGER] handle_write_frame error: websocketpp.transport:2 (Underlying Transport Error) [MY_LOGGER] handle_read_frame error: websocketpp.transport:7 (End of File) [MY_LOGGER] handle_read_frame error: asio.system:54 (Connection reset by peer) [2022-01-14 16:14:20] [info] asio async_write error: asio.system:32 (Broken pipe) ...

What am I missing to log the remaining entries with my custom logger as well?

fferri avatar Jan 14 '22 17:01 fferri

Did you configure both the access (alog) and the error (elog) interface?

Something like:

std::ostream os(&myLogger);
connectionEndpoint.get_alog().set_ostream(&os);
connectionEndpoint.get_elog().set_ostream(&os);

BTW, I haven't got it to work yet with my custom logger class (derived from std::streambuf), I have found no complete working examples.

barsnick avatar Feb 22 '22 15:02 barsnick

I don't call .get_alog() nor .get_elog().

The types alog_type and elog_type are overridden in struct my_config, then I use the type websocketpp::server<my_config> to construct the server.

Since most of the log entries go through my logger, I think this approach should work as well.

By the way, I use a custom logger, and std::cout << ... is there just as an example, I don't use any otsream-like interface.

fferri avatar Feb 22 '22 17:02 fferri

The types alog_type and elog_type are overridden in struct my_config, then I use the type websocketpp::server<my_config> to construct the server.

Yeah, I missed that. Sorry for being of no help.

barsnick avatar Feb 23 '22 11:02 barsnick

What is going on here is that you are seeing logs printed both by WebSocket++ core (the ones that are using your custom logger) and also messages printed by the websocketpp::transport::asio transport policy (the ones that are still using the default logger).

The underlying reason is that transport policies are independent replacable policies that are not coupled to the core library. The transport policy config doesn't know about or have access to my_config. As such, if you want the transport policy to also use a custom logger you'll need to override that explicitly.

Example:

// same custom config as you specified earlier
struct my_config : public websocketpp::config::asio
{
    typedef websocketpp::log::my_logger<concurrency_type, websocketpp::log::elevel> elog_type;
    typedef websocketpp::log::my_logger<concurrency_type, websocketpp::log::alevel> alog_type;
    
    // create a custom transport config based on the base asio transport config
    struct my_transport_config : public websocketpp::config::asio::transport_config {
        // just override the logger types
        typedef my_config::alog_type alog_type;
        typedef my_config::elog_type elog_type;
    };
    
    // let `my_config` know to create transport endpoints with `my_transport_config`
    typedef websocketpp::transport::asio::endpoint<my_transport_config>
        transport_type;
};

zaphoyd avatar Feb 23 '22 16:02 zaphoyd

Thanks @zaphoyd that solves my issue.

fferri avatar Feb 25 '22 16:02 fferri