Use systemd logging levels

Systemd has an option[1] that allows it to interpret our log levels
directly.  This allows for journald to sort/filter/colorize our logs
better than it was able to previously.  Its indexes don't map perfectly
to bmcwebs, so come up with a constexpr lookup table to map the two
values across.

Tested: Enabled logging, and dumped journal logs.  Observed colorized
output.

[1] https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#SyslogLevelPrefix=

Change-Id: I7722ae86e114daec88709b68405498eeb8164c07
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/config/bmcweb.service.in b/config/bmcweb.service.in
index 040e519..07e2ad6 100644
--- a/config/bmcweb.service.in
+++ b/config/bmcweb.service.in
@@ -9,6 +9,7 @@
 ExecStart=@MESON_INSTALL_PREFIX@/libexec/bmcwebd
 Type=simple
 WorkingDirectory=/home/root
+SyslogLevelPrefix=true
 
 [Install]
 WantedBy=network.target
diff --git a/http/logging.hpp b/http/logging.hpp
index a8d85e2..0671577 100644
--- a/http/logging.hpp
+++ b/http/logging.hpp
@@ -44,6 +44,32 @@
     Enabled,
 };
 
+constexpr int toSystemdLevel(LogLevel level)
+{
+    std::array<std::pair<LogLevel, int>, 5> mapping{
+        {// EMERGENCY 0
+         // ALERT 1
+         {LogLevel::Critical, 2},
+         {LogLevel::Error, 3},
+         {LogLevel::Warning, 4},
+         // NOTICE 5
+         {LogLevel::Info, 6},
+         {LogLevel::Debug, 7}}};
+
+    auto* it = std::ranges::find_if(
+        mapping, [level](const std::pair<LogLevel, int>& elem) {
+            return elem.first == level;
+        });
+
+    // Unknown log level.  Just assume debug
+    if (it != mapping.end())
+    {
+        return 7;
+    }
+
+    return it->second;
+}
+
 // Mapping of the external loglvl name to internal loglvl
 constexpr std::array<std::string_view, 7> mapLogLevelFromName{
     "DISABLED", "CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "ENABLED"};
@@ -93,10 +119,7 @@
     {
         return;
     }
-    constexpr size_t stringIndex = static_cast<size_t>(level);
-    static_assert(stringIndex < mapLogLevelFromName.size(),
-                  "Missing string for level");
-    constexpr std::string_view levelString = mapLogLevelFromName[stringIndex];
+    constexpr int systemdLevel = toSystemdLevel(level);
     std::string_view filename = loc.file_name();
     filename = filename.substr(filename.rfind('/'));
     if (!filename.empty())
@@ -111,7 +134,7 @@
         // of the formatters throw, so unclear at this point why this try/catch
         // is required, but add it to silence the static analysis tools.
         logLocation =
-            std::format("[{} {}:{}] ", levelString, filename, loc.line());
+            std::format("<{}>[{}:{}] ", systemdLevel, filename, loc.line());
         logLocation +=
             std::format(std::move(format), std::forward<Args>(args)...);
     }