libcruft-util/log/log.cpp

320 lines
8.0 KiB
C++
Raw Normal View History

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"
2019-10-10 15:10:41 +11:00
#include "../term.hpp"
#include "../time.hpp"
#include "../cast.hpp"
#include "../string.hpp"
2012-04-24 17:38:35 +10:00
#include <cstring>
#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 constexpr
cruft::log::level_t
2016-03-21 14:21:14 +11:00
ALL_LEVELS[] = {
cruft::log::EMERGENCY,
cruft::log::ALERT,
cruft::log::CRITICAL,
cruft::log::ERROR,
cruft::log::WARN,
cruft::log::NOTICE,
cruft::log::INFO,
cruft::log::DEBUG,
2016-03-21 14:21:14 +11:00
};
2012-04-24 17:38:35 +10:00
2015-07-23 17:37:02 +10: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
2019-10-10 15:10:41 +11:00
cruft::log::level_t
cruft::log::to_level (std::string_view name)
2016-01-20 15:36:32 +11:00
{
if (std::empty (name))
return cruft::log::EMERGENCY;
2019-10-10 15:10:41 +11:00
std::string upper (name.size (), char{});
std::transform (
name.cbegin (),
name.cend (),
upper.begin (),
::toupper
);
static constexpr struct {
char const *name;
cruft::log::level_t value;
} VALUES[] = {
{ "EMERGENCY", cruft::log::EMERGENCY },
{ "ALERT", cruft::log::ALERT },
{ "CRITICAL", cruft::log::CRITICAL },
{ "ERROR", cruft::log::ERROR },
{ "WARN", cruft::log::WARN },
{ "WARNING", cruft::log::WARN },
{ "NOTICE", cruft::log::NOTICE },
{ "INFO", cruft::log::INFO },
{ "INFORMATIONAL", cruft::log::INFO },
{ "DEBUG", cruft::log::DEBUG }
2012-04-26 18:22:05 +10:00
};
2019-10-10 15:10:41 +11:00
for (auto const [key, val]: VALUES)
if (!strcmp (upper.data (), key))
return val;
2014-09-11 15:33:46 +10:00
2019-10-10 15:10:41 +11:00
throw std::invalid_argument (std::string (name));
2012-04-26 18:22:05 +10:00
}
2016-03-21 14:21:14 +11:00
///////////////////////////////////////////////////////////////////////////////
const std::string&
cruft::log::to_string (level_t l)
2016-03-21 14:21:14 +11:00
{
switch (l) {
#define CASE(L) \
case cruft::log::L: { \
2016-03-21 14:21:14 +11:00
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&
cruft::log::operator<< (std::ostream& os, 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
///////////////////////////////////////////////////////////////////////////////
// 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 cruft::log::level_t
initial_log_level (void)
2016-01-20 15:36:32 +11:00
{
const char *env = getenv ("LOG_LEVEL");
if (!env)
return cruft::log::DEFAULT_LOG_LEVEL;
try {
2019-10-10 15:10:41 +11:00
return cruft::log::to_level (env);
} catch (...) {
std::clog << "Invalid environment LOG_LEVEL: '" << env << "'\n";
return cruft::log::DEFAULT_LOG_LEVEL;
}
}
//-----------------------------------------------------------------------------
// 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 cruft::log::level_t s_log_level_value;
//-----------------------------------------------------------------------------
cruft::log::level_t
cruft::log::log_level (level_t _level)
{
s_log_level_value = _level;
s_log_level_done = true;
return s_log_level_value;
}
//-----------------------------------------------------------------------------
cruft::log::level_t
cruft::log::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;
}
//-----------------------------------------------------------------------------
static bool
needs_break (cruft::log::level_t level)
{
static cruft::log::level_t break_level;
static bool has_level = [&] (void) {
const char *env = getenv ("BREAK_LEVEL");
if (!env)
return false;
try {
2019-10-10 15:10:41 +11:00
break_level = cruft::log::to_level (env);
return true;
} catch (...) {
return false;
}
} ();
return has_level && level <= break_level;
}
2016-03-21 14:21:14 +11:00
//-----------------------------------------------------------------------------
static
cruft::term::csi::graphics
level_colour (cruft::log::level_t level)
2016-03-21 14:21:14 +11:00
{
using cruft::term::csi::graphics;
2016-03-21 14:21:14 +11:00
switch (level) {
case cruft::log::EMERGENCY:
case cruft::log::ALERT:
case cruft::log::CRITICAL:
case cruft::log::ERROR:
2016-03-21 14:21:14 +11:00
return graphics (graphics::FOREGROUND, graphics::RED);
case cruft::log::WARNING:
2016-03-21 14:21:14 +11:00
return graphics (graphics::FOREGROUND, graphics::YELLOW);
case cruft::log::NOTICE:
case cruft::log::INFORMATIONAL:
2016-03-21 14:21:14 +11:00
return graphics (graphics::FOREGROUND, graphics::GREEN);
case cruft::log::DEBUG:
2016-03-21 14:21:14 +11:00
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 = cruft::max (to_string (i).size (), hi);
2016-03-21 14:21:14 +11:00
return hi;
} ();
return width;
}
2015-07-23 17:37:02 +10:00
///////////////////////////////////////////////////////////////////////////////
void
cruft::log::write (level_t level, const std::string &msg)
2019-10-10 15:10:41 +11:00
{
return write (level, std::string_view (msg));
}
//-----------------------------------------------------------------------------
void
cruft::log::write (level_t level, char const *msg)
{
return write (level, std::string_view (msg));
}
//-----------------------------------------------------------------------------
void
cruft::log::write (level_t level, std::string_view msg)
2016-01-20 15:36:32 +11:00
{
if (level <= log_level ()) {
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;
}
std::clog << time_string << " ["
<< level_colour (level)
<< std::setw (cruft::cast::lossless<int> (level_width ()))
<< std::left
<< level
<< std::setw (0)
<< cruft::term::csi::graphics::RESET
<< "] " << msg << std::endl;
}
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
///////////////////////////////////////////////////////////////////////////////
cruft::log::scoped_logger::scoped_logger (
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
//-----------------------------------------------------------------------------
cruft::log::scoped_logger::~scoped_logger ()
2015-07-23 17:37:02 +10:00
{
write (m_level, m_message);
2013-03-05 21:38:51 +11:00
}
2015-07-23 17:37:43 +10:00
///////////////////////////////////////////////////////////////////////////////
cruft::log::scoped_timer::scoped_timer (
cruft::log::level_t _level,
std::string _message
):
2015-07-23 17:37:43 +10:00
m_level (_level),
m_message (std::move (_message)),
m_start (cruft::nanoseconds ())
2015-07-23 17:37:43 +10:00
{ ; }
//-----------------------------------------------------------------------------
cruft::log::scoped_timer::~scoped_timer ()
2015-07-23 17:37:43 +10:00
{
auto finish = cruft::nanoseconds ();
2015-07-23 17:37:43 +10:00
auto duration = finish - m_start;
write (
m_level,
"%fs, %s",
float (duration) / 1'000'000'000.f,
m_message
);
2015-07-23 17:37:43 +10:00
}