regulators: Create error_logging_utils

Create the error_logging_utils namespace for utility functions that make
it easier to log errors.  Create two initial functions within the
namespace.

The first function logs an error based on an exception.  The exception
may have one or more nested inner exceptions.  The function finds the
highest priority exception and logs a corresponding error.

The second function provides the same basic behavior as the first, but
it adds an ErrorHistory parameter.  An error will only be logged if it
was not previously logged.  The ErrorHistory object is used to
determine whether an error has been previously logged.  This avoids
logging duplicate errors if a regulator operation is occurring
repeatedly, such as reading sensor values.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: If246cde9a0f60c5bba34ae4a0d68fb511c0024fc
diff --git a/phosphor-regulators/src/error_logging_utils.cpp b/phosphor-regulators/src/error_logging_utils.cpp
new file mode 100644
index 0000000..fb3506e
--- /dev/null
+++ b/phosphor-regulators/src/error_logging_utils.cpp
@@ -0,0 +1,195 @@
+/**
+ * Copyright © 2021 IBM Corporation
+ *
+ * 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 "error_logging_utils.hpp"
+
+#include "config_file_parser_error.hpp"
+#include "exception_utils.hpp"
+#include "i2c_interface.hpp"
+#include "journal.hpp"
+#include "pmbus_error.hpp"
+#include "write_verification_error.hpp"
+
+#include <sdbusplus/exception.hpp>
+
+#include <vector>
+
+namespace phosphor::power::regulators::error_logging_utils
+{
+
+void logError(std::exception_ptr eptr, Entry::Level severity,
+              Services& services)
+{
+    // Specify empty error history so that all error types will be logged
+    ErrorHistory history{};
+    logError(eptr, severity, services, history);
+}
+
+void logError(std::exception_ptr eptr, Entry::Level severity,
+              Services& services, ErrorHistory& history)
+{
+    // Check for null exception pointer
+    if (!eptr)
+    {
+        return;
+    }
+
+    // Get exception to log from specified exception and any nested exceptions
+    std::exception_ptr exceptionToLog = internal::getExceptionToLog(eptr);
+
+    // Log an error based on the exception
+    ErrorLogging& errorLogging = services.getErrorLogging();
+    Journal& journal = services.getJournal();
+    ErrorType errorType{};
+    try
+    {
+        std::rethrow_exception(exceptionToLog);
+    }
+    catch (const ConfigFileParserError& e)
+    {
+        errorType = ErrorType::configFile;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logConfigFileError(severity, journal);
+        }
+    }
+    catch (const PMBusError& e)
+    {
+        errorType = ErrorType::pmbus;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logPMBusError(severity, journal, e.getInventoryPath());
+        }
+    }
+    catch (const WriteVerificationError& e)
+    {
+        errorType = ErrorType::writeVerification;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logWriteVerificationError(severity, journal,
+                                                   e.getInventoryPath());
+        }
+    }
+    catch (const i2c::I2CException& e)
+    {
+        errorType = ErrorType::i2c;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logI2CError(severity, journal, e.bus, e.addr,
+                                     e.errorCode);
+        }
+    }
+    catch (const sdbusplus::exception_t& e)
+    {
+        errorType = ErrorType::dbus;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logDBusError(severity, journal);
+        }
+    }
+    catch (const std::exception& e)
+    {
+        errorType = ErrorType::internal;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logInternalError(severity, journal);
+        }
+    }
+    catch (...)
+    {
+        errorType = ErrorType::internal;
+        if (!history.wasLogged(errorType))
+        {
+            history.setWasLogged(errorType, true);
+            errorLogging.logInternalError(severity, journal);
+        }
+    }
+}
+
+namespace internal
+{
+
+std::exception_ptr getExceptionToLog(std::exception_ptr eptr)
+{
+    // Default to selecting the outermost exception
+    std::exception_ptr exceptionToLog{eptr};
+
+    // Get vector containing this exception and any nested exceptions
+    std::vector<std::exception_ptr> exceptions =
+        exception_utils::getExceptions(eptr);
+
+    // Define temporary constants for exception priorities
+    const int lowPriority{0}, mediumPriority{1}, highPriority{2};
+
+    // Loop through the exceptions from innermost to outermost.  Find the
+    // exception with the highest priority.  If there is a tie, select the
+    // outermost exception with that priority.
+    int highestPriorityFound{-1};
+    for (std::exception_ptr curptr : exceptions)
+    {
+        int priority{-1};
+        try
+        {
+            std::rethrow_exception(curptr);
+        }
+        catch (const ConfigFileParserError& e)
+        {
+            priority = highPriority;
+        }
+        catch (const PMBusError& e)
+        {
+            priority = highPriority;
+        }
+        catch (const WriteVerificationError& e)
+        {
+            priority = highPriority;
+        }
+        catch (const i2c::I2CException& e)
+        {
+            priority = highPriority;
+        }
+        catch (const sdbusplus::exception_t& e)
+        {
+            priority = mediumPriority;
+        }
+        catch (const std::exception& e)
+        {
+            priority = lowPriority;
+        }
+        catch (...)
+        {
+            priority = lowPriority;
+        }
+
+        if (priority >= highestPriorityFound)
+        {
+            highestPriorityFound = priority;
+            exceptionToLog = curptr;
+        }
+    }
+
+    return exceptionToLog;
+}
+
+} // namespace internal
+
+} // namespace phosphor::power::regulators::error_logging_utils