Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added custom logger example. #367

Merged
merged 1 commit into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading