Skip to content

Commit ddbb3da

Browse files
committed
Merge branch 'release/v1.4.0'
2 parents 15969c7 + f6927a6 commit ddbb3da

File tree

11 files changed

+639
-7
lines changed

11 files changed

+639
-7
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 1.4.0 (2022-02-18)
2+
3+
### Features
4+
5+
* Implement logger facility and support user-defined overriding - (#19)
6+
7+
18
## 1.3.0 (2022-02-16)
29

310
### Features

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
22

33
set(STREAMCLIENT_VERSION_MAJOR "1")
4-
set(STREAMCLIENT_VERSION_MINOR "3")
4+
set(STREAMCLIENT_VERSION_MINOR "4")
55
set(STREAMCLIENT_VERSION_RELEASE "0")
66
set(STREAMCLIENT_SUMMARY "C++ library")
77
set(STREAMCLIENT_REPOSITORY_URL "https://github.com/TinkoffCreditSystems/stream-client")

README.md

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,9 @@ client->receive(boost::asio::buffer(&recv_data[0], send_data.size()));
120120
Represents container occupied with opened sockets. Uses [connector](#connector) to open new sockets in the background thread which is triggered once there are vacant places in the pool. User can call *get_session()* to obtain a socket from the pool and *return_session()* to give it back.
121121
122122
There are two strategies to refill the pool:
123-
- greedy (`stream_client::connector::greedy_strategy`). If there are vacant places it will try to fill them with new sessions simultaneously.
124-
- conservative (`stream_client::connector::conservative_strategy`). Will try to fill up to 2/3 of vacant places in the poll. If failed will back of for some time and retry later. Also, after failures it will create only one new session.
123+
- **greedy** (`stream_client::connector::greedy_strategy`). If there are vacant places it will try to fill them with new sessions simultaneously. This is the default one.
124+
- **conservative** (`stream_client::connector::conservative_strategy`). Will try to fill up to 2/3 of vacant places in the poll. If failed will back of for some time and retry later. Also, after failures it will create only one new session.
125+
125126
Both of them are defined in terms of `stream_client::connector::pool_strategy` interface, so you are free to implement new one.
126127
127128
Limitations:
@@ -143,7 +144,12 @@ Connection pools:
143144
* `stream_client::connector::http_pool` - pool of `stream_client::http::http_client` sockets.
144145
* `stream_client::connector::https_pool` - pool of `stream_client::http::https_client` sockets.
145146
146-
*All these pools are using `stream_client::connector::greedy_strategy`.*
147+
There are also aliases for the same pools but using conservative reconnection strategy:
148+
* `stream_client::connector::tcp_conservative_pool`
149+
* `stream_client::connector::udp_conservative_pool`
150+
* `stream_client::connector::ssl_conservative_pool`
151+
* `stream_client::connector::http_conservative_pool`
152+
* `stream_client::connector::https_conservative_pool`
147153
148154
#### Example
149155
```c++
@@ -181,6 +187,44 @@ for (auto& t : threads) {
181187
}
182188
```
183189

190+
### Logging
191+
192+
The library uses basic logger interface implemented internally. You can modify logger level using `stream_client::set_log_level()` or `stream_client::get_log_level()` functions.
193+
194+
These levels are supported:
195+
```cpp
196+
enum class log_level : int
197+
{
198+
trace = 0,
199+
debug,
200+
info,
201+
warning,
202+
error,
203+
};
204+
```
205+
206+
By default library prints messages to stdout with decent formatting. If you want to overwrite this behavior you can set you own logger via:
207+
```cpp
208+
void stream_client::set_logger(std::shared_ptr<stream_client::log_interface> logger);
209+
210+
void stream_client::set_logger(stream_client::log_level level, stream_client::log_func_type log_func);
211+
```
212+
213+
Which allows to either overwrite logger instance of use a callback of with proper signature. These types are defines as:
214+
```cpp
215+
class log_interface
216+
{
217+
public:
218+
virtual void set_level(log_level level) noexcept = 0;
219+
virtual log_level get_level() const noexcept = 0;
220+
virtual void message(log_level level, const std::string& location, const std::string& message) const = 0;
221+
};
222+
223+
using log_func_type = std::function<void(log_level level, const std::string& location, const std::string& message)>;
224+
```
225+
226+
For more information please look inside [logger.hpp](./include/stream-client/logger.hpp).
227+
184228
## How to build
185229

186230
This library supposed to be somewhat multi-platform, however, it was tested and mainly used on ubuntu and macOS. </br>

include/stream-client/connector/connection_pool.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ class base_connection_pool
314314
/// Background routine used to maintain the pool.
315315
void watch_pool_routine();
316316

317-
Strategy reconnection_;
317+
std::string name_; ///< Name pf the pool.
318+
Strategy reconnection_; ///< Instance of reconnection strategy used to fill the pool.
318319
connector_type connector_; ///< Underlying connector used to establish sockets.
319320

320321
std::size_t pool_max_size_; ///< Number of stream to keep in the @p sesson_pool_.
@@ -330,16 +331,21 @@ class base_connection_pool
330331

331332
//! Connections pool with sockets over plain TCP protocol.
332333
using tcp_pool = base_connection_pool<tcp_connector>;
334+
using tcp_conservative_pool = base_connection_pool<tcp_connector, conservative_strategy<tcp_connector>>;
333335
//! Connections pool with sockets over plain UDP protocol.
334336
using udp_pool = base_connection_pool<udp_connector>;
337+
using udp_conservative_pool = base_connection_pool<udp_connector, conservative_strategy<udp_connector>>;
335338

336339
//! Connections pool with sockets over encrypted TCP protocol.
337340
using ssl_pool = base_connection_pool<ssl_connector>;
341+
using ssl_conservative_pool = base_connection_pool<ssl_connector, conservative_strategy<ssl_connector>>;
338342

339343
//! Connections pool with sockets over HTTP protocol.
340344
using http_pool = base_connection_pool<http_connector>;
345+
using http_conservative_pool = base_connection_pool<http_connector, conservative_strategy<http_connector>>;
341346
//! Connections pool with sockets over HTTPS protocol.
342347
using https_pool = base_connection_pool<https_connector>;
348+
using https_conservative_pool = base_connection_pool<https_connector, conservative_strategy<https_connector>>;
343349

344350
} // namespace connector
345351
} // namespace stream_client

