forked from envoyproxy/envoy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
logging.h
164 lines (147 loc) · 8.92 KB
/
logging.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include "common/common/logger.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "spdlog/spdlog.h"
namespace Envoy {
/**
* Provides a mechanism to temporarily set the logging level on
* construction, restoring its previous state on destruction.
*
* The log_level is the minimum log severity required to print messages.
* Messages below this loglevel will be suppressed.
*
* Note that during the scope of this object, command-line overrides, eg
* --log-level trace, will not take effect.
*
* Also note: instantiating this setter should only occur when the system is
* in a quiescent state, e.g. at startup or between tests.
*
* This is intended for use in EXPECT_LOG_CONTAINS and similar macros.
*/
class LogLevelSetter {
public:
explicit LogLevelSetter(spdlog::level::level_enum log_level);
~LogLevelSetter();
private:
std::vector<spdlog::level::level_enum> previous_levels_;
};
/**
* Records log messages in a vector<string>, forwarding them to the previous
* delegate. This is useful for unit-testing log messages while still being able
* to see them on stderr.
*
* Also note: instantiating this sink should only occur when the system is
* in a quiescent state, e.g. at startup or between tests
*
* This is intended for use in EXPECT_LOG_CONTAINS and similar macros.
*/
class LogRecordingSink : public Logger::SinkDelegate {
public:
explicit LogRecordingSink(Logger::DelegatingLogSinkPtr log_sink);
virtual ~LogRecordingSink();
// Logger::SinkDelegate
void log(absl::string_view msg) override;
void flush() override;
const std::vector<std::string>& messages() const { return messages_; }
private:
std::vector<std::string> messages_;
};
typedef std::pair<std::string, std::string> StringPair;
typedef std::vector<StringPair> ExpectedLogMessages;
// Validates that when stmt is executed, log messages containing substr and loglevel will be
// emitted. Failure message e.g.,
//
// Logs:
// [2018-04-12 05:51:00.245][7290192][debug][upstream] grpc_mux_impl.cc:160] Received gRPC
// [2018-04-12 05:51:00.246][7290192][warning][upstream] grpc_mux_impl.cc:63] Called bar
// [2018-04-12 05:51:00.246][7290192][trace][upstream] grpc_mux_impl.cc:80] Sending foo
// Does NOT contain:
// 'warning', 'Too many sendDiscoveryRequest calls for baz’
// 'warning', 'Too man sendDiscoveryRequest calls for foo'
#define EXPECT_LOG_CONTAINS_ALL_OF(expected_messages, stmt) \
do { \
ASSERT_FALSE(expected_messages.empty()) << "Expected messages cannot be empty."; \
LogLevelSetter save_levels(spdlog::level::trace); \
LogRecordingSink log_recorder(Logger::Registry::getSink()); \
stmt; \
if (log_recorder.messages().empty()) { \
FAIL() << "Expected message(s), but NONE was recorded."; \
} \
ExpectedLogMessages failed_expectations; \
for (const StringPair& expected : expected_messages) { \
const auto log_message = \
std::find_if(log_recorder.messages().begin(), log_recorder.messages().end(), \
[&expected](const std::string& message) { \
return (message.find(expected.second) != std::string::npos) && \
(message.find(expected.first) != std::string::npos); \
}); \
if (log_message == log_recorder.messages().end()) { \
failed_expectations.push_back(expected); \
} \
} \
if (!failed_expectations.empty()) { \
std::string failed_message; \
absl::StrAppend(&failed_message, "\nLogs:\n ", absl::StrJoin(log_recorder.messages(), " "), \
"\n Do NOT contain:\n"); \
for (const auto& expectation : failed_expectations) { \
absl::StrAppend(&failed_message, " '", expectation.first, "', '", expectation.second, \
"'\n"); \
} \
FAIL() << failed_message; \
} \
} while (false)
// Validates that when stmt is executed, log message containing substr and loglevel will NOT be
// emitted. Failure message e.g.,
//
// Logs:
// [2018-04-12 05:51:00.245][7290192][warning][upstream] grpc_mux_impl.cc:160] Received gRPC
// [2018-04-12 05:51:00.246][7290192][trace][upstream] grpc_mux_impl.cc:63] Called bar
// Should NOT contain:
// 'warning', 'Received gRPC’
#define EXPECT_LOG_NOT_CONTAINS(loglevel, substr, stmt) \
do { \
LogLevelSetter save_levels(spdlog::level::trace); \
LogRecordingSink log_recorder(Logger::Registry::getSink()); \
stmt; \
for (const std::string& message : log_recorder.messages()) { \
if ((message.find(substr) != std::string::npos) && \
(message.find(loglevel) != std::string::npos)) { \
FAIL() << "\nLogs:\n " << absl::StrJoin(log_recorder.messages(), " ") \
<< "\n Should NOT contain:\n '" << loglevel << "', '" << substr "'\n"; \
} \
} \
} while (false)
// Validates that when stmt is executed, the supplied substring matches at least one log message.
// Failure message e.g.,
//
// Logs:
// [2018-04-12 05:51:00.245][7290192][debug][upstream] grpc_mux_impl.cc:160] Received gRPC
// [2018-04-12 05:51:00.246][7290192][trace][upstream] grpc_mux_impl.cc:80] Sending foo
// Do NOT contain:
// 'warning', 'Too many sendDiscoveryRequest calls for baz’
#define EXPECT_LOG_CONTAINS(loglevel, substr, stmt) \
do { \
const ExpectedLogMessages message{{loglevel, substr}}; \
EXPECT_LOG_CONTAINS_ALL_OF(message, stmt); \
} while (false)
// Validates that when stmt is executed, no logs will be emitted.
// Expected equality of these values:
// 0
// logs.size()
// Which is: 3
// Logs:
// [2018-04-12 05:51:00.245][7290192][debug][upstream] grpc_mux_impl.cc:160] Received gRPC
// [2018-04-12 05:51:00.246][7290192][trace][upstream] grpc_mux_impl.cc:80] Sending foo
#define EXPECT_NO_LOGS(stmt) \
do { \
LogLevelSetter save_levels(spdlog::level::trace); \
LogRecordingSink log_recorder(Logger::Registry::getSink()); \
stmt; \
const std::vector<std::string> logs = log_recorder.messages(); \
ASSERT_EQ(0, logs.size()) << " Logs:\n " << absl::StrJoin(logs, " "); \
} while (false)
} // namespace Envoy