Add failsafe logger for zones

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.
...

Change-Id: I2c296addb7ad117c03c04a27de91204796cda036
Signed-off-by: James Zheng <alphetis@google.com>
diff --git a/dbus/dbuspassive.cpp b/dbus/dbuspassive.cpp
index aa4cbaf..160dbde 100644
--- a/dbus/dbuspassive.cpp
+++ b/dbus/dbuspassive.cpp
@@ -20,6 +20,8 @@
 #include "dbushelper_interface.hpp"
 #include "dbuspassiveredundancy.hpp"
 #include "dbusutil.hpp"
+#include "failsafeloggers/builder.hpp"
+#include "failsafeloggers/failsafe_logger_utility.hpp"
 #include "util.hpp"
 
 #include <sdbusplus/bus.hpp>
@@ -31,6 +33,8 @@
 #include <string>
 #include <variant>
 
+#include "failsafeloggers/failsafe_logger.cpp"
+
 namespace pid_control
 {
 
@@ -143,6 +147,8 @@
         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;
         }
     }
@@ -168,6 +174,8 @@
     // which is set and cleared by other causes.
     if (_badReading)
     {
+        outputFailsafeLogWithSensor(_id, true, _id,
+                                    "The sensor has bad readings.");
         return true;
     }
 
@@ -177,10 +185,35 @@
     // 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..376e644
--- /dev/null
+++ b/failsafeloggers/builder.cpp
@@ -0,0 +1,67 @@
+/**
+ * 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 "conf.hpp"
+#include "failsafeloggers/failsafe_logger.hpp"
+#include "failsafeloggers/failsafe_logger_utility.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+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..3c59820
--- /dev/null
+++ b/failsafeloggers/builder.hpp
@@ -0,0 +1,18 @@
+#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..91416e0
--- /dev/null
+++ b/failsafeloggers/failsafe_logger.cpp
@@ -0,0 +1,61 @@
+#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.
+    constexpr uint64_t secondInMS = 1000; // 1 second in milliseconds
+    while (!_logTimestamps.empty() &&
+           nowMs - _logTimestamps.front() >= secondInMS)
+    {
+        _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..0e13919
--- /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..40d0506
--- /dev/null
+++ b/failsafeloggers/failsafe_logger_utility.cpp
@@ -0,0 +1,11 @@
+#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..05ed3ea
--- /dev/null
+++ b/failsafeloggers/failsafe_logger_utility.hpp
@@ -0,0 +1,53 @@
+#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])
+    {
+        if (zoneIdToFailsafeLogger.count(zoneId))
+        {
+            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)
+{
+    if (zoneIdToFailsafeLogger.count(zoneId))
+    {
+        zoneIdToFailsafeLogger[zoneId]->outputFailsafeLog(
+            zoneId, newFailsafeState, location, reason);
+    }
+}
+} // namespace pid_control
diff --git a/main.cpp b/main.cpp
index 76c5d19..e91d109 100644
--- a/main.cpp
+++ b/main.cpp
@@ -19,6 +19,7 @@
 #include "buildjson/buildjson.hpp"
 #include "conf.hpp"
 #include "dbus/dbusconfiguration.hpp"
+#include "failsafeloggers/builder.hpp"
 #include "interfaces.hpp"
 #include "pid/builder.hpp"
 #include "pid/buildjson.hpp"
@@ -165,6 +166,11 @@
     state::mgmr = buildSensors(sensorConfig, passiveBus, hostBus);
     state::zones =
         buildZones(zoneConfig, zoneDetailsConfig, state::mgmr, modeControlBus);
+    // Set `logMaxCountPerSecond` to 20 will limit the number of logs output per
+    // second in each zone. Using 20 here would limit the output rate to be no
+    // larger than 100 per sec for most platforms as the number of zones are
+    // usually <=3. This will effectively avoid resource exhaustion.
+    buildFailsafeLoggers(state::zones, /* logMaxCountPerSecond = */ 20);
 
     if (0 == state::zones.size())
     {
diff --git a/meson.build b/meson.build
index 57c25dc..5b0a57c 100644
--- a/meson.build
+++ b/meson.build
@@ -102,6 +102,8 @@
     '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 901905d..d9fe3c0 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,6 +102,8 @@
     if (_missingAcceptable.find(name) != _missingAcceptable.end())
     {
         // Disallow sensors in MissingIsAcceptable list from causing failsafe
+        outputFailsafeLogWithZone(_zoneId, this->getFailSafeMode(), name,
+                                  "The sensor is missing but is acceptable.");
         return;
     }
 
@@ -558,6 +561,11 @@
     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 8c49f1f..14017b8 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 @@
     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;
@@ -184,6 +186,9 @@
                 {
                     std::cerr << sensorInput << " sensor timeout\n";
                 }
+                outputFailsafeLogWithZone(_zoneId, this->getFailSafeMode(),
+                                          sensorInput,
+                                          "The sensor has timed out.");
             }
             else
             {
@@ -198,6 +203,9 @@
                     }
 
                     _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 34a5352..33f0a6f 100644
