File indexing completed on 2025-09-17 08:38:22
0001
0002
0003
0004
0005
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
0033
0034 enum class log_level : uint8_t {
0035
0036 error = 1,
0037
0038
0039 warning,
0040
0041
0042 info,
0043
0044
0045 debug
0046 };
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061 class logger {
0062 constexpr static auto prefix = "[Boost.MQTT5]";
0063
0064 log_level _level;
0065 public:
0066
0067
0068
0069
0070
0071
0072 logger(log_level level = log_level::info) : _level(level) {}
0073
0074
0075
0076
0077
0078
0079
0080
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
0109
0110
0111
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
0127
0128
0129
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
0145
0146
0147
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
0163
0164
0165
0166
0167
0168
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
0188
0189
0190
0191
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 {
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
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 }
0256
0257
0258 #endif