Skip to content

Commit

Permalink
Added custom logger example.
Browse files Browse the repository at this point in the history
This example disables Boost.Log and uses simple std::log.

Updated document.
  • Loading branch information
redboltz committed Oct 26, 2024
1 parent 033adbd commit 712c859
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
= History

== 9.1.0
* Added custom logger example. #367
* Refined documents. #364, #365
* Made colored log selectable (by default true(colored)). #363
* Fixed misuse of bound allocator. #362
Expand Down
1 change: 1 addition & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@
** xref:tool/broker.adoc[]
** xref:tool/client_cli.adoc[]
** xref:tool/bench.adoc[]
* link:http://github.com/redboltz/async_mqtt[Repository]
112 changes: 104 additions & 8 deletions doc/modules/ROOT/pages/functionality/logging.adoc
Original file line number Diff line number Diff line change
@@ -1,17 +1,113 @@
= logging
= Logging

async_mqtt supports Boost.Log style logging. The severity levels are `fatal`, `error`, `warning`, `info`, `debug`, and `trace`.
== Using Boost.Log
`async_mqtt` provides logging support with Boost.Log. Supported severity levels include `fatal`, `error`, `warning`, `info`, `debug`, and `trace`.

The easiest way to setup logging is
To quickly set up logging, you can use:

```cpp
async_mqtt::setup_log(
async_mqtt::severity_level::info, // you can use any other severity_level
true // if true or omit the argument
// log is colored, otherwise non colored
async_mqtt::severity_level::info, // Choose any severity level
true // Pass `true` or omit to enable colored output
);
```

You can setup your custom settings.
Custom configurations are also supported.

See https://github.com/redboltz/async_mqtt/blob/main/include/async_mqtt/util/setup_log.hpp[setup_log.hpp]
See `setup_log.hpp` for details: https://github.com/redboltz/async_mqtt/blob/main/include/async_mqtt/util/setup_log.hpp[setup_log.hpp]

NOTE: Boost.Log-based logging is enabled when `ASYNC_MQTT_USE_LOG` is defined. For more information, see xref:config.adoc[]

=== Compile time severity filtering

`setup_log()` is for runtime severity level setting.

`ASYNC_MQTT_LOG_SEV` is for compile-time severity level setting.
If you define as follows:

```cpp
#define ASYNC_MQTT_LOG_SEV info
```

then, `fatal`, `error`, `warning`, and `info` logs are compiled. `debug` and `trace` logs are erased from the code.
It is useful for minimal code generation for logging.

== Using a Custom Logger
Boost.Log offers extensive functionality, including logging to the console, syslog, and files. However, some users may prefer a simpler logging mechanism.

All you need to do is defining two macros.
One is `ASYNC_MQTT_ADD_VALUE`, the other is `ASYNC_MQTT_LOG`.

To create and use a custom logger, follow these steps:

All steps should be completed before including any `async_mqtt` headers.

Include the minimal header to implement your custom logger:

```cpp
#include <async_mqtt/util/log_severity.hpp>
```

[Optional] Remove the `ASYNC_MQTT_USE_LOG` definition:

```cpp
#undef ASYNC_MQTT_USE_LOG
```

NOTE: Even if `ASYNC_MQTT_USE_LOG` is defined, your custom logger works well. But removing Boost.Log is often the one of the dominant motivation to introduce custom loggers.

Define your custom logger class:

```cpp
// Define logger class
struct custom_log {
explicit constexpr custom_log(
std::string chan, // chan indicates the log channel or part
async_mqtt::severity_level sev // sev represents the severity level
)
{
// Setup filter
if (sev < async_mqtt::severity_level::info) {
print = false;
return;
}
// Output header
std::clog << "[" << sev << "]" << "(" << chan << ") ";
}
~custom_log() {
// Output trailer
if (print) std::clog << std::endl;
}

bool print = true;
};

// Define output stream operator
template <typename T>
inline constexpr custom_log const& operator<<(
custom_log const& o,
T const& t
) {
// Output message body with filter
if (o.print) std::clog << t;
return o;
}
```

NOTE: This example demonstrates how to locate logging elements in the output. e.g.) channel, severity, tag, ... , so different type of brace is intentionally used. (`()<>{}`)


Apply the custom logger to the `ASYNC_MQTT_LOG()` macro. The `ASYNC_MQTT_LOG()` macro is used throughout the `async_mqtt` library.

```cpp
// Output additional value. Stringized name(tag) and tagged value.
#define ASYNC_MQTT_ADD_VALUE(name, val) "<" << #name ">{" << val << "} "
```

```cpp
// Set ASYNC_MQTT_LOG macro to custom_log.
#define ASYNC_MQTT_LOG(chan, sev) custom_log(chan, async_mqtt::severity_level::sev)
```

=== Example
For a full example, see link:../example/custom_logger.cpp[custom_logger.cpp].
3 changes: 2 additions & 1 deletion doc/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= async_mqtt

Takatoshi Kondo
http://github.com/redboltz/async_mqtt

Copyright © 2024 Takatoshi Kondo

Expand Down Expand Up @@ -48,3 +48,4 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying fil
** xref:tool/broker.adoc[]
** xref:tool/client_cli.adoc[]
** xref:tool/bench.adoc[]
* link:http://github.com/redboltz/async_mqtt[Repository]
1 change: 1 addition & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ list(APPEND exec_PROGRAMS
ep_slcoro_mqtt_client.cpp
cl_cpp17_mqtt_pub.cpp
cl_cpp17_mqtt_sub.cpp
custom_logger.cpp
)