--- a/pid/zone_interface.hpp
+++ b/pid/zone_interface.hpp
@@ -27,6 +27,9 @@
     /** 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 51d38a4..39eeb5f 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>
@@ -102,6 +104,8 @@
         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 3066ab7..f37addf 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..61b8b26 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -39,7 +39,8 @@
     '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,18 @@
                                        '../pid/thermalcontroller.cpp',
                                        '../pid/tuning.cpp',
                                        '../pid/util.cpp'],
-    'pid_zone_unittest': ['../pid/ec/pid.cpp',
+    'pid_zone_unittest': ['../failsafeloggers/builder.cpp',
+                          '../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 5155971..47b3f97 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"
@@ -10,6 +13,7 @@
 
 #include <chrono>
 #include <cstring>
+#include <unordered_map>
 #include <vector>
 
 #include <gmock/gmock.h>
@@ -344,6 +348,10 @@
     // This test will add a couple thermal inputs, and verify that the zone
     // initializes into failsafe mode, and will read each sensor.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     std::string name1 = "temp1";
     int64_t timeout = 1;
 
@@ -396,6 +404,10 @@
 {
     // This will add a couple fan inputs, and verify the values are cached.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     std::string name1 = "fan1";
     int64_t timeout = 2;
 
@@ -444,6 +456,10 @@
     // On the second updateSensors call, the updated timestamp will be beyond
     // the timeout limit.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     int64_t timeout = 1;
 
     std::string name1 = "temp1";
@@ -506,6 +522,10 @@
     // only sensor1 goes missing.
     // However, sensor2 going missing should still trigger failsafe mode.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     int64_t timeout = 1;
 
     std::string name1 = "temp1";
@@ -602,6 +622,10 @@
 {
     // This will add a couple fan inputs, and verify the values are cached.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     std::string name1 = "fan1";
     int64_t timeout = 2;
 
@@ -655,6 +679,10 @@
 {
     // This will add a couple fan inputs, and verify the values are cached.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     std::string name1 = "fan1";
     int64_t timeout = 2;
 
@@ -714,6 +742,10 @@
 {
     // One can grab a sensor from the manager through the zone.
 
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     int64_t timeout = 1;
 
     std::string name1 = "temp1";
@@ -804,6 +836,11 @@
 {
     // This property is implemented by us as read-only, such that trying to
     // write to it will have no effect.
+
+    // Disable failsafe logger for the unit test.
+    std::unordered_map<int64_t, std::shared_ptr<ZoneInterface>> empty_zone_map;
+    buildFailsafeLoggers(empty_zone_map, 0);
+
     EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode());
 }
 
diff --git a/test/sensor_host_unittest.cpp b/test/sensor_host_unittest.cpp
index e9800cf..5e958c0 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 a5f105b..885a2c4 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 @@
     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&));