blob: 95fc1e5e269e8db14a96131bec39bd9014ff6837 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_log_rpc/log_filter.h"
#include "pw_log/levels.h"
#include "pw_protobuf/decoder.h"
#include "pw_status/try.h"
namespace pw::log_rpc {
namespace {
// Returns true if the provided log parameters match the given filter rule.
bool IsRuleMet(const Filter::Rule& rule,
uint32_t level,
ConstByteSpan module,
uint32_t flags,
ConstByteSpan thread) {
if (level < static_cast<uint32_t>(rule.level_greater_than_or_equal)) {
return false;
}
if ((rule.any_flags_set != 0) && ((flags & rule.any_flags_set) == 0)) {
return false;
}
if (!rule.module_equals.empty() && !std::equal(module.begin(),
module.end(),
rule.module_equals.begin(),
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;
}
} // namespace
Status Filter::UpdateRulesFromProto(ConstByteSpan buffer) {
if (rules_.empty()) {
return Status::FailedPrecondition();
}
// Reset rules.
for (auto& rule : rules_) {
rule = {};
}
protobuf::Decoder decoder(buffer);
Status status;
for (size_t i = 0; (i < rules_.size()) && (status = decoder.Next()).ok();
++i) {
ConstByteSpan rule_buffer;
PW_TRY(decoder.ReadBytes(&rule_buffer));
protobuf::Decoder rule_decoder(rule_buffer);
while ((status = rule_decoder.Next()).ok()) {
switch (
static_cast<log::FilterRule::Fields>(rule_decoder.FieldNumber())) {
case log::FilterRule::Fields::LEVEL_GREATER_THAN_OR_EQUAL:
PW_TRY(rule_decoder.ReadUint32(reinterpret_cast<uint32_t*>(
&rules_[i].level_greater_than_or_equal)));
break;
case log::FilterRule::Fields::MODULE_EQUALS: {
ConstByteSpan module;
PW_TRY(rule_decoder.ReadBytes(&module));
if (module.size() > rules_[i].module_equals.max_size()) {
return Status::InvalidArgument();
}
rules_[i].module_equals.assign(module.begin(), module.end());
} break;
case log::FilterRule::Fields::ANY_FLAGS_SET:
PW_TRY(rule_decoder.ReadUint32(&rules_[i].any_flags_set));
break;
case log::FilterRule::Fields::ACTION:
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;
}
}
}
return status.IsOutOfRange() ? OkStatus() : status;
}
bool Filter::ShouldDropLog(ConstByteSpan entry) const {
if (rules_.empty()) {
return false;
}
uint32_t log_level = 0;
ConstByteSpan log_module;
ConstByteSpan log_thread;
uint32_t log_flags = 0;
protobuf::Decoder decoder(entry);
while (decoder.Next().ok()) {
const auto field_num =
static_cast<log::LogEntry::Fields>(decoder.FieldNumber());
if (field_num == log::LogEntry::Fields::LINE_LEVEL) {
if (decoder.ReadUint32(&log_level).ok()) {
log_level &= PW_LOG_LEVEL_BITMASK;
}
} else if (field_num == log::LogEntry::Fields::MODULE) {
decoder.ReadBytes(&log_module).IgnoreError();
} else if (field_num == log::LogEntry::Fields::FLAGS) {
decoder.ReadUint32(&log_flags).IgnoreError();
} else if (field_num == log::LogEntry::Fields::THREAD) {
decoder.ReadBytes(&log_thread).IgnoreError();
}
}
// Follow the action of the first rule whose condition is met.
for (const auto& rule : rules_) {
if (rule.action == Filter::Rule::Action::kInactive) {
continue;
}
if (IsRuleMet(rule, log_level, log_module, log_flags, log_thread)) {
return rule.action == Filter::Rule::Action::kDrop;
}
}
return false;
}
} // namespace pw::log_rpc