Skip to content

Commit fb8b524

Browse files
Add censor API to Log
It allows to set values to be masked from the log messages at runtime Relates-To: NLAM-140 Signed-off-by: Rustam Gamidov <[email protected]>
1 parent 65757ff commit fb8b524

File tree

3 files changed

+133
-20
lines changed

3 files changed

+133
-20
lines changed

olp-cpp-sdk-core/include/olp/core/logging/Log.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,16 @@ class CORE_API Log {
562562
const std::string& message, const char* file,
563563
unsigned int line, const char* function,
564564
const char* fullFunction);
565+
566+
/**
567+
* @brief Adds a line to be censored out from the log.
568+
*
569+
* Censoring out is a replacement with a predefiend mask with length not
570+
* correlated with the original line length.
571+
*
572+
* @param message The line to be censored out from the log.
573+
*/
574+
static void censor(const std::string& message);
565575
};
566576
} // namespace logging
567577
} // namespace olp

olp-cpp-sdk-core/src/logging/Log.cpp

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,24 @@
2323

2424
#include <olp/core/logging/Configuration.h>
2525
#include <olp/core/logging/FilterGroup.h>
26+
#include <olp/core/porting/optional.h>
2627
#include <olp/core/thread/Atomic.h>
2728

2829
#include <string>
2930
#include <unordered_map>
3031

