/*
* 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