Add event severity filtering to text loggers with shared common code#5343
Add event severity filtering to text loggers with shared common code#5343lestarch-autobot wants to merge 9 commits into
Conversation
Closes nasa#2817 - Create Svc::EventSeverityFilter utility class in Svc/Types/EventSeverityFilter providing shared per-severity enabled/disabled state (FATAL never filtered) - Integrate EventSeverityFilter into PassiveConsoleTextLogger, ActiveTextLogger, and EventManager, replacing inline/hardcoded filtering logic - Add configurable severity filter defaults to ActiveTextLoggerCfg.hpp and PassiveTextLoggerCfg.hpp (all severities enabled by default) - Add setSeverityFilter() API to both text loggers for runtime configuration - Remove hardcoded DIAGNOSTIC filter from ActiveTextLogger - Add unit tests for EventSeverityFilter (5 tests) and ActiveTextLogger severity filtering (2 tests) - All existing EventManager and ActiveTextLogger tests pass unchanged Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
…ity assertion - Set ACTIVE_TEXT_LOGGER_FILTER_DIAGNOSTIC_DEFAULT = false to match prior hardcoded behavior (DIAGNOSTIC was always dropped in ActiveTextLogger) - Add FW_ASSERT(severity.isValid()) in EventManager::LogRecv_handler to catch invalid/corrupted severity values before filtering Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
Add explicit return after FW_ASSERT for invalid severity values in EventManager::LogRecv_handler, matching the old default-case behavior of assert-then-return. Ensures invalid events are dropped regardless of FW_ASSERT configuration. Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
…ilter Add fromIndex() static method and SEVERITY_ORDER table to EventSeverityFilter, replacing the private filterSeverityToLogSeverity helper in EventManager. The FilterSeverity enum values (0-5) map directly to filter array indices, so EventManager now uses EventSeverityFilter::fromIndex() for the conversion. Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
- setFilter() takes Fw::Enabled instead of bool - isEnabled() returns Fw::Enabled instead of bool - toIndex()/fromIndex() return Fw::Success instead of bool - Internal m_enabled[] array stores Fw::Enabled - Config defaults use Fw::Enabled in namespaced constants - All tests updated to use enum comparisons Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
| return Fw::Success::SUCCESS; | ||
| } | ||
|
|
||
| Fw::Success EventSeverityFilter::toIndex(Fw::LogSeverity severity, FwSizeType& index) { |
| } | ||
|
|
||
| void ActiveTextLogger::setSeverityFilter(Fw::LogSeverity severity, Fw::Enabled enabled) { | ||
| this->m_severityFilter.setFilter(severity, enabled); |
| } | ||
|
|
||
| void ActiveTextLogger::setSeverityFilter(Fw::LogSeverity severity, Fw::Enabled enabled) { | ||
| this->m_severityFilter.setFilter(severity, enabled); |
| Enabled filterEnable) { | ||
| this->m_filterState[filterLevel.e].enabled = filterEnable; | ||
| Fw::LogSeverity logSeverity; | ||
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); |
| } | ||
|
|
||
| void ConsoleTextLoggerImpl::setSeverityFilter(Fw::LogSeverity severity, Fw::Enabled enabled) { | ||
| this->m_severityFilter.setFilter(severity, enabled); |
| } | ||
|
|
||
| void ConsoleTextLoggerImpl::setSeverityFilter(Fw::LogSeverity severity, Fw::Enabled enabled) { | ||
| this->m_severityFilter.setFilter(severity, enabled); |
| void EventSeverityFilter::setFilter(Fw::LogSeverity severity, Fw::Enabled enabled) { | ||
| FwSizeType index = 0; | ||
| if (toIndex(severity, index) == Fw::Success::SUCCESS) { | ||
| this->m_enabled[index] = enabled; |
| if (index >= NUM_FILTER_LEVELS) { | ||
| return Fw::Success::FAILURE; | ||
| } | ||
| severity = SEVERITY_ORDER[index]; |
Coverage report — base
|
| Module | Line | Δ | Function | Δ | Branch | Δ |
|---|---|---|---|---|---|---|
Os/Posix |
62.40 | -0.27 | 84.21 | +0.00 | 44.10 | -0.24 |
Os |
17.98 | -0.04 | 19.40 | +0.00 | 14.47 | +0.08 |
Svc/ActiveTextLogger |
84.35 | +5.30 | 90.91 | +0.91 | 79.55 | +8.32 |
New modules
| Module | Line | Function | Branch |
|---|---|---|---|
Svc/PassiveConsoleTextLogger |
57.41 | 80.00 | 70.27 |
Svc/Types/EventSeverityFilter |
89.13 | 83.33 | 77.42 |
Modules without UTs
CFDP/Checksum/GTest, Drv/ByteStreamDriverModel, Drv/Interfaces, Drv/LinuxGpioDriver, Drv/LinuxI2cDriver, Drv/LinuxSpiDriver, Drv/LinuxUartDriver, Drv/Ports, Drv/Ports/DataTypes, FppTestProject/FppTest/interfaces, FppTestProject/FppTest/topology/async, FppTestProject/FppTest/topology/components/Comp, FppTestProject/FppTest/topology/components/Framework, FppTestProject/FppTest/topology/components/Receiver, FppTestProject/FppTest/topology/components/Sender, FppTestProject/FppTest/topology/guarded, FppTestProject/FppTest/topology/ports, FppTestProject/FppTest/topology/sync, FppTestProject/FppTest/topology/top_ports, FppTestProject/FppTest/topology/types, Fw/Cmd, Fw/Com, Fw/Comp, Fw/FilePacket/GTest, Fw/Fpy, Fw/Interfaces, Fw/Obj, Fw/Port, Fw/Ports/CompletionStatus, Fw/Ports/Ready, Fw/Ports/Signal, Fw/Ports/SuccessCondition, Fw/Prm, Fw/SerializableFile/test/TestSerializable, Fw/Sm, Fw/Test, Fw/Types/GTest, Os/Models, Svc/Cycle, Svc/DpPorts, Svc/Fatal, Svc/FatalHandler, Svc/FileDownlinkPorts, Svc/FprimeProtocol, Svc/Interfaces, Svc/Ping, Svc/PolyIf, Svc/Ports/CommsPorts, Svc/Ports/FilePorts, Svc/Ports/OsTimeEpoch, Svc/Ports/TlmPacketizerPorts, Svc/Ports/VersionPorts, Svc/Sched, Svc/Seq, Svc/Subtopologies/CdhCore, Svc/Subtopologies/ComCcsds, Svc/Subtopologies/ComFprime, Svc/Subtopologies/ComLoggerTee, Svc/Subtopologies/DataProducts, Svc/Subtopologies/FileHandling, Svc/Types/TlmPacketizerTypes, Svc/WatchDog, TestDeploymentsProject/Ref/PingReceiver, TestDeploymentsProject/Ref/RecvBuffApp, TestDeploymentsProject/Ref/SendBuffApp, TestDeploymentsProject/Ref/Top, TestDeploymentsProject/Ref/TypeDemo, cmake/test/data/TestDeployment/TestBuildAutocoder, cmake/test/data/TestDeployment/TestChainedAutocoder, cmake/test/data/TestDeployment/TestHeaderAutocoder, cmake/test/data/TestDeployment/TestTargetAutocoder, cmake/test/data/test-fprime-library/TestLibrary/TestComponent, cmake/test/data/test-fprime-library2/TestLibrary2/TestComponent
Per review: only the index-lookup return values (toIndex, fromIndex) should use Fw::Success enumerator. The on/off filter state (setFilter, isEnabled, config defaults) correctly uses bool for true/false semantics. Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
| //! Set the filter state for a severity level | ||
| //! \param severity The severity level to configure (FATAL is ignored) | ||
| //! \param enabled true = events pass through, false = events are dropped | ||
| void setSeverityFilter(Fw::LogSeverity severity, bool enabled); |
There was a problem hiding this comment.
[Documentation] must fix Svc/ActiveTextLogger/docs/sdd.md does not document the new severity filtering capability.
The SDD's §3.2 Functional Description and §2 Requirements make no mention of event filtering. This PR adds a public setSeverityFilter() API and configurable per-severity defaults — a behavioral change that the SDD must reflect. Add a §3.2.2 (or similar) describing severity filtering, the config defaults in ActiveTextLoggerCfg.hpp, and the FATAL-never-filtered invariant.
| //! Set the filter state for a severity level | ||
| //! \param severity The severity level to configure (FATAL is ignored) | ||
| //! \param enabled true = events pass through, false = events are dropped | ||
| void setSeverityFilter(Fw::LogSeverity severity, bool enabled); |
There was a problem hiding this comment.
[Documentation] must fix Svc/PassiveConsoleTextLogger/docs/sdd.md does not document the new severity filtering capability.
The SDD currently describes the component as simply printing events to stdout with no mention of filtering. This PR adds setSeverityFilter() and configurable defaults — the SDD must be updated to describe this behavior, the config surface in PassiveTextLoggerCfg.hpp, and the FATAL-never-filtered invariant.
| //! | ||
| //! Provides per-severity enabled/disabled state for event filtering. | ||
| //! FATAL events are never filtered. | ||
| class EventSeverityFilter { |
There was a problem hiding this comment.
[Documentation] suggestion New utility class Svc::EventSeverityFilter has no accompanying documentation (docs/sdd.md or README).
This is a shared framework-level utility consumed by three components. A minimal SDD (or at least a docs/sdd.md) describing its purpose, public API, the canonical severity ordering, and the FATAL-never-filtered invariant would help downstream maintainers.
| class EventSeverityFilter { | |
| //! \class EventSeverityFilter | |
| //! \brief Common severity-based event filtering utility | |
| //! \see docs/sdd.md (TODO: create SDD for this utility) |
| const Fw::LogSeverity& severity, | ||
| Fw::TextLogString& text) { | ||
| // Check severity filter | ||
| if (this->m_severityFilter.isFiltered(severity)) { |
There was a problem hiding this comment.
[Documentation] suggestion docs/reference/system-functional/event-management.md §Text Logging omits severity filtering for text loggers.
The reference doc's "Text Logging" section describes the two text loggers without mentioning that they now support per-severity filtering. Add a sentence noting that both text loggers support configurable severity filters (with FATAL always passing through).
| if (this->m_severityFilter.isFiltered(severity)) { | |
| // Check severity filter (see also docs/reference/system-functional/event-management.md) | |
| if (this->m_severityFilter.isFiltered(severity)) { |
| Fw::LogSeverity logSeverity; | ||
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); |
There was a problem hiding this comment.
[Design] could fix Implicit coupling between EventSeverityFilter::SEVERITY_ORDER indices and FilterSeverity FPP enum values.
The shared utility's fromIndex()/SEVERITY_ORDER hard-codes an index-to-severity mapping that must match the FPP-defined FilterSeverity enum values (0–5). Before this PR the coupling was local to EventManager.cpp (next to the .fpp); now it lives in a separate shared utility. A static_assert block here would lock the correspondence at compile time and prevent silent breakage if the FPP enum is ever reordered.
| Fw::LogSeverity logSeverity; | |
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | |
| Fw::LogSeverity logSeverity; | |
| // Verify FilterSeverity enum values match EventSeverityFilter index ordering | |
| static_assert(static_cast<FwSizeType>(FilterSeverity::WARNING_HI) == 0, "FilterSeverity ordering mismatch"); | |
| static_assert(static_cast<FwSizeType>(FilterSeverity::DIAGNOSTIC) == 5, "FilterSeverity ordering mismatch"); | |
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); |
| // Check severity filter | ||
| if (this->m_severityFilter.isFiltered(severity)) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
[Test Quality] must fix New severity filtering behavior in PassiveConsoleTextLogger::TextLogger_handler has no unit test coverage.
This PR adds a configurable EventSeverityFilter to the handler, but PassiveConsoleTextLogger has no test/ut/ directory at all. The sibling ActiveTextLogger received two new test cases (testSeverityFilter, testSeverityFilterDiagnosticDisabled) that exercise exactly this path — analogous coverage is needed here to verify events are correctly passed or dropped by severity.
| FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(filterLevel.e)); | ||
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | ||
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); |
There was a problem hiding this comment.
[Security] must fix Ground-reachable FW_ASSERT on command argument filterLevel.
filterLevel is a ground-controlled command argument. fromIndex derives status from it; the assert fires if it returns FAILURE. Although FPP enum deserialization constrains the value, F Prime's defensive pattern for command handlers is to validate and return VALIDATION_ERROR rather than assert on ground-derived data.
| FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(filterLevel.e)); | |
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); | |
| if (status != Fw::Success::SUCCESS) { | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| return; | |
| } | |
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); |
| Fw::LogSeverity logSeverity; | ||
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | ||
| FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(filterLevel.e)); | ||
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | ||
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); |
There was a problem hiding this comment.
[C++ Design] must fix CPP-4 (FW_ASSERT on untrusted input): FW_ASSERT on the result of fromIndex() in a ground command handler. filterLevel.e is a ground command argument; if the FilterSeverity enum ever diverges from EventSeverityFilter's index space, this assert fires on a ground-reachable path.
Replace the assert with a validation check and return VALIDATION_ERROR so the command is rejected gracefully rather than crashing.
| Fw::LogSeverity logSeverity; | |
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | |
| FW_ASSERT(status == Fw::Success::SUCCESS, static_cast<FwAssertArgType>(filterLevel.e)); | |
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); | |
| Fw::LogSeverity logSeverity; | |
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | |
| if (status != Fw::Success::SUCCESS) { | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | |
| return; | |
| } | |
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); | |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); |
cc @LeStarch @thomas-bc — low-confidence finding, please confirm. FPP autocoder validates enum ranges before dispatch, so filterLevel.e will be in-range for a correctly-built system; the assert is effectively a programmer-invariant check. However, defensive coding practice favors graceful error handling over asserts on command paths.
| //! Configure component with event ID filters | ||
| void configure(const FwEventIdType* filteredIds, FwSizeType count); | ||
|
|
||
| //! Set the filter state for a severity level |
There was a problem hiding this comment.
[C++ Design] future work CPP-18 (non-explicit single-argument constructor): ConsoleTextLoggerImpl(const char* compName) at line 13 is not marked explicit. Single-argument constructors should be explicit to prevent implicit conversions.
(Anchored above the offending line; the diff does not include line 13.)
Note: ActiveTextLogger already marks its constructor explicit; this class should follow the same convention.
lestarch-autobot
left a comment
There was a problem hiding this comment.
Automated review summary (run 1)
Per-agent results
| Agent | must fix | suggestion | could fix | future work | outstanding | Verdict |
|---|---|---|---|---|---|---|
| Security Vulnerabilities | 1 | 0 | 0 | 0 | 1 | No-Go |
| Supply Chain / Runner Safety | 0 | 0 | 0 | 0 | 0 | Go |
| F Prime C/C++ Design | 1 | 0 | 0 | 1 | 2 | No-Go |
| Documentation Currency | 2 | 2 | 0 | 0 | 4 | No-Go |
| Design | 0 | 0 | 1 | 0 | 0 | Go |
| Architecture | 0 | 0 | 0 | 0 | 0 | Go |
| Test Quality | 1 | 0 | 0 | 0 | 1 | No-Go |
| CI safety | — | — | — | — | — | Go |
| Totals | 5 | 2 | 1 | 1 | 8 | No-Go |
Supply-chain surfaces
| Surface | Outstanding |
|---|---|
| Dependencies | clean |
| Vendored / submodule | clean |
| Build / test infrastructure | clean |
| Workflows / actions / scripts | clean |
| Generator output | clean |
| Prompt-injection | clean |
| Review-system integrity | clean |
Outstanding must-fix items (5)
Security Vulnerabilities
- Ground-reachable
FW_ASSERTon command argumentfilterLevel— link
F Prime C/C++ Design
- CPP-4:
FW_ASSERTon untrusted input fromfromIndex()in a ground command path — link
Documentation Currency
Svc/ActiveTextLogger/docs/sdd.mddoes not document the new severity filtering capability — linkSvc/PassiveConsoleTextLogger/docs/sdd.mddoes not document the new severity filtering capability — link
Test Quality
- New severity filtering in
PassiveConsoleTextLogger::TextLogger_handlerhas no unit tests — link
Merge readiness
Merge readiness: No-Go — 4 agents have outstanding must-fix items (Security, C++ Design, Documentation, Test Quality).
Clear skies on CI, but the crew has five items to resolve before this one's flight-ready.
…ng, PassiveConsoleTextLogger tests - Replace FW_ASSERT with VALIDATION_ERROR return in SET_EVENT_FILTER_cmdHandler (ground command arguments should not trigger asserts) - Add static_assert to verify FilterSeverity enum ordering matches EventSeverityFilter index space at compile time - Add PassiveConsoleTextLogger unit tests (3 tests) for severity filtering: basic filter, FATAL-never-filtered, disable-all Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
| Fw::LogSeverity logSeverity; | ||
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | ||
| if (status != Fw::Success::SUCCESS) { | ||
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); |
| Fw::LogSeverity logSeverity; | ||
| Fw::Success status = EventSeverityFilter::fromIndex(static_cast<FwSizeType>(filterLevel.e), logSeverity); | ||
| if (status != Fw::Success::SUCCESS) { | ||
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); |
| this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); | ||
| return; | ||
| } | ||
| this->m_severityFilter.setFilter(logSeverity, filterEnable.e == Enabled::ENABLED); |
- Update ActiveTextLogger SDD: add severity filtering section and requirements - Update PassiveConsoleTextLogger SDD: add severity filtering, requirements, ports - Create EventSeverityFilter utility SDD: API, severity ordering, FATAL invariant - Update event-management reference doc: note text logger filtering support Co-Authored-By: michael.d.starch <michael.d.starch@jpl.nasa.gov>
Change Description
Extracts event severity filtering into a shared
Svc::EventSeverityFilterutility class and integrates it intoPassiveConsoleTextLogger,ActiveTextLogger, andEventManager.New class
Svc::EventSeverityFilter(Svc/Types/EventSeverityFilter/):fromIndex()/toIndex()useFw::Successreturn (not bool) for operation statussetFilter,isEnabled, config defaults) usesbool— correct semantic for true/false predicatesSEVERITY_ORDERprovides canonical index-to-severity mapping (used byEventManagerfor itsFilterSeveritycommand enum)Integration:
PassiveConsoleTextLoggerandActiveTextLogger: gainsetSeverityFilter(severity, bool)API and config defaults in*Cfg.hppEventManager: replaces inlinem_filterStatestruct array + switch/case with shared class; usesfromIndex()forFilterSeveritymapping; returnsVALIDATION_ERROR(not assert) on invalid ground command argumentsActiveTextLogger: replaces hardcodedif (DIAGNOSTIC) return;with configurable filter (default: DIAGNOSTIC disabled, preserving backward compat)static_assertverifiesFilterSeverityenum ordering matchesEventSeverityFilterindex spaceConfig defaults (bool):
PASSIVE_TEXT_LOGGER_FILTER_*_DEFAULT— alltrue(enabled)ACTIVE_TEXT_LOGGER_FILTER_DIAGNOSTIC_DEFAULT—false(matches prior behavior)EVENT_MANAGER_FILTER_DIAGNOSTIC_DEFAULT—falseDocumentation:
ActiveTextLoggerSDD with severity filtering section and requirementsPassiveConsoleTextLoggerSDD with severity filtering, requirements, and port descriptionsEventSeverityFilterutility SDD documenting API, severity ordering, and FATAL invariantdocs/reference/system-functional/event-management.mdto mention text logger filteringRationale
PassiveTextLoggerhad no severity filtering.ActiveTextLoggerhad only hardcoded DIAGNOSTIC filtering.EventManagerhad full filtering but duplicated, non-reusable logic. This addresses #2817.Testing/Review Recommendations
EventManagertests pass unchangedfprime-util checkinSvc/Types/EventSeverityFilter,Svc/PassiveConsoleTextLogger,Svc/ActiveTextLogger,Svc/EventManagerSET_EVENT_FILTERreturnsVALIDATION_ERROR(not assert) on invalid inputFuture Work
AI Usage (see policy)
AI was used for code generation, implementation, testing, and documentation of this feature.
IAMAI