Add logging log template

C++ template to log a message to journald.
Usage:
    log<level::XX>(const char*, entry(*format), entry()...);
    log<level::XX>(char*, entry(*format), entry()...);
Example:
    log<level::DEBUG>(
        msg("Simple Example"));
    char *msg_str[] = "File not found";
    log<level::DEBUG>(
        msg_str,
        entry("MY_METADATA=%s_%x, name, number));
An std::string can be passed via: example_string.c_str()),
and entry("EXAMPLE_STRING=%s", example_string.c_str())).
A future enhancement would be for 'entry' tuples to be
individually validated with a similar approach as the gcc
attribute format.

Change-Id: I82f4450468cc0676458a0ce56cf33c934a1a66ef
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/log.hpp b/log.hpp
new file mode 100644
index 0000000..cba6d37
--- /dev/null
+++ b/log.hpp
@@ -0,0 +1,143 @@
+/**
+ * Copyright © 2016 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.
+ */
+
+#pragma once
+
+#include <tuple>
+#include <systemd/sd-journal.h>
+
+namespace phosphor
+{
+
+namespace logging
+{
+
+/** @enum level
+ *  @brief Enum for priority level
+ */
+enum class level
+{
+    EMERG   = LOG_EMERG,
+    ALERT   = LOG_ALERT,
+    CRIT    = LOG_CRIT,
+    ERR     = LOG_ERR,
+    WARNING = LOG_WARNING,
+    NOTICE  = LOG_NOTICE,
+    INFO    = LOG_INFO,
+    DEBUG   = LOG_DEBUG,
+};
+
+/** @fn log()
+ *  @brief Log message to systemd journal
+ *  @tparam L - Priority level
+ *  @param[in] msg - Message to be logged in C-string format
+ *  @param[in] entry - Metadata fields to be added to the message
+ *  @details Usage: log<level::XX>(const char*, entry(*format), entry()...);
+ *  @example log<level::DEBUG>(
+ *                 "Simple Example");
+ *           char msg_str[] = "File not found";
+ *           log<level::DEBUG>(
+ *                 msg_str,
+ *                 entry("MY_METADATA=%s_%x, name, number));
+ */
+template <level L, typename Msg, typename ...Entry>
+void log(Msg msg, Entry... entry);
+
+/** @fn entry()
+ *  @brief Pack each format string entry as a tuple to be able to validate
+ *    the string and parameters when multiple entries are passed to be logged.
+ *  @tparam Arg - Types of first argument
+ *  @tparam Args - Types of remaining arguments
+ *  @param[in] arg - First metadata string of form VAR=value where
+ *    VAR is the variable name in uppercase and
+ *    value is of any size and format
+ *  @param[in] args - Remaining metadata strings
+ */
+template <typename Arg, typename ...Args>
+constexpr auto entry(Arg&& arg, Args&&... args);
+
+namespace details
+{
+
+/** @fn prio()
+ *  @brief Prepend PRIORITY= to the input priority string.
+ *    This is required by sd_journal_send().
+ *  @tparam L - Priority level
+ */
+template <level L>
+constexpr auto prio()
+{
+    constexpr const char *prio_str = "PRIORITY=%d";
+    constexpr const auto prio_tuple = std::make_tuple(prio_str, L);
+    return prio_tuple;
+}
+
+/** @fn helper_log()
+ *  @brief Helper function for details::log(). Log request to journal.
+ *  @tparam T - Type of tuple
+ *  @tparam I - std::integer_sequence of indexes (0..N) for each tuple element
+ *  @param[in] e - Tuple containing the data to be logged
+ *  @param[unnamed] - std::integer_sequence of tuple's index values
+ */
+template <typename T, size_t ...I>
+void helper_log(T&& e, std::integer_sequence<size_t, I...>)
+{
+    sd_journal_send(std::get<I>(std::forward<T>(e))..., NULL);
+}
+
+/** @fn details::log()
+ *  @brief Implementation of logging::log() function.
+ *         Send request msg and size to helper funct to log it to the journal.
+ *  @tparam T - Type of tuple
+ *  @param[in] e - Tuple containing the data to be logged
+ */
+template <typename T>
+void log(T&& e)
+{
+    constexpr auto e_size = std::tuple_size<std::decay_t<T>>::value;
+    helper_log(std::forward<T>(e), std::make_index_sequence<e_size>{});
+}
+
+} // namespace details
+
+template <level L, typename Msg, typename ...Entry>
+void log(Msg msg, Entry... entry)
+{
+    static_assert((std::is_same<const char*, std::decay_t<Msg>>::value ||
+                   std::is_same<char*, std::decay_t<Msg>>::value),
+                  "First parameter must be a C-string.");
+
+    constexpr const char *msg_str = "MESSAGE=%s";
+    const auto msg_tuple = std::make_tuple(msg_str, std::forward<Msg>(msg));
+
+    auto log_tuple = std::tuple_cat(details::prio<L>(),
+                                    msg_tuple,
+                                    std::forward<Entry>(entry)...);
+    details::log(log_tuple);
+}
+
+template <typename Arg, typename ...Args>
+constexpr auto entry(Arg&& arg, Args&&... args)
+{
+    const auto entry_tuple = std::make_tuple(std::forward<Arg>(arg),
+                                       std::forward<Args>(args)...);
+    return entry_tuple;
+}
+
+} // namespace logging
+
+} // namespace phosphor
+