log: modularise the logging infrastructure
This commit is contained in:
parent
2e16e8962b
commit
67ea686753
@ -423,8 +423,22 @@ list (
|
||||
list/sort.hpp
|
||||
log.hpp
|
||||
log/fwd.hpp
|
||||
log/log.hpp
|
||||
log/level.cpp
|
||||
log/level.hpp
|
||||
log/log.cpp
|
||||
log/log.hpp
|
||||
log/packet.cpp
|
||||
log/packet.hpp
|
||||
log/scoped.cpp
|
||||
log/scoped.hpp
|
||||
log/sink/base.cpp
|
||||
log/sink/base.hpp
|
||||
log/sink/console.cpp
|
||||
log/sink/console.hpp
|
||||
log/sink/null.cpp
|
||||
log/sink/null.hpp
|
||||
log/sink/ostream.cpp
|
||||
log/sink/ostream.hpp
|
||||
map/fixed.cpp
|
||||
map/fixed.hpp
|
||||
maths.cpp
|
||||
|
22
log/fwd.hpp
Normal file
22
log/fwd.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2019, Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace cruft::log {
|
||||
struct packet;
|
||||
|
||||
namespace sink {
|
||||
class base;
|
||||
|
||||
class console;
|
||||
class noop;
|
||||
class ostream;
|
||||
class tee;
|
||||
};
|
||||
}
|
138
log/level.cpp
Normal file
138
log/level.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2012-2016 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "level.hpp"
|
||||
|
||||
#include "../debug/assert.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// convert a string representation of a log-level into an enumeration value.
|
||||
///
|
||||
/// conversion is case insensitive
|
||||
/// throws std::range_error if unable to convert
|
||||
cruft::log::level_t
|
||||
cruft::log::to_level (std::string_view name)
|
||||
{
|
||||
if (std::empty (name))
|
||||
return cruft::log::EMERGENCY;
|
||||
|
||||
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 }
|
||||
};
|
||||
|
||||
for (auto const [key, val]: VALUES)
|
||||
if (!strcmp (upper.data (), key))
|
||||
return val;
|
||||
|
||||
throw std::invalid_argument (std::string (name));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const std::string&
|
||||
cruft::log::to_string (level_t l)
|
||||
{
|
||||
switch (l) {
|
||||
#define CASE(L) \
|
||||
case cruft::log::L: { \
|
||||
static const std::string STR = #L; \
|
||||
return STR; \
|
||||
}
|
||||
|
||||
MAP_LEVEL_T(CASE)
|
||||
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
cruft::log::operator<< (std::ostream& os, level_t l)
|
||||
{
|
||||
return os << to_string (l);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
{
|
||||
const char *env = getenv ("LOG_LEVEL");
|
||||
if (!env)
|
||||
return cruft::log::DEFAULT_LOG_LEVEL;
|
||||
|
||||
try {
|
||||
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;
|
||||
}
|
76
log/level.hpp
Normal file
76
log/level.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2012-2019 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cruft/util/preprocessor.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace cruft::log {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// rfc5424 log levels. It is assumed they are contiguous to simplify array
|
||||
// indexing in logging code.
|
||||
enum level_t {
|
||||
EMERGENCY, /** system is unusable */
|
||||
ALERT, /** action must be taken immediately */
|
||||
CRITICAL, /** critical conditions */
|
||||
ERROR, /** error conditions */
|
||||
WARNING,
|
||||
WARN = WARNING, /** warning conditions */
|
||||
NOTICE, /** normal but significant condition */
|
||||
INFORMATIONAL,
|
||||
INFO = INFORMATIONAL, /** informational messages */
|
||||
DEBUG /** debug-level messages */
|
||||
};
|
||||
|
||||
#define MAP_LEVEL_T(F) \
|
||||
MAP0(F, \
|
||||
EMERGENCY, \
|
||||
ALERT, \
|
||||
CRITICAL, \
|
||||
ERROR, \
|
||||
WARN, \
|
||||
NOTICE, \
|
||||
INFO, \
|
||||
DEBUG \
|
||||
)
|
||||
|
||||
constexpr auto DEFAULT_LOG_LEVEL = NOTICE;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
std::string const& to_string (level_t);
|
||||
level_t to_level (std::string_view);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
operator<< (std::ostream&, level_t);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Query or set the global logging level filter. If the severity passed to
|
||||
// a logging call is less than this then the output is discarded.
|
||||
//
|
||||
// The initial logging level can be controlled by setting the environment
|
||||
// variable LOG_LEVEL to a string corresponding to the names of the values
|
||||
// in level_t.
|
||||
//
|
||||
// Otherwise we fall back to the hard coded default from above.
|
||||
//
|
||||
// As a special case, a blank value for the environment variable LOG_LEVEL
|
||||
// corresponds to the maximum log level. It indicates a preference for
|
||||
// minimum output, and may be changed to disable all output in future
|
||||
// updates.
|
||||
level_t log_level (void);
|
||||
level_t log_level (level_t);
|
||||
|
||||
|
||||
bool needs_break (level_t);
|
||||
|
||||
}
|
267
log/log.cpp
267
log/log.cpp
@ -9,161 +9,21 @@
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
#include "../term.hpp"
|
||||
#include "../time.hpp"
|
||||
#include "../cast.hpp"
|
||||
#include "../string.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include "sink/console.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static constexpr
|
||||
cruft::log::level_t
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// convert a string representation of a log-level into an enumeration value.
|
||||
///
|
||||
/// conversion is case insensitive
|
||||
/// throws std::range_error if unable to convert
|
||||
cruft::log::level_t
|
||||
cruft::log::to_level (std::string_view name)
|
||||
bool
|
||||
cruft::log::needs_break (level_t level)
|
||||
{
|
||||
if (std::empty (name))
|
||||
return cruft::log::EMERGENCY;
|
||||
static level_t break_level;
|
||||
|
||||
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 }
|
||||
};
|
||||
|
||||
for (auto const [key, val]: VALUES)
|
||||
if (!strcmp (upper.data (), key))
|
||||
return val;
|
||||
|
||||
throw std::invalid_argument (std::string (name));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
const std::string&
|
||||
cruft::log::to_string (level_t l)
|
||||
{
|
||||
switch (l) {
|
||||
#define CASE(L) \
|
||||
case cruft::log::L: { \
|
||||
static const std::string STR = #L; \
|
||||
return STR; \
|
||||
}
|
||||
|
||||
MAP_LEVEL_T(CASE)
|
||||
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
cruft::log::operator<< (std::ostream& os, level_t l)
|
||||
{
|
||||
return os << to_string (l);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
{
|
||||
const char *env = getenv ("LOG_LEVEL");
|
||||
if (!env)
|
||||
return cruft::log::DEFAULT_LOG_LEVEL;
|
||||
|
||||
try {
|
||||
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)
|
||||
@ -181,50 +41,12 @@ needs_break (cruft::log::level_t level)
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static
|
||||
cruft::term::csi::graphics
|
||||
level_colour (cruft::log::level_t level)
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::log::sink::base&
|
||||
cruft::log::default_sink ()
|
||||
{
|
||||
using cruft::term::csi::graphics;
|
||||
|
||||
switch (level) {
|
||||
case cruft::log::EMERGENCY:
|
||||
case cruft::log::ALERT:
|
||||
case cruft::log::CRITICAL:
|
||||
case cruft::log::ERROR:
|
||||
return graphics (graphics::FOREGROUND, graphics::RED);
|
||||
|
||||
case cruft::log::WARNING:
|
||||
return graphics (graphics::FOREGROUND, graphics::YELLOW);
|
||||
|
||||
case cruft::log::NOTICE:
|
||||
case cruft::log::INFORMATIONAL:
|
||||
return graphics (graphics::FOREGROUND, graphics::GREEN);
|
||||
|
||||
case cruft::log::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 = cruft::max (to_string (i).size (), hi);
|
||||
|
||||
return hi;
|
||||
} ();
|
||||
|
||||
return width;
|
||||
static sink::console s_default_sink (PACKAGE_NAME);
|
||||
return s_default_sink;
|
||||
}
|
||||
|
||||
|
||||
@ -248,72 +70,7 @@ cruft::log::write (level_t level, char const *msg)
|
||||
void
|
||||
cruft::log::write (level_t level, std::string_view msg)
|
||||
{
|
||||
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 ();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::log::scoped_logger::scoped_logger (
|
||||
level_t _level,
|
||||
std::string _message
|
||||
):
|
||||
m_level (_level),
|
||||
m_message (std::move (_message))
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
cruft::log::scoped_logger::~scoped_logger ()
|
||||
{
|
||||
write (m_level, m_message);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::log::scoped_timer::scoped_timer (
|
||||
cruft::log::level_t _level,
|
||||
std::string _message
|
||||
):
|
||||
m_level (_level),
|
||||
m_message (std::move (_message)),
|
||||
m_start (cruft::nanoseconds ())
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
cruft::log::scoped_timer::~scoped_timer ()
|
||||
{
|
||||
auto finish = cruft::nanoseconds ();
|
||||
auto duration = finish - m_start;
|
||||
|
||||
write (
|
||||
m_level,
|
||||
"%fs, %s",
|
||||
float (duration) / 1'000'000'000.f,
|
||||
m_message
|
||||
return default_sink ().write (
|
||||
packet (level, msg)
|
||||
);
|
||||
}
|
||||
|
115
log/log.hpp
115
log/log.hpp
@ -8,9 +8,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "level.hpp"
|
||||
#include "packet.hpp"
|
||||
#include "sink/base.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#include <cruft/util/preprocessor.hpp>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
@ -22,52 +24,8 @@
|
||||
|
||||
namespace cruft::log {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// rfc5424 log levels. It is assumed they are contiguous to simplify array
|
||||
// indexing in logging code.
|
||||
enum level_t {
|
||||
EMERGENCY, /** system is unusable */
|
||||
ALERT, /** action must be taken immediately */
|
||||
CRITICAL, /** critical conditions */
|
||||
ERROR, /** error conditions */
|
||||
WARNING,
|
||||
WARN = WARNING, /** warning conditions */
|
||||
NOTICE, /** normal but significant condition */
|
||||
INFORMATIONAL,
|
||||
INFO = INFORMATIONAL, /** informational messages */
|
||||
DEBUG /** debug-level messages */
|
||||
};
|
||||
sink::base& default_sink (void);
|
||||
|
||||
#define MAP_LEVEL_T(F) MAP0(F, EMERGENCY, ALERT, CRITICAL, ERROR, WARN, NOTICE, INFO, DEBUG)
|
||||
|
||||
constexpr auto DEFAULT_LOG_LEVEL = NOTICE;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
const std::string& to_string (level_t);
|
||||
level_t to_level (std::string_view);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
std::ostream&
|
||||
operator<< (std::ostream&, level_t);
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Query or set the global logging level filter. If the severity passed to
|
||||
// a logging call is less than this then the output is discarded.
|
||||
//
|
||||
// The initial logging level can be controlled by setting the environment
|
||||
// variable LOG_LEVEL to a string corresponding to the names of the values
|
||||
// in level_t.
|
||||
//
|
||||
// Otherwise we fall back to the hard coded default from above.
|
||||
//
|
||||
// As a special case, a blank value for the environment variable LOG_LEVEL
|
||||
// corresponds to the maximum log level. It indicates a preference for
|
||||
// minimum output, and may be changed to disable all output in future
|
||||
// updates.
|
||||
level_t log_level (void);
|
||||
level_t log_level (level_t);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
void write (level_t, std::string const &msg);
|
||||
void write (level_t, char const *msg);
|
||||
void write (level_t, std::string_view msg);
|
||||
@ -78,29 +36,11 @@ namespace cruft::log {
|
||||
void
|
||||
write (level_t l, FormatT fmt, ArgsT &&...args)
|
||||
{
|
||||
write (
|
||||
l,
|
||||
to_string (
|
||||
format::printf (
|
||||
std::forward<FormatT> (fmt),
|
||||
std::forward<ArgsT> (args)...
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
template <typename ...Args, size_t N>
|
||||
void
|
||||
write (level_t l, const char (&fmt)[N], Args &&...args)
|
||||
{
|
||||
write (
|
||||
l,
|
||||
to_string (
|
||||
format::printf (fmt) (
|
||||
std::forward<Args> (args)...
|
||||
)
|
||||
default_sink ().write (
|
||||
packet (
|
||||
l,
|
||||
std::forward<FormatT> (fmt),
|
||||
std::forward<ArgsT> (args)...
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -126,41 +66,4 @@ namespace cruft::log {
|
||||
#else
|
||||
#define LOG_DEBUG(...) do { ; } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class scoped_logger {
|
||||
public:
|
||||
scoped_logger (level_t, std::string);
|
||||
~scoped_logger ();
|
||||
|
||||
scoped_logger (scoped_logger const&) = delete;
|
||||
scoped_logger& operator= (scoped_logger const&) = delete;
|
||||
|
||||
scoped_logger (scoped_logger &&) = delete;
|
||||
scoped_logger& operator= (scoped_logger &&) = delete;
|
||||
|
||||
protected:
|
||||
const level_t m_level;
|
||||
const std::string m_message;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class scoped_timer {
|
||||
public:
|
||||
scoped_timer (level_t, std::string);
|
||||
~scoped_timer ();
|
||||
|
||||
scoped_timer (scoped_timer const&) = delete;
|
||||
scoped_timer& operator= (scoped_timer const&) = delete;
|
||||
|
||||
scoped_timer (scoped_timer &&) = delete;
|
||||
scoped_timer& operator= (scoped_timer &&) = delete;
|
||||
|
||||
private:
|
||||
const level_t m_level;
|
||||
const std::string m_message;
|
||||
uint64_t m_start;
|
||||
};
|
||||
}
|
||||
|
0
log/packet.cpp
Normal file
0
log/packet.cpp
Normal file
43
log/packet.hpp
Normal file
43
log/packet.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "level.hpp"
|
||||
#include "../format.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
||||
namespace cruft::log {
|
||||
struct packet {
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
|
||||
packet (level_t _level, std::string _message)
|
||||
: level (_level)
|
||||
, message (std::move (_message))
|
||||
, timestamp (clock::now ())
|
||||
{ ; }
|
||||
|
||||
|
||||
template <typename FormatT, typename ...ArgsT>
|
||||
packet (
|
||||
level_t _level,
|
||||
FormatT _format,
|
||||
ArgsT &&..._args
|
||||
) : packet (
|
||||
_level,
|
||||
cruft::format::to_string (
|
||||
cruft::format::printf (
|
||||
std::forward<FormatT> (_format)
|
||||
) (
|
||||
std::forward<ArgsT> (_args)...
|
||||
)
|
||||
)
|
||||
)
|
||||
{ ; }
|
||||
|
||||
level_t level;
|
||||
std::string message;
|
||||
clock::time_point timestamp;
|
||||
};
|
||||
}
|
57
log/scoped.cpp
Normal file
57
log/scoped.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2012-2019 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "scoped.hpp"
|
||||
|
||||
#include "../time.hpp"
|
||||
|
||||
using cruft::log::scoped_logger;
|
||||
using cruft::log::scoped_timer;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::log::scoped_logger::scoped_logger (
|
||||
level_t _level,
|
||||
std::string _message
|
||||
):
|
||||
m_level (_level),
|
||||
m_message (std::move (_message))
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
cruft::log::scoped_logger::~scoped_logger ()
|
||||
{
|
||||
write (m_level, m_message);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
cruft::log::scoped_timer::scoped_timer (
|
||||
cruft::log::level_t _level,
|
||||
std::string _message
|
||||
):
|
||||
m_level (_level),
|
||||
m_message (std::move (_message)),
|
||||
m_start (cruft::nanoseconds ())
|
||||
{ ; }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
cruft::log::scoped_timer::~scoped_timer ()
|
||||
{
|
||||
auto finish = cruft::nanoseconds ();
|
||||
auto duration = finish - m_start;
|
||||
|
||||
write (
|
||||
m_level,
|
||||
"%fs, %s",
|
||||
float (duration) / 1'000'000'000.f,
|
||||
m_message
|
||||
);
|
||||
}
|
52
log/scoped.hpp
Normal file
52
log/scoped.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2012-2019 Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
namespace cruft::log {
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class scoped_logger {
|
||||
public:
|
||||
scoped_logger (level_t, std::string);
|
||||
~scoped_logger ();
|
||||
|
||||
scoped_logger (scoped_logger const&) = delete;
|
||||
scoped_logger& operator= (scoped_logger const&) = delete;
|
||||
|
||||
scoped_logger (scoped_logger &&) = delete;
|
||||
scoped_logger& operator= (scoped_logger &&) = delete;
|
||||
|
||||
protected:
|
||||
const level_t m_level;
|
||||
const std::string m_message;
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
class scoped_timer {
|
||||
public:
|
||||
scoped_timer (level_t, std::string);
|
||||
~scoped_timer ();
|
||||
|
||||
scoped_timer (scoped_timer const&) = delete;
|
||||
scoped_timer& operator= (scoped_timer const&) = delete;
|
||||
|
||||
scoped_timer (scoped_timer &&) = delete;
|
||||
scoped_timer& operator= (scoped_timer &&) = delete;
|
||||
|
||||
private:
|
||||
const level_t m_level;
|
||||
const std::string m_message;
|
||||
uint64_t m_start;
|
||||
};
|
||||
}
|
9
log/sink/base.cpp
Normal file
9
log/sink/base.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "base.hpp"
|
||||
|
||||
using cruft::log::sink::base;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
base::base (std::string _name)
|
||||
: m_name (std::move (_name))
|
||||
{ ; }
|
32
log/sink/base.hpp
Normal file
32
log/sink/base.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "../fwd.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace cruft::log::sink {
|
||||
class base {
|
||||
public:
|
||||
virtual ~base () = default;
|
||||
|
||||
base (std::string name);
|
||||
|
||||
std::string const& name (void);
|
||||
|
||||
virtual void write (packet const&) = 0;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
|
||||
template <typename DerivedT>
|
||||
class crtp : public base {
|
||||
public:
|
||||
template <typename ...ArgsT>
|
||||
crtp (ArgsT &&...args)
|
||||
: base (std::forward<ArgsT> (args)...)
|
||||
{ ; }
|
||||
};
|
||||
}
|
122
log/sink/console.cpp
Normal file
122
log/sink/console.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2019, Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#include "console.hpp"
|
||||
|
||||
#include "../level.hpp"
|
||||
#include "../packet.hpp"
|
||||
|
||||
#include "../../term.hpp"
|
||||
#include "../../debug/assert.hpp"
|
||||
#include "../../cast.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
|
||||
using cruft::log::sink::console;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static std::size_t
|
||||
level_width (void)
|
||||
{
|
||||
static constexpr
|
||||
cruft::log::level_t
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
static size_t width = [] {
|
||||
size_t hi = 0;
|
||||
|
||||
for (auto i: ALL_LEVELS)
|
||||
hi = cruft::max (to_string (i).size (), hi);
|
||||
|
||||
return hi;
|
||||
} ();
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
static
|
||||
cruft::term::csi::graphics
|
||||
level_colour (cruft::log::level_t level)
|
||||
{
|
||||
using cruft::term::csi::graphics;
|
||||
|
||||
switch (level) {
|
||||
case cruft::log::EMERGENCY:
|
||||
case cruft::log::ALERT:
|
||||
case cruft::log::CRITICAL:
|
||||
case cruft::log::ERROR:
|
||||
return graphics (graphics::FOREGROUND, graphics::RED);
|
||||
|
||||
case cruft::log::WARNING:
|
||||
return graphics (graphics::FOREGROUND, graphics::YELLOW);
|
||||
|
||||
case cruft::log::NOTICE:
|
||||
case cruft::log::INFORMATIONAL:
|
||||
return graphics (graphics::FOREGROUND, graphics::GREEN);
|
||||
|
||||
case cruft::log::DEBUG:
|
||||
return graphics (graphics::FOREGROUND, graphics::WHITE);
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
console::console (std::string name)
|
||||
: crtp (std::move (name))
|
||||
{ ; }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void
|
||||
console::write (packet const &val)
|
||||
{
|
||||
if (val.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 (val.level)
|
||||
<< std::setw (cruft::cast::lossless<int> (level_width ()))
|
||||
<< std::left
|
||||
<< val.level
|
||||
<< std::setw (0)
|
||||
<< cruft::term::csi::graphics::RESET
|
||||
<< "] " << val.message << std::endl;
|
||||
}
|
||||
|
||||
if (needs_break (val.level))
|
||||
breakpoint ();
|
||||
}
|
22
log/sink/console.hpp
Normal file
22
log/sink/console.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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/.
|
||||
*
|
||||
* Copyright 2019, Danny Robson <danny@nerdcruft.net>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base.hpp"
|
||||
|
||||
#include "ostream.hpp"
|
||||
|
||||
namespace cruft::log::sink {
|
||||
class console : public crtp<console> {
|
||||
public:
|
||||
console (std::string name);
|
||||
|
||||
virtual void write (packet const&);
|
||||
};
|
||||
}
|
0
log/sink/null.cpp
Normal file
0
log/sink/null.cpp
Normal file
0
log/sink/null.hpp
Normal file
0
log/sink/null.hpp
Normal file
0
log/sink/ostream.cpp
Normal file
0
log/sink/ostream.cpp
Normal file
0
log/sink/ostream.hpp
Normal file
0
log/sink/ostream.hpp
Normal file
0
log/sink/tee.cpp
Normal file
0
log/sink/tee.cpp
Normal file
0
log/sink/tee.hpp
Normal file
0
log/sink/tee.hpp
Normal file
@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "log.hpp"
|
||||
#include "log/log.hpp"
|
||||
#include "introspection.hpp"
|
||||
|
||||
#include <map>
|
||||
|
Loading…
Reference in New Issue
Block a user