pw_log_rpc: Add log filter thread name support
- Add supporting infastructure for thread label.
- Add thread label to existing tests for log_service and log_filter.
- Create tests showing thread labels impact.
Change-Id: Id56e9723f82ba44149fcccdc496b8eacd3c5ed8e
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/96622
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Carlos Chinchilla <cachinchilla@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Carlos Chinchilla <cachinchilla@google.com>
diff --git a/pw_log/log.proto b/pw_log/log.proto
index 611724a..14c3155 100644
--- a/pw_log/log.proto
+++ b/pw_log/log.proto
@@ -199,6 +199,9 @@
DROP = 2; // Drop the log entry if all conditions are met
};
Action action = 4;
+
+ // Condition 4: (thread_equals.size() == 0 || log.thread == thread_equals).
+ optional bytes thread_equals = 5 [(tokenizer.format) = TOKENIZATION_OPTIONAL];
}
// A filter is a series of rules. First matching rule wins.
diff --git a/pw_log_rpc/docs.rst b/pw_log_rpc/docs.rst
index bc0d78c..7586891 100644
--- a/pw_log_rpc/docs.rst
+++ b/pw_log_rpc/docs.rst
@@ -296,8 +296,8 @@
- ``module_equals``: the condition is met if this byte array is empty, or the
log module equals the contents of this byte array.
-- ``thread``: the condition is met if this byte array is empty or the log
- thread equals the contents of this byte array.
+- ``thread_equals``: the condition is met if this byte array is empty or the
+ log thread equals the contents of this byte array.
Filter
------
diff --git a/pw_log_rpc/log_filter.cc b/pw_log_rpc/log_filter.cc
index 5385be4..044640c 100644
--- a/pw_log_rpc/log_filter.cc
+++ b/pw_log_rpc/log_filter.cc
@@ -25,7 +25,8 @@
bool IsRuleMet(const Filter::Rule& rule,
uint32_t level,
ConstByteSpan module,
- uint32_t flags) {
+ uint32_t flags,
+ ConstByteSpan thread) {
if (level < static_cast<uint32_t>(rule.level_greater_than_or_equal)) {
return false;
}
@@ -38,6 +39,12 @@
rule.module_equals.end())) {
return false;
}
+ if (!rule.thread_equals.empty() && !std::equal(thread.begin(),
+ thread.end(),
+ rule.thread_equals.begin(),
+ rule.thread_equals.end())) {
+ return false;
+ }
return true;
}
@@ -82,6 +89,14 @@
PW_TRY(rule_decoder.ReadUint32(
reinterpret_cast<uint32_t*>(&rules_[i].action)));
break;
+ case log::FilterRule::Fields::THREAD_EQUALS: {
+ ConstByteSpan thread;
+ PW_TRY(rule_decoder.ReadBytes(&thread));
+ if (thread.size() > rules_[i].thread_equals.max_size()) {
+ return Status::InvalidArgument();
+ }
+ rules_[i].thread_equals.assign(thread.begin(), thread.end());
+ } break;
}
}
}
@@ -95,6 +110,7 @@
uint32_t log_level = 0;
ConstByteSpan log_module;
+ ConstByteSpan log_thread;
uint32_t log_flags = 0;
protobuf::Decoder decoder(entry);
while (decoder.Next().ok()) {
@@ -110,6 +126,9 @@
case log::LogEntry::Fields::FLAGS:
decoder.ReadUint32(&log_flags).IgnoreError();
break;
+ case log::LogEntry::Fields::THREAD:
+ decoder.ReadBytes(&log_thread).IgnoreError();
+ break;
default:
break;
}
@@ -120,7 +139,7 @@
if (rule.action == Filter::Rule::Action::kInactive) {
continue;
}
- if (IsRuleMet(rule, log_level, log_module, log_flags)) {
+ if (IsRuleMet(rule, log_level, log_module, log_flags, log_thread)) {
return rule.action == Filter::Rule::Action::kDrop;
}
}
diff --git a/pw_log_rpc/log_filter_service.cc b/pw_log_rpc/log_filter_service.cc
index 65e3974..2d8b63c 100644
--- a/pw_log_rpc/log_filter_service.cc
+++ b/pw_log_rpc/log_filter_service.cc
@@ -69,6 +69,7 @@
rule_encoder.WriteAnyFlagsSet(rule.any_flags_set).IgnoreError();
rule_encoder.WriteAction(static_cast<log::FilterRule::Action>(rule.action))
.IgnoreError();
+ rule_encoder.WriteThreadEquals(rule.thread_equals).IgnoreError();
PW_TRY_WITH_SIZE(rule_encoder.status());
}
PW_TRY_WITH_SIZE(encoder.status());
diff --git a/pw_log_rpc/log_filter_service_test.cc b/pw_log_rpc/log_filter_service_test.cc
index d0d9afc..a608caf 100644
--- a/pw_log_rpc/log_filter_service_test.cc
+++ b/pw_log_rpc/log_filter_service_test.cc
@@ -99,6 +99,7 @@
encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
+ PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
return encoder.WriteAction(static_cast<log::FilterRule::Action>(rule.action));
}
@@ -131,6 +132,7 @@
expected_rule.level_greater_than_or_equal);
EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
+ EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
EXPECT_EQ(rule.action, expected_rule.action);
}
@@ -141,24 +143,36 @@
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0x0f,
.module_equals{std::byte(123)},
+ .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(4)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
}};
const Filter new_filter(filters_[0].id(),
@@ -186,24 +200,40 @@
.level_greater_than_or_equal = log::FilterRule::Level::CRITICAL_LEVEL,
.any_flags_set = 0xfd,
.module_equals{std::byte(543)},
+ .thread_equals{std::byte('M'),
+ std::byte('0'),
+ std::byte('L'),
+ std::byte('O'),
+ std::byte('G')},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xca,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0xabcd,
.module_equals{std::byte(9000)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(123)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
}};
Filter& filter = filters_[0];
@@ -242,24 +272,28 @@
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0xab,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0x11,
.module_equals{std::byte(34)},
+ .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{std::byte(23)},
+ .thread_equals{std::byte('R'), std::byte('P'), std::byte('C')},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0x0f,
.module_equals{},
+ .thread_equals{std::byte('R'), std::byte('P'), std::byte('C')},
},
}};
const Filter second_filter(
@@ -284,7 +318,9 @@
void VerifyFilterRule(protobuf::Decoder& decoder,
const Filter::Rule& expected_rule) {
ASSERT_TRUE(decoder.Next().ok());
- ASSERT_EQ(decoder.FieldNumber(), 1u); // level_greater_than_or_equal
+ ASSERT_EQ(decoder.FieldNumber(),
+ static_cast<uint32_t>(
+ log::FilterRule::Fields::LEVEL_GREATER_THAN_OR_EQUAL));
log::FilterRule::Level level_greater_than_or_equal;
ASSERT_EQ(decoder.ReadUint32(
reinterpret_cast<uint32_t*>(&level_greater_than_or_equal)),
@@ -293,7 +329,8 @@
expected_rule.level_greater_than_or_equal);
ASSERT_TRUE(decoder.Next().ok());
- ASSERT_EQ(decoder.FieldNumber(), 2u); // module_equals
+ ASSERT_EQ(decoder.FieldNumber(),
+ static_cast<uint32_t>(log::FilterRule::Fields::MODULE_EQUALS));
ConstByteSpan module_equals;
ASSERT_EQ(decoder.ReadBytes(&module_equals), OkStatus());
ASSERT_EQ(module_equals.size(), expected_rule.module_equals.size());
@@ -303,17 +340,30 @@
0);
ASSERT_TRUE(decoder.Next().ok());
- ASSERT_EQ(decoder.FieldNumber(), 3u); // any_flags_set
+ ASSERT_EQ(decoder.FieldNumber(),
+ static_cast<uint32_t>(log::FilterRule::Fields::ANY_FLAGS_SET));
uint32_t any_flags_set;
ASSERT_EQ(decoder.ReadUint32(&any_flags_set), OkStatus());
EXPECT_EQ(any_flags_set, expected_rule.any_flags_set);
ASSERT_TRUE(decoder.Next().ok());
- ASSERT_EQ(decoder.FieldNumber(), 4u); // action
+ ASSERT_EQ(decoder.FieldNumber(),
+ static_cast<uint32_t>(log::FilterRule::Fields::ACTION));
Filter::Rule::Action action;
ASSERT_EQ(decoder.ReadUint32(reinterpret_cast<uint32_t*>(&action)),
OkStatus());
EXPECT_EQ(action, expected_rule.action);
+
+ ASSERT_TRUE(decoder.Next().ok());
+ ASSERT_EQ(decoder.FieldNumber(),
+ static_cast<uint32_t>(log::FilterRule::Fields::THREAD_EQUALS));
+ ConstByteSpan thread;
+ ASSERT_EQ(decoder.ReadBytes(&thread), OkStatus());
+ ASSERT_EQ(thread.size(), expected_rule.thread_equals.size());
+ EXPECT_EQ(
+ std::memcmp(
+ thread.data(), expected_rule.thread_equals.data(), thread.size()),
+ 0);
}
void VerifyFilterRules(protobuf::Decoder& decoder,
@@ -355,6 +405,9 @@
rules1_[0].any_flags_set = 0xab;
const std::array<std::byte, 2> module1{std::byte(123), std::byte(0xab)};
rules1_[0].module_equals.assign(module1.begin(), module1.end());
+ const std::array<std::byte, 4> thread1{
+ std::byte('H'), std::byte('O'), std::byte('S'), std::byte('T')};
+ rules1_[0].thread_equals.assign(thread1.begin(), thread1.end());
rules1_[1].action = Filter::Rule::Action::kDrop;
rules1_[1].level_greater_than_or_equal = log::FilterRule::Level::ERROR_LEVEL;
rules1_[1].any_flags_set = 0;
@@ -373,6 +426,9 @@
rules1_[2].any_flags_set = 0xcd;
const std::array<std::byte, 2> module2{std::byte(1), std::byte(2)};
rules1_[2].module_equals.assign(module2.begin(), module2.end());
+ const std::array<std::byte, 3> thread2{
+ std::byte('A'), std::byte('P'), std::byte('P')};
+ rules1_[2].thread_equals.assign(thread2.begin(), thread2.end());
rules1_[3].action = Filter::Rule::Action::kInactive;
PW_RAW_TEST_METHOD_CONTEXT(FilterService, GetFilter, 1)
diff --git a/pw_log_rpc/log_filter_test.cc b/pw_log_rpc/log_filter_test.cc
index be1fcdd..8f85fef 100644
--- a/pw_log_rpc/log_filter_test.cc
+++ b/pw_log_rpc/log_filter_test.cc
@@ -35,6 +35,8 @@
constexpr uint32_t kSampleModule = 0x1234;
constexpr uint32_t kSampleFlags = 0x3;
+const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kSampleThread = {
+ std::byte('R'), std::byte('P'), std::byte('C')};
constexpr char kSampleMessage[] = "message";
constexpr auto kSampleModuleLittleEndian =
bytes::CopyInOrder<uint32_t>(std::endian::little, kSampleModule);
@@ -42,12 +44,13 @@
// Creates and encodes a log entry in the provided buffer.
template <uintptr_t log_level, uintptr_t module, uintptr_t flags>
Result<ConstByteSpan> EncodeLogEntry(std::string_view message,
- ByteSpan buffer) {
+ ByteSpan buffer,
+ ConstByteSpan thread) {
auto metadata = log_tokenized::Metadata::Set<log_level, module, flags, 0>();
return log::EncodeTokenizedLog(metadata,
std::as_bytes(std::span(message)),
/*ticks_since_epoch=*/0,
- /*thread_name=*/{},
+ thread,
buffer);
}
@@ -57,6 +60,7 @@
encoder.WriteLevelGreaterThanOrEqual(rule.level_greater_than_or_equal));
PW_TRY(encoder.WriteModuleEquals(rule.module_equals));
PW_TRY(encoder.WriteAnyFlagsSet(rule.any_flags_set));
+ PW_TRY(encoder.WriteThreadEquals(rule.thread_equals));
return encoder.WriteAction(static_cast<log::FilterRule::Action>(rule.action));
}
@@ -74,6 +78,7 @@
expected_rule.level_greater_than_or_equal);
EXPECT_EQ(rule.module_equals, expected_rule.module_equals);
EXPECT_EQ(rule.any_flags_set, expected_rule.any_flags_set);
+ EXPECT_EQ(rule.thread_equals, expected_rule.thread_equals);
EXPECT_EQ(rule.action, expected_rule.action);
}
@@ -128,24 +133,32 @@
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0x0f,
.module_equals{std::byte(123)},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
+ .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(4)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
}};
@@ -173,6 +186,7 @@
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{},
+ .thread_equals{},
};
for (const auto& rule : filter.rules()) {
VerifyRule(rule, empty_rule);
@@ -186,12 +200,18 @@
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
}};
const Filter filter_few_rules(
@@ -216,36 +236,50 @@
.level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
.any_flags_set = 0x0f,
.module_equals{std::byte(123)},
+ .thread_equals{std::byte('P'),
+ std::byte('O'),
+ std::byte('W'),
+ std::byte('E'),
+ std::byte('R')},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kInactive,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0xef,
.module_equals{},
+ .thread_equals{},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
.module_equals{std::byte(99)},
+ .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{std::byte(4)},
+ .thread_equals{std::byte('L'), std::byte('O'), std::byte('G')},
},
{
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = 0x1234,
- .module_equals{std::byte(99)},
+ .module_equals{std::byte('M'),
+ std::byte('0'),
+ std::byte('L'),
+ std::byte('O'),
+ std::byte('G')},
+ .thread_equals{},
},
}};
const Filter filter_extra_rules(
@@ -273,6 +307,7 @@
.any_flags_set = kSampleFlags,
.module_equals{kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals{kSampleThread.begin(), kSampleThread.end()},
},
// This rule catches all logs.
{
@@ -280,6 +315,7 @@
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals = {},
+ .thread_equals{},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
@@ -290,34 +326,34 @@
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
- EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_info_different.value()));
// Because the last rule catches all logs, the filter default action is not
@@ -329,15 +365,21 @@
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
- EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer);
+ EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
- EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_module.value()));
+
+ buffer.fill(std::byte(0));
+ const Result<ConstByteSpan> log_entry_same_thread =
+ EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
+ ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
+ EXPECT_TRUE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsKeepLogsWhenNoRuleMatches) {
@@ -349,6 +391,7 @@
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
@@ -362,48 +405,54 @@
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
- EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
- EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer);
+ EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
- EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
+
+ buffer.fill(std::byte(0));
+ const Result<ConstByteSpan> log_entry_same_thread =
+ EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
+ ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
+ EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsKeepLogsWhenRulesEmpty) {
@@ -416,48 +465,54 @@
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_debug =
EncodeLogEntry<PW_LOG_LEVEL_DEBUG, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_debug.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_debug.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_warn =
EncodeLogEntry<PW_LOG_LEVEL_WARN, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_warn.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_warn.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_error =
EncodeLogEntry<PW_LOG_LEVEL_ERROR, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_error.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_error.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_info_different =
- EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<PW_LOG_LEVEL_INFO, 0, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_info_different.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info_different.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_flags =
- EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer);
+ EncodeLogEntry<0, 0, kSampleFlags>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_flags.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_flags.value()));
buffer.fill(std::byte(0));
const Result<ConstByteSpan> log_entry_same_module =
- EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer);
+ EncodeLogEntry<0, kSampleModule, 0>(kSampleMessage, buffer, {});
ASSERT_EQ(log_entry_same_module.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_module.value()));
+
+ buffer.fill(std::byte(0));
+ const Result<ConstByteSpan> log_entry_same_thread =
+ EncodeLogEntry<0, 0, 0>(kSampleMessage, buffer, kSampleThread);
+ ASSERT_EQ(log_entry_same_thread.status(), OkStatus());
+ EXPECT_FALSE(filter.ShouldDropLog(log_entry_same_thread.value()));
}
TEST(FilterTest, FilterLogsFirstRuleWins) {
@@ -468,6 +523,7 @@
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
{
.action = Filter::Rule::Action::kDrop,
@@ -475,6 +531,7 @@
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<Filter::Rule, 2> rules_reversed{{
@@ -484,6 +541,7 @@
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
{
.action = Filter::Rule::Action::kKeep,
@@ -491,6 +549,7 @@
.any_flags_set = kSampleFlags,
.module_equals = {kSampleModuleLittleEndian.begin(),
kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
},
}};
const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
@@ -505,11 +564,157 @@
std::array<std::byte, 50> buffer;
const Result<ConstByteSpan> log_entry_info =
EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
- kSampleMessage, buffer);
+ kSampleMessage, buffer, kSampleThread);
ASSERT_EQ(log_entry_info.status(), OkStatus());
EXPECT_FALSE(filter.ShouldDropLog(log_entry_info.value()));
EXPECT_TRUE(filter_reverse_rules.ShouldDropLog(log_entry_info.value()));
}
+TEST(FilterTest, DropFilterRuleDueToThreadName) {
+ const std::array<std::byte, cfg::kMaxThreadNameBytes - 7> kDropThread = {
+ std::byte('L'), std::byte('O'), std::byte('G')};
+ const std::array<Filter::Rule, 2> rules{{
+ {
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kDropThread.begin(), kDropThread.end()},
+ },
+ {
+ .action = Filter::Rule::Action::kDrop,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
+ },
+ }};
+
+ const std::array<Filter::Rule, 2> drop_rule{{
+ {
+ .action = Filter::Rule::Action::kDrop,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kDropThread.begin(), kDropThread.end()},
+ },
+ }};
+
+ const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id1{
+ std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
+ // A filter's thread_equals name that does and does not match the log's thread
+ // name.
+ const Filter filter(filter_id1,
+ const_cast<std::array<Filter::Rule, 2>&>(rules));
+ const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id2{
+ std::byte(0), std::byte(0), std::byte(0), std::byte(2)};
+ // A filter's thread_equals name that does not match the log's thread name.
+ const Filter filter_with_unregistered_filter_rule(
+ filter_id2, const_cast<std::array<Filter::Rule, 2>&>(drop_rule));
+ std::array<std::byte, 50> buffer;
+ const Result<ConstByteSpan> log_entry_thread =
+ EncodeLogEntry<PW_LOG_LEVEL_INFO, kSampleModule, kSampleFlags>(
+ kSampleMessage, buffer, kSampleThread);
+ ASSERT_EQ(log_entry_thread.status(), OkStatus());
+ // Set filter rules to kDrop to showcase the output difference.
+ // Drop_rule not being dropped, while rules is dropped successfully.
+ EXPECT_TRUE(filter.ShouldDropLog(log_entry_thread.value()));
+ EXPECT_FALSE(filter_with_unregistered_filter_rule.ShouldDropLog(
+ log_entry_thread.value()));
+}
+
+TEST(FilterTest, UpdateFilterWithLargeThreadNamePasses) {
+ // Threads are limited to a size of kMaxThreadNameBytes.
+ // However, the excess bytes will not be in the updated rules.
+ const std::array<std::byte, cfg::kMaxThreadNameBytes + 1>
+ kThreadNameLongerThanAllowed = {
+ std::byte('L'),
+ std::byte('O'),
+ std::byte('C'),
+ std::byte('A'),
+ std::byte('L'),
+ std::byte('E'),
+ std::byte('G'),
+ std::byte('R'),
+ std::byte('E'),
+ std::byte('S'),
+ std::byte('S'),
+ };
+
+ const std::array<Filter::Rule, 2> rule{{
+ {
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kThreadNameLongerThanAllowed.begin(),
+ kThreadNameLongerThanAllowed.end()},
+ },
+ {
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
+ },
+ }};
+
+ const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
+ std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
+ Filter filter(filter_id, const_cast<std::array<Filter::Rule, 2>&>(rule));
+ std::byte buffer[256];
+ auto encode_result = EncodeFilter(filter, buffer);
+ ASSERT_EQ(encode_result.status(), OkStatus());
+ EXPECT_EQ(filter.UpdateRulesFromProto(encode_result.value()), OkStatus());
+ size_t i = 0;
+ for (const auto& rules : filter.rules()) {
+ VerifyRule(rules, rule[i++]);
+ }
+}
+
+TEST(FilterTest, UpdateFilterWithLargeThreadNameFails) {
+ const std::array<Filter::Rule, 1> rule_with_more_than_ten_bytes{{{
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = kSampleFlags,
+ .module_equals = {kSampleModuleLittleEndian.begin(),
+ kSampleModuleLittleEndian.end()},
+ .thread_equals = {kSampleThread.begin(), kSampleThread.end()},
+ }}};
+ const std::array<std::byte, cfg::kMaxFilterIdBytes> filter_id{
+ std::byte(0xba), std::byte(0x1d), std::byte(0xba), std::byte(0xb1)};
+ Filter filter(
+ filter_id,
+ const_cast<std::array<Filter::Rule, 1>&>(rule_with_more_than_ten_bytes));
+ std::byte buffer[256];
+ log::Filter::MemoryEncoder encoder(buffer);
+ {
+ std::array<const std::byte, cfg::kMaxThreadNameBytes + 1>
+ kThreadNameLongerThanAllowed = {
+ std::byte('L'),
+ std::byte('O'),
+ std::byte('C'),
+ std::byte('A'),
+ std::byte('L'),
+ std::byte('E'),
+ std::byte('G'),
+ std::byte('R'),
+ std::byte('E'),
+ std::byte('S'),
+ std::byte('S'),
+ };
+ // Stream encoder writes to the buffer when it goes out of scope.
+ log::FilterRule::StreamEncoder rule_encoder = encoder.GetRuleEncoder();
+ ASSERT_EQ(rule_encoder.WriteThreadEquals(kThreadNameLongerThanAllowed),
+ OkStatus());
+ }
+ EXPECT_EQ(filter.UpdateRulesFromProto(ConstByteSpan(encoder)),
+ Status::InvalidArgument());
+}
} // namespace
} // namespace pw::log_rpc
diff --git a/pw_log_rpc/log_service_test.cc b/pw_log_rpc/log_service_test.cc
index 1072ec5..2527228 100644
--- a/pw_log_rpc/log_service_test.cc
+++ b/pw_log_rpc/log_service_test.cc
@@ -60,6 +60,11 @@
static_assert(sizeof(kLongMessage) + RpcLogDrain::kMinEntrySizeWithoutPayload >
RpcLogDrain::kMinEntryBufferSize);
std::array<std::byte, 1> rpc_request_buffer;
+const std::array<std::byte, 5> kSampleThread = {std::byte('M'),
+ std::byte('0'),
+ std::byte('L'),
+ std::byte('O'),
+ std::byte('G')};
constexpr auto kSampleMetadata =
log_tokenized::Metadata::Set<PW_LOG_LEVEL_INFO, 123, 0x03, __LINE__>();
constexpr auto kDropMessageMetadata =
@@ -82,20 +87,22 @@
void AddLogEntries(size_t log_count,
std::string_view message,
log_tokenized::Metadata metadata,
- int64_t timestamp) {
+ int64_t timestamp,
+ ConstByteSpan thread) {
for (size_t i = 0; i < log_count; ++i) {
- ASSERT_TRUE(AddLogEntry(message, metadata, timestamp).ok());
+ ASSERT_TRUE(AddLogEntry(message, metadata, timestamp, thread).ok());
}
}
StatusWithSize AddLogEntry(std::string_view message,
log_tokenized::Metadata metadata,
- int64_t timestamp) {
+ int64_t timestamp,
+ ConstByteSpan thread) {
Result<ConstByteSpan> encoded_log_result =
log::EncodeTokenizedLog(metadata,
std::as_bytes(std::span(message)),
timestamp,
- /*thread_name=*/{},
+ thread,
entry_encode_buffer_);
PW_TRY_WITH_SIZE(encoded_log_result.status());
multisink_.HandleEntry(encoded_log_result.value());
@@ -203,7 +210,7 @@
// Add log entries.
const size_t total_entries = 10;
- AddLogEntries(total_entries, kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntries(total_entries, kMessage, kSampleMetadata, kSampleTimestamp, {});
// Request logs.
context.call(rpc_request_buffer);
@@ -221,10 +228,11 @@
// Verify data in responses.
Vector<TestLogEntry, total_entries> expected_messages;
for (size_t i = 0; i < total_entries; ++i) {
- expected_messages.push_back({.metadata = kSampleMetadata,
- .timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(
- std::span(std::string_view(kMessage)))});
+ expected_messages.push_back(
+ {.metadata = kSampleMetadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = {}});
}
size_t entries_found = 0;
uint32_t drop_count_found = 0;
@@ -252,13 +260,17 @@
const uint32_t total_drop_count = 2;
// Force a drop entry in between entries.
- AddLogEntries(
- entries_before_drop, kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntries(entries_before_drop,
+ kMessage,
+ kSampleMetadata,
+ kSampleTimestamp,
+ kSampleThread);
multisink_.HandleDropped(total_drop_count);
AddLogEntries(total_entries - entries_before_drop,
kMessage,
kSampleMetadata,
- kSampleTimestamp);
+ kSampleTimestamp,
+ kSampleThread);
// Request logs.
context.call(rpc_request_buffer);
@@ -271,21 +283,24 @@
Vector<TestLogEntry, total_entries + 1> expected_messages;
size_t i = 0;
for (; i < entries_before_drop; ++i) {
- expected_messages.push_back({.metadata = kSampleMetadata,
- .timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(
- std::span(std::string_view(kMessage)))});
+ expected_messages.push_back(
+ {.metadata = kSampleMetadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
}
expected_messages.push_back(
{.metadata = kDropMessageMetadata,
.dropped = total_drop_count,
.tokenized_data = std::as_bytes(
- std::span(std::string_view(RpcLogDrain::kIngressErrorMessage)))});
+ std::span(std::string_view(RpcLogDrain::kIngressErrorMessage))),
+ .thread = {}});
for (; i < total_entries; ++i) {
- expected_messages.push_back({.metadata = kSampleMetadata,
- .timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(
- std::span(std::string_view(kMessage)))});
+ expected_messages.push_back(
+ {.metadata = kSampleMetadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
}
// Verify data in responses.
@@ -320,14 +335,16 @@
for (size_t i = 1; i < total_entries; ++i) {
ASSERT_EQ(
OkStatus(),
- AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp).status());
+ AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp, kSampleThread)
+ .status());
multisink_.HandleDropped(1);
}
// Add message that won't be filtered out.
constexpr auto metadata =
log_tokenized::Metadata::Set<PW_LOG_LEVEL_DEBUG, 0, 0, __LINE__>();
ASSERT_EQ(OkStatus(),
- AddLogEntry(kMessage, metadata, kSampleTimestamp).status());
+ AddLogEntry(kMessage, metadata, kSampleTimestamp, kSampleThread)
+ .status());
// Request logs.
context.call(rpc_request_buffer);
@@ -342,11 +359,13 @@
{.metadata = kDropMessageMetadata,
.dropped = total_drop_count,
.tokenized_data = std::as_bytes(
- std::span(std::string_view(RpcLogDrain::kIngressErrorMessage)))});
+ std::span(std::string_view(RpcLogDrain::kIngressErrorMessage))),
+ .thread = {}});
expected_messages.push_back(
{.metadata = metadata,
.timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage)))});
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
// Verify data in responses.
size_t entries_found = 0;
@@ -374,10 +393,15 @@
// one, since drop count messages are only sent when a log entry can be sent.
const size_t total_entries = 5;
const uint32_t total_drop_count = total_entries - 1;
- AddLogEntries(
- total_drop_count, kLongMessage, kSampleMetadata, kSampleTimestamp);
- EXPECT_EQ(OkStatus(),
- AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp).status());
+ AddLogEntries(total_drop_count,
+ kLongMessage,
+ kSampleMetadata,
+ kSampleTimestamp,
+ kSampleThread);
+ EXPECT_EQ(
+ OkStatus(),
+ AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp, kSampleThread)
+ .status());
// Request logs.
context.call(rpc_request_buffer);
@@ -391,11 +415,13 @@
{.metadata = kDropMessageMetadata,
.dropped = total_drop_count,
.tokenized_data = std::as_bytes(std::span(
- std::string_view(RpcLogDrain::kSmallStackBufferErrorMessage)))});
+ std::string_view(RpcLogDrain::kSmallStackBufferErrorMessage))),
+ .thread = {}});
expected_messages.push_back(
{.metadata = kSampleMetadata,
.timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage)))});
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
// Expect one drop message with the total drop count, and the only message
// that fits the buffer.
@@ -421,7 +447,11 @@
// Add log entries.
const size_t total_entries = 5;
- AddLogEntries(total_entries, kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntries(total_entries,
+ kMessage,
+ kSampleMetadata,
+ kSampleTimestamp,
+ kSampleThread);
// Request logs.
context.call(rpc_request_buffer);
EXPECT_EQ(detached_drain.Close(), OkStatus());
@@ -438,6 +468,7 @@
(1 << PW_LOG_TOKENIZED_LINE_BITS) - 1>(),
.timestamp = std::numeric_limits<int64_t>::max(),
.tokenized_data = std::as_bytes(std::span(kMessage)),
+ .thread = kSampleThread,
};
// Add entry to multisink.
@@ -455,6 +486,7 @@
ASSERT_EQ(
encoder.WriteModule(std::as_bytes(std::span(&little_endian_module, 1))),
OkStatus());
+ ASSERT_EQ(encoder.WriteThread(expected_entry.thread), OkStatus());
ASSERT_EQ(encoder.status(), OkStatus());
multisink_.HandleEntry(encoder);
@@ -493,7 +525,7 @@
// Add as many entries needed to have multiple packets send.
StatusWithSize status =
- AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp, kSampleThread);
ASSERT_TRUE(status.ok());
const uint32_t max_messages_per_response =
@@ -504,7 +536,11 @@
const size_t max_entries = 50;
// Check we can test all these entries.
ASSERT_GE(max_entries, total_entries);
- AddLogEntries(total_entries - 1, kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntries(total_entries - 1,
+ kMessage,
+ kSampleMetadata,
+ kSampleTimestamp,
+ kSampleThread);
// Interrupt log stream with an error.
const uint32_t successful_packets_sent = packets_sent / 2;
@@ -524,10 +560,11 @@
// Verify data in responses.
Vector<TestLogEntry, max_entries> expected_messages;
for (size_t i = 0; i < total_entries; ++i) {
- expected_messages.push_back({.metadata = kSampleMetadata,
- .timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(
- std::span(std::string_view(kMessage)))});
+ expected_messages.push_back(
+ {.metadata = kSampleMetadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
}
size_t entries_found = 0;
uint32_t drop_count_found = 0;
@@ -568,8 +605,8 @@
expected_messages_after_reset.push_back(
{.metadata = kSampleMetadata,
.timestamp = kSampleTimestamp,
- .tokenized_data =
- std::as_bytes(std::span(std::string_view(kMessage)))});
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
}
size_t entries_found_after_reset = 0;
@@ -600,7 +637,7 @@
// Add as many entries needed to have multiple packets send.
StatusWithSize status =
- AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntry(kMessage, kSampleMetadata, kSampleTimestamp, kSampleThread);
ASSERT_TRUE(status.ok());
const uint32_t max_messages_per_response =
@@ -611,7 +648,11 @@
const size_t max_entries = 50;
// Check we can test all these entries.
ASSERT_GT(max_entries, total_entries);
- AddLogEntries(total_entries - 1, kMessage, kSampleMetadata, kSampleTimestamp);
+ AddLogEntries(total_entries - 1,
+ kMessage,
+ kSampleMetadata,
+ kSampleTimestamp,
+ kSampleThread);
// Interrupt log stream with an error.
const uint32_t error_on_packet_count = packets_sent / 2;
@@ -640,10 +681,11 @@
const uint32_t total_drop_count = total_entries - entries_found;
Vector<TestLogEntry, max_entries> expected_messages;
for (size_t i = 0; i < entries_found; ++i) {
- expected_messages.push_back({.metadata = kSampleMetadata,
- .timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(
- std::span(std::string_view(kMessage)))});
+ expected_messages.push_back(
+ {.metadata = kSampleMetadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread});
}
entries_found = 0;
@@ -687,37 +729,68 @@
const uint32_t module = 0xcafe;
const uint32_t flags = 0x02;
const uint32_t line_number = 100;
+ const std::array<std::byte, 3> kNewThread = {
+ std::byte('A'), std::byte('P'), std::byte('P')};
+ const std::array<std::byte, 3> kInvalidThread = {
+ std::byte('C'), std::byte('D'), std::byte('C')};
const auto debug_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_DEBUG, module, flags, line_number>();
- ASSERT_TRUE(AddLogEntry(kMessage, debug_metadata, kSampleTimestamp).ok());
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, debug_metadata, kSampleTimestamp, kSampleThread)
+ .ok());
const auto info_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_INFO, module, flags, line_number>();
- ASSERT_TRUE(AddLogEntry(kMessage, info_metadata, kSampleTimestamp).ok());
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, info_metadata, kSampleTimestamp, kSampleThread)
+ .ok());
const auto warn_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_WARN, module, flags, line_number>();
- ASSERT_TRUE(AddLogEntry(kMessage, warn_metadata, kSampleTimestamp).ok());
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, warn_metadata, kSampleTimestamp, kSampleThread)
+ .ok());
const auto error_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_ERROR, module, flags, line_number>();
- ASSERT_TRUE(AddLogEntry(kMessage, error_metadata, kSampleTimestamp).ok());
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, error_metadata, kSampleTimestamp, kNewThread).ok());
const auto different_flags_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_ERROR, module, 0x01, line_number>();
ASSERT_TRUE(
- AddLogEntry(kMessage, different_flags_metadata, kSampleTimestamp).ok());
+ AddLogEntry(
+ kMessage, different_flags_metadata, kSampleTimestamp, kSampleThread)
+ .ok());
const auto different_module_metadata = log_tokenized::Metadata::
Set<PW_LOG_LEVEL_ERROR, 0xabcd, flags, line_number>();
ASSERT_TRUE(
- AddLogEntry(kMessage, different_module_metadata, kSampleTimestamp).ok());
+ AddLogEntry(
+ kMessage, different_module_metadata, kSampleTimestamp, kSampleThread)
+ .ok());
+ const auto second_info_metadata = log_tokenized::Metadata::
+ Set<PW_LOG_LEVEL_INFO, module, flags, line_number>();
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, second_info_metadata, kSampleTimestamp, kNewThread)
+ .ok());
+ const auto metadata = log_tokenized::Metadata::
+ Set<PW_LOG_LEVEL_INFO, module, flags, line_number>();
+ ASSERT_TRUE(
+ AddLogEntry(kMessage, metadata, kSampleTimestamp, kInvalidThread).ok());
- Vector<TestLogEntry, 3> expected_messages{
+ Vector<TestLogEntry, 4> expected_messages{
{.metadata = info_metadata,
.timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage)))},
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread},
{.metadata = warn_metadata,
.timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage)))},
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kSampleThread},
{.metadata = error_metadata,
.timestamp = kSampleTimestamp,
- .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage)))},
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kNewThread},
+ {.metadata = second_info_metadata,
+ .timestamp = kSampleTimestamp,
+ .tokenized_data = std::as_bytes(std::span(std::string_view(kMessage))),
+ .thread = kNewThread},
};
// Set up filter rules for drain at drains_[1].
@@ -731,13 +804,26 @@
.action = Filter::Rule::Action::kKeep,
.level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
.any_flags_set = flags,
- .module_equals{module_little_endian.begin(), module_little_endian.end()}};
+ .module_equals{module_little_endian.begin(), module_little_endian.end()},
+ .thread_equals{kSampleThread.begin(), kSampleThread.end()}};
rules2_[1] = {
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::DEBUG_LEVEL,
+ .any_flags_set = flags,
+ .module_equals{module_little_endian.begin(), module_little_endian.end()},
+ .thread_equals{kNewThread.begin(), kNewThread.end()}};
+ rules2_[2] = {
.action = Filter::Rule::Action::kDrop,
.level_greater_than_or_equal = log::FilterRule::Level::ANY_LEVEL,
.any_flags_set = 0,
.module_equals{},
- };
+ .thread_equals{}};
+ rules2_[3] = {
+ .action = Filter::Rule::Action::kKeep,
+ .level_greater_than_or_equal = log::FilterRule::Level::INFO_LEVEL,
+ .any_flags_set = flags,
+ .module_equals{module_little_endian.begin(), module_little_endian.end()},
+ .thread_equals{kNewThread.begin(), kNewThread.end()}};
// Request logs.
LOG_SERVICE_METHOD_CONTEXT context(drain_map_);
@@ -755,7 +841,7 @@
entries_found,
drop_count_found);
}
- EXPECT_EQ(entries_found, 3u);
+ EXPECT_EQ(entries_found, 4u);
EXPECT_EQ(drop_count_found, 0u);
}
diff --git a/pw_log_rpc/public/pw_log_rpc/internal/config.h b/pw_log_rpc/public/pw_log_rpc/internal/config.h
index 9fbcfbf..0b3bcdc 100644
--- a/pw_log_rpc/public/pw_log_rpc/internal/config.h
+++ b/pw_log_rpc/public/pw_log_rpc/internal/config.h
@@ -25,6 +25,14 @@
#define PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_MODULE_NAME_SIZE 4
#endif // PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_MODULE_NAME_SIZE
+// Log filter threads are optionally tokenized,thus their backing on-device
+// container can have different sizes. A token may be represented by a 32-bit
+// integer, usually two. Default the max thread size to
+// 10 bytes.
+#ifndef PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_THREAD_NAME_SIZE
+#define PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_THREAD_NAME_SIZE 10
+#endif // PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_THREAD_NAME_SIZE
+
// Log filter IDs are optionally tokenized, and thus their backing on-device
// container can have different sizes. A token may be represented by a 32-bit
// integer (though it is usually 2 bytes). Default the max module name size to
@@ -80,4 +88,7 @@
inline constexpr size_t kMaxFilterIdBytes =
PW_LOG_RPC_CONFIG_MAX_FILTER_ID_SIZE;
-} // namespace pw::log_rpc::cfg
+
+inline constexpr size_t kMaxThreadNameBytes =
+ PW_LOG_RPC_CONFIG_MAX_FILTER_RULE_THREAD_NAME_SIZE;
+} // namespace pw::log_rpc::cfg
\ No newline at end of file
diff --git a/pw_log_rpc/public/pw_log_rpc/log_filter.h b/pw_log_rpc/public/pw_log_rpc/log_filter.h
index 56e01f7..e16bb0a 100644
--- a/pw_log_rpc/public/pw_log_rpc/log_filter.h
+++ b/pw_log_rpc/public/pw_log_rpc/log_filter.h
@@ -50,6 +50,9 @@
// Checks if the log entry module equals this value when not empty.
Vector<std::byte, cfg::kMaxModuleNameBytes> module_equals{};
+
+ // Checks if the log entry thread equals this value when not empty.
+ Vector<std::byte, cfg::kMaxThreadNameBytes> thread_equals{};
};
Filter(std::span<const std::byte> id, std::span<Rule> rules) : rules_(rules) {