2012-04-24 17:38:35 +10:00
|
|
|
/*
|
2018-08-04 15:14:06 +10:00
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
2012-04-24 17:38:35 +10:00
|
|
|
*
|
2016-01-20 15:37:52 +11:00
|
|
|
* Copyright 2012-2016 Danny Robson <danny@nerdcruft.net>
|
2012-04-24 17:38:35 +10:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "log.hpp"
|
|
|
|
|
|
|
|
#include "debug.hpp"
|
2016-03-21 14:21:14 +11:00
|
|
|
#include "term.hpp"
|
2015-07-23 17:37:43 +10:00
|
|
|
#include "time.hpp"
|
2016-05-12 17:39:33 +10:00
|
|
|
#include "cast.hpp"
|
2012-04-24 17:38:35 +10:00
|
|
|
|
|
|
|
#include <cstring>
|
2016-08-04 17:42:41 +10:00
|
|
|
#include <ctime>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <iostream>
|
2012-04-26 18:22:05 +10:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2016-03-21 14:21:14 +11:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static
|
|
|
|
const util::level_t
|
|
|
|
ALL_LEVELS[] = {
|
|
|
|
util::EMERGENCY,
|
|
|
|
util::ALERT,
|
|
|
|
util::CRITICAL,
|
|
|
|
util::ERROR,
|
|
|
|
util::WARN,
|
|
|
|
util::NOTICE,
|
|
|
|
util::INFO,
|
|
|
|
util::DEBUG,
|
|
|
|
};
|
2012-04-24 17:38:35 +10:00
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-01-21 13:54:41 +11:00
|
|
|
/// convert a string representation of a log-level into an enumeration value.
|
|
|
|
///
|
|
|
|
/// conversion is case insensitive
|
|
|
|
/// throws std::range_error if unable to convert
|
2016-01-20 15:44:01 +11:00
|
|
|
static util::level_t
|
|
|
|
to_level (std::string name)
|
2016-01-20 15:36:32 +11:00
|
|
|
{
|
2017-01-31 20:30:38 +11:00
|
|
|
if (std::empty (name))
|
|
|
|
return util::EMERGENCY;
|
|
|
|
|
2016-01-20 15:44:01 +11:00
|
|
|
static const std::map<std::string, util::level_t> NAME_LEVELS = {
|
|
|
|
{ "EMERGENCY", util::EMERGENCY },
|
|
|
|
{ "ALERT", util::ALERT },
|
|
|
|
{ "CRITICAL", util::CRITICAL },
|
|
|
|
{ "ERROR", util::ERROR },
|
|
|
|
{ "WARN", util::WARN },
|
|
|
|
{ "WARNING", util::WARN },
|
|
|
|
{ "NOTICE", util::NOTICE },
|
|
|
|
{ "INFO", util::INFO },
|
|
|
|
{ "INFORMATIONAL", util::INFO },
|
|
|
|
{ "DEBUG", util::DEBUG }
|
2012-04-26 18:22:05 +10:00
|
|
|
};
|
|
|
|
|
2014-09-11 15:33:46 +10:00
|
|
|
std::transform (name.cbegin (), name.cend (), name.begin (), ::toupper);
|
|
|
|
|
2012-04-26 18:22:05 +10:00
|
|
|
auto pos = NAME_LEVELS.find (name);
|
|
|
|
if (pos == NAME_LEVELS.end ())
|
|
|
|
throw std::range_error (name);
|
|
|
|
|
|
|
|
return pos->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-21 14:21:14 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const std::string&
|
|
|
|
util::to_string (level_t l)
|
|
|
|
{
|
|
|
|
switch (l) {
|
|
|
|
#define CASE(L) \
|
|
|
|
case util::L: { \
|
|
|
|
static const std::string STR = #L; \
|
|
|
|
return STR; \
|
|
|
|
}
|
|
|
|
|
|
|
|
MAP_LEVEL_T(CASE)
|
|
|
|
|
|
|
|
#undef CASE
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
2012-04-24 17:38:35 +10:00
|
|
|
std::ostream&
|
2016-01-20 15:44:01 +11:00
|
|
|
util::operator<< (std::ostream& os, util::level_t l)
|
2016-01-20 15:36:32 +11:00
|
|
|
{
|
2016-03-21 14:21:14 +11:00
|
|
|
return os << to_string (l);
|
2012-04-24 17:38:35 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2017-01-31 20:30:38 +11:00
|
|
|
// Determine what the value for LOG_LEVEL should be at the beginning of
|
|
|
|
// execution given the system environment.
|
|
|
|
//
|
|
|
|
// Note that the LOG macros _cannot_ be used from within this function as it
|
|
|
|
// will likely result in infinite recursion.
|
|
|
|
static
|
|
|
|
util::level_t
|
|
|
|
initial_log_level (void)
|
2016-01-20 15:36:32 +11:00
|
|
|
{
|
2012-04-26 18:22:24 +10:00
|
|
|
const char *env = getenv ("LOG_LEVEL");
|
|
|
|
if (!env)
|
2016-01-21 13:55:08 +11:00
|
|
|
return util::DEFAULT_LOG_LEVEL;
|
2012-04-26 18:22:24 +10:00
|
|
|
|
|
|
|
try {
|
2016-01-20 15:37:31 +11:00
|
|
|
return to_level (env);
|
2012-04-26 18:22:24 +10:00
|
|
|
} catch (...) {
|
2017-01-31 20:30:38 +11:00
|
|
|
std::clog << "Invalid environment LOG_LEVEL: '" << env << "'\n";
|
2016-01-21 13:55:08 +11:00
|
|
|
return util::DEFAULT_LOG_LEVEL;
|
2012-04-26 18:22:24 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-31 20:30:38 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// We shouldn't ever actually get to use the default value, but we set it to
|
|
|
|
// the most verbose option just in case we've made a mistake elsewhere.
|
|
|
|
|
|
|
|
static bool s_log_level_done;
|
|
|
|
static util::level_t s_log_level_value;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
util::level_t
|
|
|
|
util::log_level (level_t _level)
|
|
|
|
{
|
|
|
|
s_log_level_value = _level;
|
|
|
|
s_log_level_done = true;
|
|
|
|
return s_log_level_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
util::level_t
|
|
|
|
util::log_level (void)
|
|
|
|
{
|
|
|
|
if (!s_log_level_done) {
|
|
|
|
s_log_level_value = initial_log_level ();
|
|
|
|
s_log_level_done = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return s_log_level_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-21 13:55:23 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static bool
|
|
|
|
needs_break (util::level_t level)
|
|
|
|
{
|
|
|
|
static util::level_t break_level;
|
|
|
|
static bool has_level = [&] (void) {
|
|
|
|
const char *env = getenv ("BREAK_LEVEL");
|
|
|
|
if (!env)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
break_level = to_level (env);
|
|
|
|
return true;
|
|
|
|
} catch (...) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} ();
|
|
|
|
|
|
|
|
return has_level && level <= break_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-21 14:21:14 +11:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static
|
|
|
|
util::term::csi::graphics
|
|
|
|
level_colour (util::level_t level)
|
|
|
|
{
|
|
|
|
using util::term::csi::graphics;
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
case util::EMERGENCY:
|
|
|
|
case util::ALERT:
|
|
|
|
case util::CRITICAL:
|
|
|
|
case util::ERROR:
|
|
|
|
return graphics (graphics::FOREGROUND, graphics::RED);
|
|
|
|
|
|
|
|
case util::WARNING:
|
|
|
|
return graphics (graphics::FOREGROUND, graphics::YELLOW);
|
|
|
|
|
|
|
|
case util::NOTICE:
|
|
|
|
case util::INFORMATIONAL:
|
|
|
|
return graphics (graphics::FOREGROUND, graphics::GREEN);
|
|
|
|
|
|
|
|
case util::DEBUG:
|
|
|
|
return graphics (graphics::FOREGROUND, graphics::WHITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
unreachable ();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static
|
|
|
|
size_t
|
|
|
|
level_width (void)
|
|
|
|
{
|
|
|
|
static size_t width = [] {
|
|
|
|
size_t hi = 0;
|
|
|
|
|
|
|
|
for (auto i: ALL_LEVELS)
|
|
|
|
hi = util::max (to_string (i).size (), hi);
|
|
|
|
|
|
|
|
return hi;
|
|
|
|
} ();
|
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
2016-01-20 16:40:42 +11:00
|
|
|
util::log (util::level_t level, const std::string &msg)
|
2016-01-20 15:36:32 +11:00
|
|
|
{
|
2017-01-31 20:30:38 +11:00
|
|
|
if (level <= log_level ()) {
|
2016-04-06 13:50:44 +10:00
|
|
|
static const size_t time_len = strlen("YYYY-mm-dd HHMMhSS") + 1;
|
|
|
|
std::string time_string (time_len - 1, '\0');
|
|
|
|
time_t unix_time = time (nullptr);
|
|
|
|
|
|
|
|
if (0 == strftime (&time_string[0],
|
|
|
|
time_len,
|
|
|
|
"%Y-%m-%d %H%Mh%S",
|
|
|
|
localtime (&unix_time))) {
|
|
|
|
warn ("failed to log time");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-31 20:30:38 +11:00
|
|
|
std::clog << time_string << " ["
|
2016-04-06 13:50:44 +10:00
|
|
|
<< level_colour (level)
|
2018-01-16 15:11:15 +11:00
|
|
|
<< std::setw (util::cast::lossless<int> (level_width ()))
|
2016-04-06 13:50:44 +10:00
|
|
|
<< std::left
|
|
|
|
<< level
|
|
|
|
<< std::setw (0)
|
|
|
|
<< util::term::csi::graphics::RESET
|
|
|
|
<< "] " << msg << std::endl;
|
2016-01-21 13:55:23 +11:00
|
|
|
}
|
|
|
|
|
2016-04-06 13:50:44 +10:00
|
|
|
if (needs_break (level))
|
|
|
|
breakpoint ();
|
2012-04-24 17:38:35 +10:00
|
|
|
}
|
2013-03-05 21:38:51 +11:00
|
|
|
|
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-01-20 16:40:42 +11:00
|
|
|
util::scoped_logger::scoped_logger (util::level_t _level,
|
|
|
|
std::string _message):
|
2013-03-05 21:38:51 +11:00
|
|
|
m_level (_level),
|
2015-07-23 17:37:02 +10:00
|
|
|
m_message (std::move (_message))
|
2013-03-05 21:38:51 +11:00
|
|
|
{ ; }
|
|
|
|
|
|
|
|
|
2015-07-23 17:37:02 +10:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-01-20 15:44:01 +11:00
|
|
|
util::scoped_logger::~scoped_logger ()
|
2015-07-23 17:37:02 +10:00
|
|
|
{
|
2013-03-05 21:38:51 +11:00
|
|
|
log (m_level, m_message);
|
|
|
|
}
|
2015-07-23 17:37:43 +10:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2016-01-20 15:44:01 +11:00
|
|
|
util::scoped_timer::scoped_timer (util::level_t _level,
|
2016-01-20 16:40:42 +11:00
|
|
|
std::string _message):
|
2015-07-23 17:37:43 +10:00
|
|
|
m_level (_level),
|
2016-01-20 16:40:42 +11:00
|
|
|
m_message (std::move (_message)),
|
2015-07-23 17:37:43 +10:00
|
|
|
m_start (util::nanoseconds ())
|
|
|
|
{ ; }
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2016-01-20 15:44:01 +11:00
|
|
|
util::scoped_timer::~scoped_timer ()
|
2015-07-23 17:37:43 +10:00
|
|
|
{
|
|
|
|
auto finish = util::nanoseconds ();
|
|
|
|
auto duration = finish - m_start;
|
|
|
|
|
2016-05-12 17:39:33 +10:00
|
|
|
log (m_level, "%fs, %s", float (duration) / 1'000'000'000.f, m_message);
|
2015-07-23 17:37:43 +10:00
|
|
|
}
|