Add failsafe logger for phosphor-pid-control service.
Further optimizations will be handled in b/380946245.
https://fusion2.corp.google.com/6f303831-040f-31c0-a747-2c7c4458b54c
https://fusion2.corp.google.com/b2560a17-2a26-36a0-a1e9-b13a4d395f02
https://fusion2.corp.google.com/b62ed499-47ba-30bc-97c5-4675fafe01cc
Tested:
...
Nov 23 21:40:06 tmddp10-nfd01.prod.google.com swampd[4893]: Zone `0` is in failsafe mode. With update at `fleeting0`: The sensor has bad readings.
Nov 23 21:40:06 tmddp10-nfd01.prod.google.com swampd[4893]: Zone `1` is in failsafe mode. With update at `fleeting1`: The sensor has bad readings.
Nov 23 21:40:06 tmddp10-nfd01.prod.google.com swampd[4893]: Zone `1` leaves failsafe mode. With update at `hotswap_in_Input_Power`: The sensor has recovered.
Nov 23 21:40:06 tmddp10-nfd01.prod.google.com swampd[4893]: Zone `0` leaves failsafe mode. With update at `hotswap_in_Input_Power`: The sensor has recovered.
...
Platforms-Affected: all platforms
Google-Bug-Id: 355242359
Change-Id: I428dec24501d8036f8478b8cf2aa3c0cfbc2ce80
Signed-off-by: alphetis <alphetis@google.com>
(cherry picked from commit 61f697c8c309327096a7da346ace12b6d6d1d223)
diff --git a/recipes-phosphor/fans/phosphor-pid-control/0001-Add-failsafe-logger-for-zones.patch b/recipes-phosphor/fans/phosphor-pid-control/0001-Add-failsafe-logger-for-zones.patch
new file mode 100644
index 0000000..0bc9ee0
--- /dev/null
+++ b/recipes-phosphor/fans/phosphor-pid-control/0001-Add-failsafe-logger-for-zones.patch
@@ -0,0 +1,645 @@
+From 01dedb7a26b2293044ed4e50ed45999e9fb5b462 Mon Sep 17 00:00:00 2001
+From: James Zheng <alphetis@google.com>
+Date: Sun, 24 Nov 2024 04:01:17 +0000
+Subject: [PATCH] Add failsafe logger for zones
+
+Tested: Manually
+
+Patch Tracking Bug: b/381994545
+Upstream info / review: https://gerrit.openbmc.org/c/openbmc/phosphor-pid-control/+/75964
+Upstream-Status: Pending
+Justification: Enable failsafe logger for each zone in pid control
+
+---
+ dbus/dbuspassive.cpp | 34 ++++++++++-
+ failsafeloggers/builder.cpp | 63 +++++++++++++++++++++
+ failsafeloggers/builder.hpp | 17 ++++++
+ failsafeloggers/failsafe_logger.cpp | 50 ++++++++++++++++
+ failsafeloggers/failsafe_logger.hpp | 56 ++++++++++++++++++
+ failsafeloggers/failsafe_logger_utility.cpp | 12 ++++
+ failsafeloggers/failsafe_logger_utility.hpp | 50 ++++++++++++++++
+ main.cpp | 2 +
+ meson.build | 2 +
+ pid/zone.cpp | 12 +++-
+ pid/zone.hpp | 10 ++++
+ pid/zone_interface.hpp | 3 +
+ sensors/host.cpp | 4 ++
+ test/dbus_passive_unittest.cpp | 3 +
+ test/meson.build | 11 +++-
+ test/pid_fancontroller_unittest.cpp | 3 +
+ test/pid_zone_unittest.cpp | 3 +
+ test/sensor_host_unittest.cpp | 3 +
+ test/zone_mock.hpp | 2 +
+ 19 files changed, 335 insertions(+), 5 deletions(-)
+ create mode 100644 failsafeloggers/builder.cpp
+ create mode 100644 failsafeloggers/builder.hpp
+ create mode 100644 failsafeloggers/failsafe_logger.cpp
+ create mode 100644 failsafeloggers/failsafe_logger.hpp
+ create mode 100644 failsafeloggers/failsafe_logger_utility.cpp
+ create mode 100644 failsafeloggers/failsafe_logger_utility.hpp
+
+diff --git a/dbus/dbuspassive.cpp b/dbus/dbuspassive.cpp
+index 14f28ea..a782f67 100644
+--- a/dbus/dbuspassive.cpp
++++ b/dbus/dbuspassive.cpp
+@@ -18,6 +18,9 @@
+ #include "dbushelper_interface.hpp"
+ #include "dbuspassiveredundancy.hpp"
+ #include "dbusutil.hpp"
++#include "failsafeloggers/builder.hpp"
++#include "failsafeloggers/failsafe_logger.cpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "util.hpp"
+
+ #include <sdbusplus/bus.hpp>
+@@ -210,6 +213,8 @@ bool DbusPassive::getFailed(void) const
+ const std::set<std::string>& failures = redundancy->getFailed();
+ if (failures.find(path) != failures.end())
+ {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor path is marked redundant.");
+ return true;
+ }
+ }
+@@ -235,6 +240,8 @@ bool DbusPassive::getFailed(void) const
+ // which is set and cleared by other causes.
+ if (_badReading)
+ {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor has bad readings.");
+ return true;
+ }
+
+@@ -244,10 +251,35 @@ bool DbusPassive::getFailed(void) const
+ // they are not cooling the system, enable failsafe mode also.
+ if (_marginHot)
+ {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor has no thermal margin left.");
+ return true;
+ }
+
+- return _failed || !_available || !_functional;
++ if (_failed)
++ {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor has failed with a critical issue.");
++ return true;
++ }
++
++ if (!_available)
++ {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor is unavailable.");
++ return true;
++ }
++
++ if (!_functional) {
++ outputFailsafeLogWithSensor(_id, true, _id,
++ "The sensor is not functional.");
++ return true;
++ }
++
++ outputFailsafeLogWithSensor(_id, false, _id,
++ "The sensor has recovered.");
++
++ return false;
+ }
+
+ void DbusPassive::setFailed(bool value)
+diff --git a/failsafeloggers/builder.cpp b/failsafeloggers/builder.cpp
+new file mode 100644
+index 0000000..96f30ff
+--- /dev/null
++++ b/failsafeloggers/builder.cpp
+@@ -0,0 +1,63 @@
++/**
++ * Copyright 2017 Google Inc.
++ *
++ * 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
++ *
++ * http://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 "failsafeloggers/builder.hpp"
++
++#include <algorithm>
++#include <iostream>
++#include <memory>
++#include <string>
++#include <unordered_map>
++#include <vector>
++
++#include "conf.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
++
++namespace pid_control {
++
++void buildFailsafeLoggers(
++ const std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>>& zones,
++ const size_t logMaxCountPerSecond /* = 20 */) {
++ zoneIdToFailsafeLogger =
++ std::unordered_map<int64_t,
++ std::shared_ptr<pid_control::FailsafeLogger>>();
++ sensorNameToZoneId = std::unordered_map<std::string, std::vector<int64_t>>();
++ for (const auto& zoneIdToZone : zones) {
++ int64_t zoneId = zoneIdToZone.first;
++ // Create a failsafe logger for each zone.
++ zoneIdToFailsafeLogger[zoneId] =
++ std::make_shared<FailsafeLogger>(
++ logMaxCountPerSecond, zoneIdToZone.second->getFailSafeMode());
++
++ // Build the sensor-zone topology map.
++ std::vector<std::string> sensorNames =
++ zoneIdToZone.second->getSensorNames();
++ for (const std::string& sensorName : sensorNames) {
++ if (std::find(sensorNameToZoneId[sensorName].begin(),
++ sensorNameToZoneId[sensorName].end(),
++ zoneId) ==
++ sensorNameToZoneId[sensorName].end()) {
++ sensorNameToZoneId[sensorName].push_back(zoneId);
++ }
++ }
++ std::cerr << "Build failsafe logger for Zone " << zoneId << " with initial "
++ << "failsafe mode: " << zoneIdToZone.second->getFailSafeMode()
++ << "\n";
++ }
++}
++
++} // namespace pid_control
+diff --git a/failsafeloggers/builder.hpp b/failsafeloggers/builder.hpp
+new file mode 100644
+index 0000000..2c11a05
+--- /dev/null
++++ b/failsafeloggers/builder.hpp
+@@ -0,0 +1,17 @@
++#pragma once
++
++#include "conf.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "pid/zone_interface.hpp"
++#include "sensors/manager.hpp"
++
++#include <memory>
++#include <unordered_map>
++
++namespace pid_control
++{
++
++void buildFailsafeLoggers(const std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>>& zones,
++ const size_t logMaxCountPerSecond = 20);
++
++} // namespace pid_control
+diff --git a/failsafeloggers/failsafe_logger.cpp b/failsafeloggers/failsafe_logger.cpp
+new file mode 100644
+index 0000000..8485c97
+--- /dev/null
++++ b/failsafeloggers/failsafe_logger.cpp
+@@ -0,0 +1,50 @@
++#include "failsafe_logger.hpp"
++
++#include <chrono>
++#include <iostream>
++
++namespace pid_control {
++
++void FailsafeLogger::outputFailsafeLog(const int64_t zoneId, const bool newFailsafeState,
++ const std::string location,
++ const std::string reason) {
++ // Remove outdated log entries.
++ const auto now = std::chrono::high_resolution_clock::now();
++ uint64_t nowMs = std::chrono::duration_cast<std::chrono::milliseconds>
++ (now.time_since_epoch()).count();
++ // Limit the log output in 1 second.
++ while (!_logTimestamps.empty() && nowMs - _logTimestamps.front() >= 1000) {
++ _logTimestamps.pop_front();
++ }
++
++ // There is a failsafe state change, clear the logs in current state.
++ bool originFailsafeState = _currentFailsafeState;
++ if (newFailsafeState != _currentFailsafeState) {
++ _logsInCurrentState.clear();
++ _currentFailsafeState = newFailsafeState;
++ }
++ // Do not output the log if the capacity is reached, or if the log is already
++ // encountered in the current state.
++ std::string locationReason = location + " @ " + reason;
++ if (_logTimestamps.size() >= _logMaxCountPerSecond ||
++ !_logsInCurrentState.contains(locationReason)) {
++ return;
++ }
++ _logsInCurrentState.insert(locationReason);
++
++ // Only output the log if the zone enters, stays in, or leaves failsafe mode.
++ // No need to output the log if the zone stays in non-failsafe mode.
++ if (newFailsafeState) {
++ std::cerr << "Zone `" << zoneId
++ << "` is in failsafe mode.\t\tWith update at `"
++ << location << "`: " << reason << "\n";
++ } else if (!newFailsafeState && originFailsafeState) {
++ std::cerr << "Zone `" << zoneId
++ << "` leaves failsafe mode.\t\tWith update at `"
++ << location << "`: " << reason << "\n";
++ }
++
++ _logTimestamps.push_back(nowMs);
++}
++
++} // namespace pid_control
+diff --git a/failsafeloggers/failsafe_logger.hpp b/failsafeloggers/failsafe_logger.hpp
+new file mode 100644
+index 0000000..67fa3eb
+--- /dev/null
++++ b/failsafeloggers/failsafe_logger.hpp
+@@ -0,0 +1,56 @@
++#pragma once
++
++#include <deque>
++#include <memory>
++#include <string>
++#include <unordered_map>
++#include <unordered_set>
++
++namespace pid_control
++{
++
++/**
++ * Log the reason for a zone to enter and leave the failsafe mode.
++ *
++ * Particularly, for entering the failsafe mode:
++ * 1. A sensor is specified in thermal config as an input but missed in DBus
++ * 2. A sensor has null readings in DBus
++ * 3. A sensor is abnormal in DBus (not functional, not enabled, etc)
++ * 4. A sensor's reading is above upper critical (UC) limit
++ *
++ * Among the above reasons:
++ * 1 excludes 2, 3, 4.
++ * 2 excludes 1, 4.
++ * 3 excludes 1.
++ * 4 excludes 1, 2.
++ *
++ * Note that this log is at the zone level, not the sensor level.
++ */
++class FailsafeLogger
++{
++ public:
++ FailsafeLogger(size_t logMaxCountPerSecond = 20,
++ bool currentFailsafeState = false)
++ : _logMaxCountPerSecond(logMaxCountPerSecond),
++ _currentFailsafeState(currentFailsafeState) {}
++ ~FailsafeLogger() = default;
++
++ /** Attempt to output an entering/leaving-failsafe-mode log.
++ */
++ void outputFailsafeLog(int64_t zoneId, bool newFailsafeState,
++ const std::string location,
++ const std::string reason);
++
++ private:
++ // The maximum number of log entries to be output within 1 second.
++ size_t _logMaxCountPerSecond;
++ // Whether the zone is currently in the failsafe mode.
++ bool _currentFailsafeState;
++ // The timestamps of the log entries.
++ std::deque<size_t> _logTimestamps;
++ // The logs already encountered in the current state.
++ std::unordered_set<std::string> _logsInCurrentState;
++};
++
++} // namespace pid_control
++
+diff --git a/failsafeloggers/failsafe_logger_utility.cpp b/failsafeloggers/failsafe_logger_utility.cpp
+new file mode 100644
+index 0000000..3466362
+--- /dev/null
++++ b/failsafeloggers/failsafe_logger_utility.cpp
+@@ -0,0 +1,12 @@
++#include "failsafe_logger_utility.hpp"
++
++#include <string>
++
++
++std::unordered_map<int64_t, std::shared_ptr<pid_control::FailsafeLogger>>
++ zoneIdToFailsafeLogger =
++ std::unordered_map<int64_t, std::shared_ptr<pid_control::FailsafeLogger>>();
++
++std::unordered_map<std::string, std::vector<int64_t>>
++ sensorNameToZoneId = std::unordered_map<std::string, std::vector<int64_t>>();
++
+diff --git a/failsafeloggers/failsafe_logger_utility.hpp b/failsafeloggers/failsafe_logger_utility.hpp
+new file mode 100644
+index 0000000..d886406
+--- /dev/null
++++ b/failsafeloggers/failsafe_logger_utility.hpp
+@@ -0,0 +1,50 @@
++#pragma once
++
++#include "conf.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++
++#include <memory>
++#include <string>
++#include <unordered_map>
++#include <vector>
++
++/** Map of the zone ID to its failsafe logger.
++ */
++extern std::unordered_map<int64_t, std::shared_ptr<pid_control::FailsafeLogger>>
++ zoneIdToFailsafeLogger;
++
++/** Map of the sensor name/ID to its corresponding zone IDs.
++ */
++extern std::unordered_map<std::string, std::vector<int64_t>> sensorNameToZoneId;
++
++namespace pid_control {
++
++ /** Given a sensor name, attempt to output entering/leaving-failsafe-mode
++ * logs for its corresponding zones.
++ */
++ inline void outputFailsafeLogWithSensor(const std::string sensorName,
++ const bool newFailsafeState,
++ const std::string location,
++ const std::string reason) {
++ for (const int64_t zoneId : sensorNameToZoneId[sensorName]) {
++ zoneIdToFailsafeLogger[zoneId]->outputFailsafeLog(zoneId,
++ newFailsafeState,
++ location,
++ reason);
++ }
++ }
++
++ /** Given a zone ID, attempt to output entering/leaving-failsafe-mode
++ * logs for its corresponding zones.
++ */
++ inline void outputFailsafeLogWithZone(const int64_t zoneId,
++ const bool newFailsafeState,
++ const std::string location,
++ const std::string reason) {
++ zoneIdToFailsafeLogger[zoneId]->outputFailsafeLog(zoneId,
++ newFailsafeState,
++ location,
++ reason);
++ }
++} // namespace pid_control
++
+diff --git a/main.cpp b/main.cpp
+index 911096e..6e7b275 100644
+--- a/main.cpp
++++ b/main.cpp
+@@ -20,6 +20,7 @@
+ #include "conf.hpp"
+ #include "dbus/dbusconfiguration.hpp"
+ #include "dbus/dbuspassive.hpp"
++#include "failsafeloggers/builder.hpp"
+ #include "interfaces.hpp"
+ #include "pid/builder.hpp"
+ #include "pid/buildjson.hpp"
+@@ -167,6 +168,7 @@ void restartControlLoops()
+ state::mgmr = buildSensors(sensorConfig, passiveBus, hostBus);
+ state::zones = buildZones(zoneConfig, zoneDetailsConfig, state::mgmr,
+ modeControlBus);
++ buildFailsafeLoggers(state::zones, /* logMaxCountPerSecond = */ 20);
+
+ if (0 == state::zones.size())
+ {
+diff --git a/meson.build b/meson.build
+index 967087a..fcfa4d0 100644
+--- a/meson.build
++++ b/meson.build
+@@ -100,6 +100,8 @@ libswampd_sources = [
+ 'dbus/dbuspassive.cpp',
+ 'dbus/dbusactiveread.cpp',
+ 'dbus/dbuswrite.cpp',
++ 'failsafeloggers/builder.cpp',
++ 'failsafeloggers/failsafe_logger_utility.cpp',
+ 'sysfs/sysfsread.cpp',
+ 'sysfs/sysfswrite.cpp',
+ 'sysfs/util.cpp',
+diff --git a/pid/zone.cpp b/pid/zone.cpp
+index 5332efe..4b44ced 100644
+--- a/pid/zone.cpp
++++ b/pid/zone.cpp
+@@ -18,6 +18,7 @@
+ #include "zone.hpp"
+
+ #include "conf.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "pid/controller.hpp"
+ #include "pid/ec/pid.hpp"
+ #include "pid/fancontroller.hpp"
+@@ -101,7 +102,11 @@ void DbusPidZone::markSensorMissing(const std::string& name)
+ if (_missingAcceptable.find(name) != _missingAcceptable.end())
+ {
+ // Disallow sensors in MissingIsAcceptable list from causing failsafe
+- return;
++ outputFailsafeLogWithZone(_zoneId,
++ this->getFailSafeMode(),
++ name,
++ "The sensor is missing but is acceptable.");
++ return;
+ }
+
+ _failSafeSensors.emplace(name);
+@@ -530,6 +535,11 @@ Sensor* DbusPidZone::getSensor(const std::string& name)
+ return _mgr.getSensor(name);
+ }
+
++std::vector<std::string> DbusPidZone::getSensorNames(void)
++{
++ return _thermalInputs;
++}
++
+ bool DbusPidZone::getRedundantWrite(void) const
+ {
+ return _redundantWrite;
+diff --git a/pid/zone.hpp b/pid/zone.hpp
+index 52180a5..172e9df 100644
+--- a/pid/zone.hpp
++++ b/pid/zone.hpp
+@@ -2,6 +2,7 @@
+
+ #include "conf.hpp"
+ #include "controller.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "pidcontroller.hpp"
+ #include "sensors/manager.hpp"
+ #include "sensors/sensor.hpp"
+@@ -88,6 +89,7 @@ class DbusPidZone : public ZoneInterface, public ModeObject
+ uint64_t getUpdateThermalsCycle(void) const override;
+
+ Sensor* getSensor(const std::string& name) override;
++ std::vector<std::string> getSensorNames(void) override;
+ void determineMaxSetPointRequest(void) override;
+ void updateFanTelemetry(void) override;
+ void updateSensors(void) override;
+@@ -185,6 +187,10 @@ class DbusPidZone : public ZoneInterface, public ModeObject
+ {
+ std::cerr << sensorInput << " sensor timeout\n";
+ }
++ outputFailsafeLogWithZone(_zoneId,
++ this->getFailSafeMode(),
++ sensorInput,
++ "The sensor has timed out.");
+ }
+ else
+ {
+@@ -199,6 +205,10 @@ class DbusPidZone : public ZoneInterface, public ModeObject
+ }
+
+ _failSafeSensors.erase(kt);
++ outputFailsafeLogWithZone(_zoneId,
++ this->getFailSafeMode(),
++ sensorInput,
++ "The sensor has recovered.");
+ }
+ }
+ }
+diff --git a/pid/zone_interface.hpp b/pid/zone_interface.hpp
+index 31f3256..bb2454c 100644
+--- a/pid/zone_interface.hpp
++++ b/pid/zone_interface.hpp
+@@ -27,6 +27,9 @@ class ZoneInterface
+ /** Return a pointer to the sensor specified by name. */
+ virtual Sensor* getSensor(const std::string& name) = 0;
+
++ /** Return the list of sensor names in the zone. */
++ virtual std::vector<std::string> getSensorNames(void) = 0;
++
+ /* updateFanTelemetry() and updateSensors() both clear the failsafe state
+ * for a sensor if it's no longer in that state.
+ */
+diff --git a/sensors/host.cpp b/sensors/host.cpp
+index ae63896..8d43a96 100644
+--- a/sensors/host.cpp
++++ b/sensors/host.cpp
+@@ -16,6 +16,8 @@
+
+ #include "host.hpp"
+
++#include "failsafeloggers/failsafe_logger_utility.hpp"
++
+ #include <cmath>
+ #include <iostream>
+ #include <memory>
+@@ -103,6 +105,8 @@ bool HostSensor::getFailed(void)
+ return false;
+ }
+
++ outputFailsafeLogWithSensor(getName(), true, getName(),
++ "The sensor has invalid readings.");
+ return true;
+ }
+
+diff --git a/test/dbus_passive_unittest.cpp b/test/dbus_passive_unittest.cpp
+index 29c73dc..5b652d5 100644
+--- a/test/dbus_passive_unittest.cpp
++++ b/test/dbus_passive_unittest.cpp
+@@ -1,5 +1,8 @@
+ #include "conf.hpp"
+ #include "dbus/dbuspassive.hpp"
++#include "failsafeloggers/builder.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "test/dbushelper_mock.hpp"
+
+ #include <sdbusplus/test/sdbus_mock.hpp>
+diff --git a/test/meson.build b/test/meson.build
+index 50f71a3..fae0312 100644
+--- a/test/meson.build
++++ b/test/meson.build
+@@ -39,7 +39,8 @@ unittest_source = {
+ 'dbus_active_unittest': ['../dbus/dbusactiveread.cpp'],
+ 'dbus_passive_unittest': ['../dbus/dbuspassive.cpp',
+ '../dbus/dbuspassiveredundancy.cpp',
+- '../dbus/dbusutil.cpp'],
++ '../dbus/dbusutil.cpp',
++ '../failsafeloggers/failsafe_logger_utility.cpp'],
+ 'dbus_util_unittest': ['../dbus/dbusutil.cpp'],
+ 'json_parse_unittest': ['../buildjson/buildjson.cpp'],
+ 'pid_json_unittest': ['../pid/buildjson.cpp',
+@@ -60,13 +61,17 @@ unittest_source = {
+ '../pid/thermalcontroller.cpp',
+ '../pid/tuning.cpp',
+ '../pid/util.cpp'],
+- 'pid_zone_unittest': ['../pid/ec/pid.cpp',
++ 'pid_zone_unittest': ['../failsafeloggers/failsafe_logger.cpp',
++ '../failsafeloggers/failsafe_logger_utility.cpp',
++ '../pid/ec/pid.cpp',
+ '../pid/ec/logging.cpp',
+ '../pid/pidcontroller.cpp',
+ '../pid/tuning.cpp',
+ '../pid/zone.cpp',
+ '../sensors/manager.cpp'],
+- 'sensor_host_unittest': ['../sensors/host.cpp'],
++ 'sensor_host_unittest': ['../failsafeloggers/failsafe_logger.cpp',
++ '../failsafeloggers/failsafe_logger_utility.cpp',
++ '../sensors/host.cpp'],
+ 'sensor_manager_unittest': ['../sensors/manager.cpp'],
+ 'sensor_pluggable_unittest': ['../sensors/pluggable.cpp'],
+ 'sensors_json_unittest': ['../sensors/buildjson.cpp'],
+diff --git a/test/pid_fancontroller_unittest.cpp b/test/pid_fancontroller_unittest.cpp
+index 6075a95..97dda0a 100644
+--- a/test/pid_fancontroller_unittest.cpp
++++ b/test/pid_fancontroller_unittest.cpp
+@@ -1,5 +1,8 @@
+ #include "config.h"
+
++#include "failsafeloggers/builder.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "pid/ec/logging.hpp"
+ #include "pid/ec/pid.hpp"
+ #include "pid/fancontroller.hpp"
+diff --git a/test/pid_zone_unittest.cpp b/test/pid_zone_unittest.cpp
+index 1f6e672..5430535 100644
+--- a/test/pid_zone_unittest.cpp
++++ b/test/pid_zone_unittest.cpp
+@@ -1,3 +1,6 @@
++#include "failsafeloggers/builder.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "pid/ec/logging.hpp"
+ #include "pid/ec/pid.hpp"
+ #include "pid/zone.hpp"
+diff --git a/test/sensor_host_unittest.cpp b/test/sensor_host_unittest.cpp
+index cb5ede4..dcde0a6 100644
+--- a/test/sensor_host_unittest.cpp
++++ b/test/sensor_host_unittest.cpp
+@@ -1,3 +1,6 @@
++#include "failsafeloggers/builder.hpp"
++#include "failsafeloggers/failsafe_logger.hpp"
++#include "failsafeloggers/failsafe_logger_utility.hpp"
+ #include "sensors/host.hpp"
+ #include "test/helpers.hpp"
+
+diff --git a/test/zone_mock.hpp b/test/zone_mock.hpp
+index 4e34890..fa3e21d 100644
+--- a/test/zone_mock.hpp
++++ b/test/zone_mock.hpp
+@@ -3,6 +3,7 @@
+ #include "pid/zone_interface.hpp"
+
+ #include <string>
++#include <vector>
+
+ #include <gmock/gmock.h>
+
+@@ -49,6 +50,7 @@ class ZoneMock : public ZoneInterface
+ MOCK_CONST_METHOD0(getAccSetPoint, bool());
+
+ MOCK_METHOD1(getSensor, Sensor*(const std::string&));
++ MOCK_METHOD0(getSensorNames, std::vector<std::string>());
+
+ MOCK_METHOD0(initializeLog, void());
+ MOCK_METHOD1(writeLog, void(const std::string&));
+--
+2.47.0.371.ga323438b13-goog
diff --git a/recipes-phosphor/fans/phosphor-pid-control_%.bbappend b/recipes-phosphor/fans/phosphor-pid-control_%.bbappend
index c57fc8e..39e8c0f 100644
--- a/recipes-phosphor/fans/phosphor-pid-control_%.bbappend
+++ b/recipes-phosphor/fans/phosphor-pid-control_%.bbappend
@@ -3,4 +3,5 @@
SRC_URI:append = " \
file://0001-Support-temperature-sensor-override-dbus-interface.patch \
file://0001-set-fan-pwm-forcely.patch \
+ file://0001-Add-failsafe-logger-for-zones.patch \
"