3132
namespace olp {
3233
namespace logging {
34+
35+
namespace {
36+
37+
constexpr auto kSecretMask = "*****";
38+
}
39+
40+
struct LogMessageEx : public LogMessage {
41+
olp::porting::optional<std::string> adjusted_message;
42+
};
43+
3344
class LogImpl {
3445
public:
3546
friend class olp::thread::Atomic<logging::LogImpl>;
@@ -67,14 +78,20 @@ class LogImpl {
6778
unsigned int line, const char* function,
6879
const char* fullFunction);
6980

81+
void censor(std::string msg);
82+
7083
private:
7184
LogImpl();
7285
template <class LogItem>
7386
void appendLogItem(const LogItem& log_item);
7487

88+
template <class LogItem>
89+
void censorLogItem(LogItem& log_item, const std::string& original);
90+
7591
Configuration m_configuration;
7692
std::unordered_map<std::string, Level> m_logLevels;
7793
Level m_defaultLevel;
94+
std::vector<std::string> m_toCensor;
7895
};
7996

8097
LogImpl::LogImpl()
@@ -120,7 +137,8 @@ void LogImpl::setLevel(Level level, const std::string& tag) {
120137
}
121138

122139
porting::optional<Level> LogImpl::getLevel(const std::string& tag) const {
123-
if (tag.empty()) return getLevel();
140+
if (tag.empty())
141+
return getLevel();
124142

125143
auto foundIter = m_logLevels.find(tag);
126144
if (foundIter == m_logLevels.end())
@@ -130,21 +148,24 @@ porting::optional<Level> LogImpl::getLevel(const std::string& tag) const {
130148
}
131149

132150
void LogImpl::clearLevel(const std::string& tag) {
133-
if (tag.empty()) return;
151+
if (tag.empty())
152+
return;
134153

135154
m_logLevels.erase(tag);
136155
}
137156

138157
void LogImpl::clearLevels() { m_logLevels.clear(); }
139158

140159
bool LogImpl::isEnabled(Level level) const {
141-
if (level == Level::Off) return false;
160+
if (level == Level::Off)
161+
return false;
142162

143163
return static_cast<int>(level) >= static_cast<int>(m_defaultLevel);
144164
}
145165

146166
bool LogImpl::isEnabled(Level level, const std::string& tag) const {
147-
if (level == Level::Off) return false;
167+
if (level == Level::Off)
168+
return false;
148169

149170
auto foundIter = m_logLevels.find(tag);
150171
Level targetLevel;
@@ -159,7 +180,7 @@ void LogImpl::logMessage(Level level, const std::string& tag,
159180
const std::string& message, const char* file,
160181
unsigned int line, const char* function,
161182
const char* fullFunction) {
162-
LogMessage logMessage;
183+
LogMessageEx logMessage;
163184
logMessage.level = level;
164185
logMessage.tag = tag.c_str();
165186
logMessage.message = message.c_str();
@@ -170,6 +191,7 @@ void LogImpl::logMessage(Level level, const std::string& tag,
170191
logMessage.time = std::chrono::system_clock::now();
171192
logMessage.threadId = getThreadId();
172193

194+
censorLogItem(logMessage, message);
173195
appendLogItem(logMessage);
174196
}
175197

@@ -181,95 +203,134 @@ void LogImpl::appendLogItem(const LogItem& log_item) {
181203
}
182204
}
183205

206+
template <class LogItem>
207+
void LogImpl::censorLogItem(LogItem& log_item, const std::string& original) {
208+
bool has_copy = log_item.adjusted_message.has_value();
209+
const std::string& src =
210+
has_copy ? log_item.adjusted_message.value() : original;
211+
212+
for (const std::string& secret : m_toCensor) {
213+
auto found_pos = src.find(secret);
214+
while (found_pos != std::string::npos) {
215+
if (!has_copy) {
216+
log_item.adjusted_message = original;
217+
has_copy = true;
218+
log_item.message = log_item.adjusted_message.value().c_str();
219+
}
220+
log_item.adjusted_message.value().replace(found_pos, secret.length(),
221+
kSecretMask);
222+
found_pos = log_item.adjusted_message.value().find(secret, found_pos);
223+
}
224+
}
225+
}
226+
227+
void LogImpl::censor(std::string msg) {
228+
m_toCensor.emplace_back(std::move(msg));
229+
}
230+
184231
// implementation of public static Log API
185232
//--------------------------------------------------------
186233

187234
bool Log::configure(Configuration configuration) {
188-
if (!LogImpl::aliveStatus()) return false;
235+
if (!LogImpl::aliveStatus())
236+
return false;
189237

190238
return LogImpl::getInstance().locked([&configuration](LogImpl& log) {
191239
return log.configure(std::move(configuration));
192240
});
193241
}
194242

195243
Configuration Log::getConfiguration() {
196-
if (!LogImpl::aliveStatus()) return Configuration();
244+
if (!LogImpl::aliveStatus())
245+
return Configuration();
197246

198247
return LogImpl::getInstance().locked(
199248
[](const LogImpl& log) { return log.getConfiguration(); });
200249
}
201250

202251
void Log::setLevel(Level level) {
203-
if (!LogImpl::aliveStatus()) return;
252+
if (!LogImpl::aliveStatus())
253+
return;
204254

205255
LogImpl::getInstance().locked([level](LogImpl& log) { log.setLevel(level); });
206256
}
207257

208258
Level Log::getLevel() {
209-
if (!LogImpl::aliveStatus()) return Level::Off;
259+
if (!LogImpl::aliveStatus())
260+
return Level::Off;
210261

211262
return LogImpl::getInstance().locked(
212263
[](const LogImpl& log) { return log.getLevel(); });
213264
}
214265

215266
void Log::setLevel(Level level, const std::string& tag) {
216-
if (!LogImpl::aliveStatus()) return;
267+
if (!LogImpl::aliveStatus())
268+
return;
217269

218270
LogImpl::getInstance().locked(
219271
[level, &tag](LogImpl& log) { log.setLevel(level, tag); });
220272
}
221273

222274
void Log::setLevel(const std::string& level, const std::string& tag) {
223275
auto log_level = FilterGroup::stringToLevel(level);
224-
if (!log_level) return;
276+
if (!log_level)
277+
return;
225278

226-
if (!LogImpl::aliveStatus()) return;
279+
if (!LogImpl::aliveStatus())
280+
return;
227281

228282
LogImpl::getInstance().locked(
229283
[&log_level, &tag](LogImpl& log) { log.setLevel(*log_level, tag); });
230284
}
231285

232286
porting::optional<Level> Log::getLevel(const std::string& tag) {
233-
if (!LogImpl::aliveStatus()) return porting::none;
287+
if (!LogImpl::aliveStatus())
288+
return porting::none;
234289

235290
return LogImpl::getInstance().locked(
236291
[&tag](const LogImpl& log) { return log.getLevel(tag); });
237292
}
238293

239294
void Log::clearLevel(const std::string& tag) {
240-
if (!LogImpl::aliveStatus()) return;
295+
if (!LogImpl::aliveStatus())
296+
return;
241297

242298
LogImpl::getInstance().locked([&tag](LogImpl& log) { log.clearLevel(tag); });
243299
}
244300

245301
void Log::clearLevels() {
246-
if (!LogImpl::aliveStatus()) return;
302+
if (!LogImpl::aliveStatus())
303+
return;
247304

248305
LogImpl::getInstance().locked([](LogImpl& log) { log.clearLevels(); });
249306
}
250307

251308
void Log::applyFilterGroup(const FilterGroup& filters) {
252-
if (!LogImpl::aliveStatus()) return;
309+
if (!LogImpl::aliveStatus())
310+
return;
253311

254312
LogImpl::getInstance().locked([&filters](LogImpl& log) {
255313
log.clearLevels();
256314
log.clearLevels();
257315
auto defaultLevel = filters.getLevel();
258-
if (defaultLevel) log.setLevel(*defaultLevel);
316+
if (defaultLevel)
317+
log.setLevel(*defaultLevel);
259318
for (const auto& tagLevel : filters.m_tagLevels)
260319
log.setLevel(tagLevel.second, tagLevel.first);
261320
});
262321
}
263322

264323
bool Log::isEnabled(Level level) {
265-
if (!LogImpl::aliveStatus()) return false;
324+
if (!LogImpl::aliveStatus())
325+
return false;
266326

267327
return LogImpl::getInstance().locked(
268328
[level](const LogImpl& log) { return log.isEnabled(level); });
269329
}
270330

271331
bool Log::isEnabled(Level level, const std::string& tag) {
272-
if (!LogImpl::aliveStatus()) return false;
332+
if (!LogImpl::aliveStatus())
333+
return false;
273334

274335
return LogImpl::getInstance().locked(
275336
[level, &tag](const LogImpl& log) { return log.isEnabled(level, tag); });
@@ -279,12 +340,20 @@ void Log::logMessage(Level level, const std::string& tag,
279340
const std::string& message, const char* file,
280341
unsigned int line, const char* function,
281342
const char* fullFunction) {
282-
if (!LogImpl::aliveStatus()) return;
343+
if (!LogImpl::aliveStatus())
344+
return;
283345

284346
LogImpl::getInstance().locked([&](LogImpl& log) {
285347
log.logMessage(level, tag, message, file, line, function, fullFunction);
286348
});
287349
}
288350

351+
void Log::censor(const std::string& message) {
352+
if (!LogImpl::aliveStatus())
353+
return;
354+
355+
LogImpl::getInstance().locked([&](LogImpl& log) { log.censor(message); });
356+
}
357+
289358
} // namespace logging
290359
} // namespace olp

olp-cpp-sdk-core/tests/logging/LogTest.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,40 @@ TEST(LogTest, LogFormat) {
409409
++index;
410410
}
411411

412+
TEST(LogTest, LogCensor) {
413+
auto appender = std::make_shared<testing::MockAppender>();
414+
olp::logging::Configuration configuration;
415+
configuration.addAppender(appender);
416+
EXPECT_TRUE(olp::logging::Log::configure(configuration));
417+
olp::logging::Log::setLevel(olp::logging::Level::Trace);
418+
419+
constexpr auto secret_01 = "1st secret";
420+
constexpr auto secret_02 = "to hide";
421+
422+
olp::logging::Log::censor(secret_01);
423+
olp::logging::Log::censor(secret_02);
424+
425+
OLP_SDK_LOG_INFO_F("", "Nothing to censor");
426+
OLP_SDK_LOG_INFO_F("", "Inlined1st secretin message");
427+
OLP_SDK_LOG_TRACE_F("trace", "%s %s", "plain", secret_01);
428+
OLP_SDK_LOG_DEBUG_F("debug", "%s %s%s", "no spaces", secret_02, "**");
429+
OLP_SDK_LOG_INFO_F("info", "%s %s %s", secret_02, "several", secret_01);
430+
OLP_SDK_LOG_WARNING_F("warning", "%s %s %s", secret_02, "twice", secret_02);
431+
OLP_SDK_LOG_ERROR_F("error", "%s %s", "Error", secret_01);
432+
OLP_SDK_LOG_FATAL_F("fatal", "%s %s", "Fatal", secret_02);
433+
434+
ASSERT_EQ(8U, appender->messages_.size());
435+
436+
EXPECT_EQ("Nothing to censor", appender->messages_[0].message_);
437+
EXPECT_EQ("Inlined*****in message", appender->messages_[1].message_);
438+
EXPECT_EQ("plain *****", appender->messages_[2].message_);
439+
EXPECT_EQ("no spaces *******", appender->messages_[3].message_);
440+
EXPECT_EQ("***** several *****", appender->messages_[4].message_);
441+
EXPECT_EQ("***** twice *****", appender->messages_[5].message_);
442+
EXPECT_EQ("Error *****", appender->messages_[6].message_);
443+
EXPECT_EQ("Fatal *****", appender->messages_[7].message_);
444+
}
445+
412446
TEST(LogTest, LogLimits) {
413447
auto appender = std::make_shared<testing::MockAppender>();
414448
olp::logging::Configuration configuration;

0 commit comments

Comments
 (0)