Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-17 08:38:22

0001 //
0002 // Copyright (c) 2023-2025 Ivica Siladic, Bruno Iljazovic, Korina Simicevic
0003 //
0004 // Distributed under the Boost Software License, Version 1.0.
0005 // (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MQTT5_LOGGER_HPP
0009 #define BOOST_MQTT5_LOGGER_HPP
0010 
0011 #include <boost/mqtt5/logger_traits.hpp>
0012 #include <boost/mqtt5/property_types.hpp>
0013 #include <boost/mqtt5/reason_codes.hpp>
0014 #include <boost/mqtt5/types.hpp>
0015 
0016 #include <boost/mqtt5/detail/traits.hpp>
0017 
0018 #include <boost/asio/ip/tcp.hpp>
0019 #include <boost/system/error_code.hpp>
0020 #include <boost/type_traits/remove_cv_ref.hpp>
0021 
0022 #include <cstdint>
0023 #include <iostream>
0024 #include <string_view>
0025 
0026 namespace boost::mqtt5 {
0027 
0028 namespace asio = boost::asio;
0029 using error_code = boost::system::error_code;
0030 
0031 /**
0032  * \brief Represents the severity level of log messages.
0033  */
0034 enum class log_level : uint8_t {
0035     /** Error messages that indicate serious issues. **/
0036     error = 1,
0037 
0038     /** Warnings that indicate potential problems or non-critical issues. **/
0039     warning,
0040 
0041     /** Informational messages that highlight normal application behaviour and events. **/
0042     info,
0043 
0044     /** Detailed messages useful for diagnosing issues. **/
0045     debug
0046 };
0047 
0048 /**
0049  * \brief A logger class that can used by the \ref mqtt_client to output
0050  * the results of operations to stderr.
0051  *
0052  * \details All functions are invoked directly within the \ref mqtt_client using
0053  * its default executor. If the \ref mqtt_client is initialized with an explicit or
0054  * implicit strand, none of the functions will be invoked concurrently.
0055  * 
0056  * \par Thread safety
0057  * Distinct objects: usafe. \n
0058  * Shared objects: unsafe. \n
0059  * This class is <b>not thread-safe</b>.
0060 */
0061 class logger {
0062     constexpr static auto prefix = "[Boost.MQTT5]";
0063 
0064     log_level _level;
0065 public:
0066 
0067     /**
0068      * \brief Constructs a logger that filters log messages based on the specified log level.
0069      *
0070      * \param level Messages with a log level higher than the given log level will be suppressed.
0071      */
0072     logger(log_level level = log_level::info) : _level(level) {}
0073 
0074     /**
0075      * \brief Outputs the results of the resolve operation.
0076      *
0077      * \param ec Error code returned by the resolve operation.
0078      * \param host Hostname used in the resolve operation.
0079      * \param port Port used in the resolve operation.
0080      * \param eps Endpoints returned by the resolve operation.
0081      */
0082     void at_resolve(
0083         error_code ec, std::string_view host, std::string_view port,
0084         const asio::ip::tcp::resolver::results_type& eps
0085     ) {
0086         if (!ec && _level < log_level::info)
0087             return;
0088 
0089         output_prefix();
0090         std::clog
0091             << "resolve: "
0092             << host << ":" << port;
0093         std::clog << " - " << ec.message() << ".";
0094 
0095         if (_level == log_level::debug) {
0096             std::clog << " [";
0097             for (auto it = eps.begin(); it != eps.end();) {
0098                 std::clog << it->endpoint().address().to_string();
0099                 if (++it != eps.end())
0100                     std::clog << ",";
0101             }
0102             std::clog << "]";
0103         }
0104         std::clog << std::endl;
0105     }
0106 
0107     /**
0108      * \brief Outputs the results of the TCP connect operation.
0109      *
0110      * \param ec Error code returned by the TCP connect operation.
0111      * \param ep The TCP endpoint used to establish the TCP connection.
0112      */
0113     void at_tcp_connect(error_code ec, asio::ip::tcp::endpoint ep) {
0114         if (!ec && _level < log_level::info)
0115             return;
0116 
0117         output_prefix();
0118         std::clog
0119             << "TCP connect: "
0120             << ep.address().to_string() << ":" << ep.port()
0121             << " - " << ec.message() << "."
0122         << std::endl;
0123     }
0124 
0125     /**
0126      * \brief Outputs the results of the TLS handshake operation.
0127      *
0128      * \param ec Error code returned by the TLS handshake operation.
0129      * \param ep The TCP endpoint used to establish the TLS handshake.
0130      */
0131     void at_tls_handshake(error_code ec, asio::ip::tcp::endpoint ep) {
0132         if (!ec && _level < log_level::info)
0133             return;
0134 
0135         output_prefix();
0136         std::clog
0137             << "TLS handshake: "
0138             << ep.address().to_string() << ":" << ep.port()
0139             << " - " << ec.message() << "."
0140         << std::endl;
0141     }
0142 
0143     /**
0144      * \brief Outputs the results of the WebSocket handshake operation.
0145      *
0146      * \param ec Error code returned by the WebSocket handshake operation.
0147      * \param ep The TCP endpoint used to establish the WebSocket handshake.
0148      */
0149     void at_ws_handshake(error_code ec, asio::ip::tcp::endpoint ep) {
0150         if (!ec && _level < log_level::info)
0151             return;
0152 
0153         output_prefix();
0154         std::clog
0155             << "WebSocket handshake: "
0156             << ep.address().to_string() << ":" << ep.port()
0157             << " - " << ec.message() << "."
0158         << std::endl;
0159     }
0160 
0161     /** 
0162      * \brief Outputs the contents of the \__CONNACK\__ packet sent by the Broker.
0163      * 
0164      * \param rc Reason Code in the received \__CONNACK\__ packet indicating
0165      * the result of the MQTT handshake.
0166      * \param session_present A flag indicating whether the Broker already has a session associated
0167      * with this connection.
0168      * \param ca_props \__CONNACK_PROPS\__ received in the \__CONNACK\__ packet.
0169      */
0170     void at_connack(
0171         reason_code rc,
0172         bool session_present, const connack_props& ca_props
0173     ) {
0174         if (!rc && _level < log_level::info)
0175             return;
0176 
0177         output_prefix();
0178         std::clog << "connack: " << rc.message() << ".";
0179         if (_level == log_level::debug) {
0180             std::clog << " session_present:" << session_present << " ";
0181             output_props(ca_props);
0182         }
0183         std::clog << std::endl;
0184     }
0185 
0186     /**
0187      * \brief Outputs the contents of the \__DISCONNECT\__ packet sent by the Broker.
0188      *
0189      * \param rc Reason Code in the received \__DISCONNECT\__ packet indicating
0190      * the reason behind the disconnection.
0191      * \param dc_props \__DISCONNECT_PROPS\__ received in the \__DISCONNECT\__ packet.
0192      */
0193     void at_disconnect(reason_code rc, const disconnect_props& dc_props) {
0194         output_prefix();
0195         std::clog << "disconnect: " << rc.message() << ".";
0196         if (_level == log_level::debug)
0197             output_props(dc_props);
0198         std::clog << std::endl;
0199     }
0200 
0201 private:
0202     void output_prefix() {
0203         std::clog << prefix << " ";
0204     }
0205 
0206     template <typename Props>
0207     void output_props(const Props& props) {
0208         props.visit(
0209             [](const auto& prop, const auto& val) -> bool {
0210                 if constexpr (detail::is_optional<decltype(val)>) {
0211                     if (val.has_value()) {
0212                         std::clog << property_name(prop) << ":";
0213                         using value_type = boost::remove_cv_ref_t<decltype(*val)>;
0214                         if constexpr (std::is_same_v<value_type, uint8_t>)
0215                             std::clog << std::to_string(*val) << " ";
0216                         else
0217                             std::clog << *val << " ";
0218                     }
0219                 } else { // is vector
0220                     if (val.empty())
0221                         return true;
0222 
0223                     std::clog << property_name(prop) << ":";
0224                     std::clog << "[";
0225                     for (size_t i = 0; i < val.size(); i++) {
0226                         if constexpr (detail::is_pair<decltype(val[i])>)
0227                             std::clog << "(" << val[i].first << "," << val[i].second << ")";
0228                         else
0229                             std::clog << std::to_string(val[i]);
0230                         if (i + 1 < val.size())
0231                             std::clog << ", ";
0232                     }
0233                     std::clog << "] ";
0234                 }
0235                 return true;
0236             }
0237         );
0238     }
0239 
0240     template <prop::property_type p>
0241     static std::string_view property_name(std::integral_constant<prop::property_type, p>) {
0242         return prop::name_v<p>;
0243     }
0244 
0245 };
0246 
0247 // Verify that the logger class satisfies the LoggerType concept
0248 static_assert(has_at_resolve<logger>);
0249 static_assert(has_at_tcp_connect<logger>);
0250 static_assert(has_at_tls_handshake<logger>);
0251 static_assert(has_at_ws_handshake<logger>);
0252 static_assert(has_at_connack<logger>);
0253 static_assert(has_at_disconnect<logger>);
0254 
0255 } // end namespace boost::mqtt5
0256 
0257 
0258 #endif // !BOOST_MQTT5_LOGGER_HPP