if("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
Expand Down
2 changes: 1 addition & 1 deletion example/cl_cpp17_mqtt_pub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// Finally, it disconnects from the broker.
//
// Example:
// ./cl_cpp20coro_mqtt_pub mqtt.redboltz.net 1883
// ./cl_cpp17_mqtt_pub mqtt.redboltz.net 1883

#include <iostream>
#include <string>
Expand Down
2 changes: 1 addition & 1 deletion example/cl_cpp17_mqtt_sub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// Finally, it disconnects from the broker.
//
// Example:
// ./cl_cpp20coro_mqtt_pub mqtt.redboltz.net 1883
// ./cl_cpp17_mqtt_sub mqtt.redboltz.net 1883

#include <iostream>
#include <string>
Expand Down
197 changes: 197 additions & 0 deletions example/custom_logger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright Takatoshi Kondo 2023
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// This example connects to the specified MQTT broker.
// It then publishes to topic1, topic2, and topic3, and receives the publish results.
// Finally, it disconnects from the broker.
//
// Example:
// ./custom_logger_pub mqtt.redboltz.net 1883

#include <iostream>
#include <string>

#include <boost/asio.hpp>

////////////////////////////////////////////////////////////////////////////////
// Using custom logger BEGIN

// Implement the following codes before any async_mqtt headers include.
// Exception:
// async_mqtt/util/log_severity.hpp is used for defining custom_log

#include <async_mqtt/util/log_severity.hpp>

// Optional:
// Define custom logger instead of Boost.Log (default)
// undef ASYNC_MQTT_USE_LOG or remove from compiler option
// -DASYNC_MQTT_USE_LOG removes all Boost.Log related code.
// It can compile faster.
#undef ASYNC_MQTT_USE_LOG

// Define simple custom logger
// This is example implementation.
// Just output channel, sevarity, and message body to std::clog
struct custom_log {
explicit constexpr custom_log(std::string_view chan, async_mqtt::severity_level sev)
{
// Setup filter
if (sev < async_mqtt::severity_level::info) {
print = false;
return;
}
// Output header
std::clog << "[" << sev << "]" << "(" << chan << ") ";
}
~custom_log() {
// Output trailer
if (print) std::clog << std::endl;
}

bool print = true;
};

template <typename T>
inline constexpr custom_log const& operator<<(
custom_log const& o,
T const& t
) {
// Output message body with filter
if (o.print) std::clog << t;
return o;
}

// Output additional value. Stringized name(tag) and tagged value.
#define ASYNC_MQTT_ADD_VALUE(name, val) "<" << #name ">{" << val << "} "

// Set ASYNC_MQTT_LOG macro to custom_log.
#define ASYNC_MQTT_LOG(chan, sev) custom_log(chan, async_mqtt::severity_level::sev)

// Using custom logger END
////////////////////////////////////////////////////////////////////////////////

// The following code is the same as cl_cpp17_mqtt_pub.cpp

#include <async_mqtt/all.hpp>


namespace as = boost::asio;
namespace am = async_mqtt;

using client_t = am::client<am::protocol_version::v5, am::protocol::mqtt>;

struct app {
app(as::any_io_executor exe, std::string_view host, std::string_view port)
: cli_{exe}
{
am::async_underlying_handshake(
cli_.next_layer(),
host,
port,
[this](auto&&... args) {
handle_underlying_handshake(
std::forward<std::remove_reference_t<decltype(args)>>(args)...
);
}
);
}

private:
void handle_underlying_handshake(
am::error_code ec
) {
std::cout << "underlying_handshake:" << ec.message() << std::endl;
if (ec) return;
cli_.async_start(
true, // clean_start
std::uint16_t(0), // keep_alive
"", // Client Identifier, empty means generated by the broker
std::nullopt, // will
"UserName1",
"Password1",
[this](auto&&... args) {
handle_start_response(std::forward<decltype(args)>(args)...);
}
);
}

void handle_start_response(
am::error_code ec,
std::optional<client_t::connack_packet> connack_opt
) {
std::cout << "start:" << ec.message() << std::endl;
if (ec) return;
if (connack_opt) {
std::cout << *connack_opt << std::endl;
cli_.async_publish(
"topic1",
"payload1",
am::qos::at_most_once,
[this](auto&&... args) {
handle_publish_response(
std::forward<std::remove_reference_t<decltype(args)>>(args)...
);
}
);
cli_.async_publish(
*cli_.acquire_unique_packet_id(), // sync version only works thread safe context
"topic2",
"payload2",
am::qos::at_least_once,
[this](auto&&... args) {
handle_publish_response(
std::forward<std::remove_reference_t<decltype(args)>>(args)...
);
}
);
cli_.async_publish(
*cli_.acquire_unique_packet_id(), // sync version only works thread safe context
"topic3",
"payload3",
am::qos::exactly_once,
[this](auto&&... args) {
handle_publish_response(
std::forward<std::remove_reference_t<decltype(args)>>(args)...
);
}
);
}
}

void handle_publish_response(
am::error_code ec,
client_t::pubres_type pubres
) {
std::cout << "publish:" << ec.message() << std::endl;
if (ec) return;
if (pubres.puback_opt) {
std::cout << *pubres.puback_opt << std::endl;
}
if (pubres.pubrec_opt) {
std::cout << *pubres.pubrec_opt << std::endl;
}
if (pubres.pubcomp_opt) {
std::cout << *pubres.pubcomp_opt << std::endl;
cli_.async_disconnect(as::detached);
}
}

client_t cli_;
};

int main(int argc, char* argv[]) {
am::setup_log(
am::severity_level::warning,
true // log colored
);
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " host port" << std::endl;
return -1;
}
as::io_context ioc;
app a{ioc.get_executor(), argv[1], argv[2]};
ioc.run();
}
Loading

0 comments on commit 712c859

Please sign in to comment.