include/stream-client/connector/impl/connection_pool.ipp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include "stream-client/logger.hpp"
4+
35
namespace stream_client {
46
namespace connector {
57

@@ -12,7 +14,9 @@ base_connection_pool<Connector, Strategy>::base_connection_pool(std::size_t size
1214
, idle_timeout_(idle_timeout)
1315
, watch_pool_(true)
1416
{
17+
name_ = "connection_pool[" + connector_.get_target() + "](" + std::to_string(pool_max_size_) + ")";
1518
pool_watcher_ = std::thread([this]() { this->watch_pool_routine(); });
19+
STREAM_LOG_TRACE(name_ + " has been created");
1620
}
1721

1822
template <typename Connector, typename Strategy>

include/stream-client/connector/impl/pool_strategy.ipp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include "stream-client/logger.hpp"
4+
35
#include <boost/system/system_error.hpp>
46

57
#include <atomic>
@@ -31,7 +33,7 @@ bool greedy_strategy<Connector>::refill(connector_type& connector, std::size_t v
3133
auto new_session = connector.new_session();
3234
append_func(std::move(new_session));
3335
} catch (const boost::system::system_error& e) {
34-
// TODO: log errors ?
36+
STREAM_LOG_ERROR("failed to establish new session to " + connector.get_target() + ": " + e.what());
3537
}
3638
};
3739

@@ -75,7 +77,7 @@ bool conservative_strategy<Connector>::refill(connector_type& connector, std::si
7577
append_func(std::move(new_session));
7678
is_added = true;
7779
} catch (const boost::system::system_error& e) {
78-
// TODO: log errors ?
80+
STREAM_LOG_ERROR("failed to establish new session to " + connector.get_target() + ": " + e.what());
7981
}
8082
};
8183

include/stream-client/impl/logger.ipp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#pragma once
2+
3+
#include <iomanip>
4+
#include <iostream>
5+
#include <mutex>
6+
#include <sstream>
7+
8+
namespace stream_client {
9+
namespace detail {
10+
11+
/// Get/set global logger instance.
12+
inline std::shared_ptr<log_interface> logger_instance(std::shared_ptr<log_interface> new_logger)
13+
{
14+
// By default use cout logger with trace level
15+
static std::shared_ptr<log_interface> glob_instance = std::make_shared<cout_logger>();
16+
17+
if (new_logger) {
18+
glob_instance = new_logger;
19+
}
20+
return glob_instance;
21+
}
22+
23+
} // namespace detail
24+
25+
base_logger::base_logger(log_level level)
26+
: level_(level)
27+
{
28+
}
29+
30+
void base_logger::set_level(log_level level) noexcept
31+
{
32+
level_ = level;
33+
}
34+
35+
log_level base_logger::get_level() const noexcept
36+
{
37+
return level_;
38+
}
39+
40+
func_logger::func_logger(log_level level, stream_client::log_func_type log_func)
41+
: base_logger(level)
42+
, log_func_(log_func)
43+
{
44+
}
45+
46+
void func_logger::message(log_level level, const std::string& location, const std::string& message) const
47+
{
48+
if (log_func_) {
49+
log_func_(level, location, message);
50+
}
51+
}
52+
53+
cout_logger::cout_logger(log_level level)
54+
: base_logger(level)
55+
{
56+
}
57+
58+
void cout_logger::message(log_level level, const std::string& location, const std::string& message) const
59+
{
60+
static constexpr const char* kLevelPrefixes[] = {
61+
"TRACE", "DEBUG", "INFO", "WARNING", "ERROR",
62+
};
63+
64+
std::stringstream ss;
65+
const auto now = std::chrono::system_clock::now();
66+
const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
67+
std::lock_guard<std::mutex> lock(mutex_);
68+
ss << std::put_time(std::localtime(&t_c), "%Y-%m-%dT%H:%M:%SZ") << ": " << kLevelPrefixes[static_cast<int>(level)]
69+
<< ": " << location << ": " << message << std::endl;
70+
std::cout << ss.str();
71+
}
72+
73+
inline void set_logger(std::shared_ptr<log_interface> logger)
74+
{
75+
detail::logger_instance(std::move(logger));
76+
}
77+
78+
inline void set_logger(log_level level, stream_client::log_func_type log_func)
79+
{
80+
detail::logger_instance(std::make_shared<func_logger>(level, std::move(log_func)));
81+
}
82+
83+
inline void set_log_level(log_level level)
84+
{
85+
const auto logger = detail::logger_instance();
86+
if (logger) {
87+
logger->set_level(level);
88+
}
89+
}
90+
91+
inline log_level get_log_level()
92+
{
93+
const auto logger = detail::logger_instance();
94+
if (logger) {
95+
return logger->get_level();
96+
}
97+
return log_level::mute;
98+
}
99+
100+
inline void log_message(log_level level, const std::string& location, const std::string& message)
101+
{
102+
const auto logger = detail::logger_instance();
103+
if (logger) {
104+
logger->message(level, location, message);
105+
}
106+
}
107+
108+
} // namespace stream_client

0 commit comments

Comments
 (0)