/* * Copyright (C) 2015 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * */ #include #ifndef BOOST_LOG_DYN_LINK #define BOOST_LOG_DYN_LINK #endif #include #include #include #include #include #include #include #define BOOST_LOG_USE_NATIVE_SYSLOG #include #include "anbox/logger.h" namespace { namespace attrs { BOOST_LOG_ATTRIBUTE_KEYWORD(Severity, "Severity", anbox::Logger::Severity) BOOST_LOG_ATTRIBUTE_KEYWORD(Location, "Location", anbox::Logger::Location) BOOST_LOG_ATTRIBUTE_KEYWORD(Timestamp, "Timestamp", boost::posix_time::ptime) } struct BoostLogLogger : public anbox::Logger { BoostLogLogger() : initialized_(false) {} void Init(const anbox::Logger::Severity& severity = anbox::Logger::Severity::kWarning) override { if (initialized_) return; boost::log::formatter formatter = boost::log::expressions::stream << "[" << attrs::Severity << " " << boost::log::expressions::format_date_time( "Timestamp", "%Y-%m-%d %H:%M:%S") << "] " << boost::log::expressions::if_(boost::log::expressions::has_attr( attrs::Location))[boost::log::expressions::stream << "[" << attrs::Location << "] "] << boost::log::expressions::smessage; boost::log::core::get()->remove_all_sinks(); // If we have a controlling tty then we use the console for log outpu // and otherwise we move everything into the system syslog. if (isatty(0)) { auto logger = boost::log::add_console_log(std::cout); logger->set_formatter(formatter); } else { boost::shared_ptr backend( new boost::log::sinks::syslog_backend( boost::log::keywords::facility = boost::log::sinks::syslog::user, boost::log::keywords::use_impl = boost::log::sinks::syslog::native)); backend->set_severity_mapper(boost::log::sinks::syslog::direct_severity_mapping("Severity")); boost::log::core::get()->add_sink(boost::make_shared>(backend)); } severity_ = severity; initialized_ = true; } void SetSeverity(const Severity& severity) override { severity_ = severity; } Severity GetSeverity() override { return severity_; } void Log(Severity severity, const std::string& message, const boost::optional& loc) override { if (!initialized_) Init(); // FIXME somehow set_filter doesn't work with the trivial logger. If // we set a filter based on the severity attribute open_record will // not return a new record. Because of that we do a poor man filtering // here until we have a proper way to do this via boost. if (severity < severity_) return; if (auto rec = boost::log::trivial::logger::get().open_record()) { boost::log::record_ostream out{rec}; out << boost::log::add_value(attrs::Severity, severity) << boost::log::add_value(attrs::Timestamp, boost::posix_time::microsec_clock::universal_time()) << message; if (loc) { // We have to pass in a temporary as boost::log (<= 1.55) expects a // mutable reference to be passed to boost::log::add_value(...). auto tmp = *loc; out << boost::log::add_value(attrs::Location, tmp); } boost::log::trivial::logger::get().push_record(std::move(rec)); } } private: Severity severity_; bool initialized_; }; std::shared_ptr& MutableInstance() { static std::shared_ptr instance{new BoostLogLogger()}; return instance; } void SetInstance(const std::shared_ptr& logger) { MutableInstance() = logger; } } namespace anbox { bool Logger::SetSeverityFromString(const std::string& severity) { if (severity == "trace") SetSeverity(Severity::kTrace); else if (severity == "debug") SetSeverity(Severity::kDebug); else if (severity == "info") SetSeverity(Severity::kInfo); else if (severity == "warning") SetSeverity(Severity::kWarning); else if (severity == "error") SetSeverity(Severity::kError); else if (severity == "fatal") SetSeverity(Severity::kFatal); else return false; return true; } void Logger::Trace(const std::string& message, const boost::optional& location) { Log(Severity::kTrace, message, location); } void Logger::Debug(const std::string& message, const boost::optional& location) { Log(Severity::kDebug, message, location); } void Logger::Info(const std::string& message, const boost::optional& location) { Log(Severity::kInfo, message, location); } void Logger::Warning(const std::string& message, const boost::optional& location) { Log(Severity::kWarning, message, location); } void Logger::Error(const std::string& message, const boost::optional& location) { Log(Severity::kError, message, location); } void Logger::Fatal(const std::string& message, const boost::optional& location) { Log(Severity::kFatal, message, location); } std::ostream& operator<<(std::ostream& strm, anbox::Logger::Severity severity) { switch (severity) { case anbox::Logger::Severity::kTrace: return strm << "TT"; case anbox::Logger::Severity::kDebug: return strm << "DD"; case anbox::Logger::Severity::kInfo: return strm << "II"; case anbox::Logger::Severity::kWarning: return strm << "WW"; case anbox::Logger::Severity::kError: return strm << "EE"; case anbox::Logger::Severity::kFatal: return strm << "FF"; default: return strm << static_cast(severity); } } std::ostream& operator<<(std::ostream& out, const Logger::Location& location) { return out << utils::string_format( "%s:%d@%s", boost::filesystem::path(location.file).filename().string(), location.line, location.function); } Logger& Log() { return *MutableInstance(); } void SetLogger(const std::shared_ptr& logger) { SetInstance(logger); } } // namespace anbox