0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-03 04:18:55 +01:00
construct/ircd/logger.cc

415 lines
10 KiB
C++
Raw Normal View History

/*
* charybdis: an advanced Internet Relay Chat Daemon(ircd).
*
* Copyright (C) 2003 Lee H <lee@leeh.co.uk>
* Copyright (C) 2003-2005 ircd-ratbox development team
* Copyright (C) 2008 William Pitcock <nenolod@sacredspiral.co.uk>
2016-08-26 06:38:15 +02:00
* Copyright (C) 2016 Charybdis Development Team
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1.Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2.Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3.The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace ircd::log
{
// Option toggles
std::array<bool, num_of<facility>()> file_flush;
std::array<bool, num_of<facility>()> console_flush;
std::array<const char *, num_of<facility>()> console_ansi;
// Runtime master switches
std::array<bool, num_of<facility>()> file_out;
std::array<bool, num_of<facility>()> console_out;
std::array<bool, num_of<facility>()> console_err;
// Suppression state (for struct console_quiet)
std::array<bool, num_of<facility>()> quieted_out;
std::array<bool, num_of<facility>()> quieted_err;
// Logfile name and device
std::array<const char *, num_of<facility>()> fname;
std::array<std::ofstream, num_of<facility>()> file;
/*
ConfEntry conf_log_table[] =
{
{ "file_critical", CF_QSTRING, NULL, PATH_MAX, &fname[CRITICAL] },
{ "file_error", CF_QSTRING, NULL, PATH_MAX, &fname[ERROR] },
{ "file_warning", CF_QSTRING, NULL, PATH_MAX, &fname[WARNING] },
{ "file_notice", CF_QSTRING, NULL, PATH_MAX, &fname[NOTICE] },
{ "file_info", CF_QSTRING, NULL, PATH_MAX, &fname[INFO] },
{ "file_debug", CF_QSTRING, NULL, PATH_MAX, &fname[DEBUG] },
};
*/
static void open(const facility &fac);
static void prefix(const facility &fac, const string_view &date);
static void suffix(const facility &fac);
}
2016-08-26 06:38:15 +02:00
void
ircd::log::init()
2016-08-26 06:38:15 +02:00
{
//TODO: XXX: config + cmd control + other fancy stuff
//add_top_conf("log", NULL, NULL, conf_log_table);
2016-08-26 06:38:15 +02:00
console_err[CRITICAL] = true;
console_err[ERROR] = true;
console_err[WARNING] = true;
console_err[NOTICE] = true;
console_out[INFO] = true;
console_out[DEBUG] = ircd::debugmode;
2016-08-26 06:38:15 +02:00
file_out[CRITICAL] = true;
file_out[ERROR] = true;
file_out[WARNING] = true;
file_out[NOTICE] = true;
file_out[INFO] = true;
file_out[DEBUG] = ircd::debugmode;
2016-08-26 06:38:15 +02:00
file_flush[CRITICAL] = true;
file_flush[ERROR] = true;
file_flush[WARNING] = true;
file_flush[NOTICE] = false;
file_flush[INFO] = false;
file_flush[DEBUG] = false;
console_flush[CRITICAL] = true;
console_flush[ERROR] = true;
console_flush[WARNING] = true;
console_flush[NOTICE] = false;
console_flush[INFO] = false;
console_flush[DEBUG] = true;
console_ansi[CRITICAL] = "\033[1;5;37;45m";
console_ansi[ERROR] = "\033[1;37;41m";
console_ansi[WARNING] = "\033[0;30;43m";
console_ansi[NOTICE] = "\033[1;37;46m";
console_ansi[INFO] = "\033[1;37;42m";
console_ansi[DEBUG] = "\033[1;30;47m";
}
void
ircd::log::fini()
{
//remove_top_conf("log");
}
void
ircd::log::open()
{
2016-08-26 06:38:15 +02:00
for_each<facility>([](const facility &fac)
{
if(!fname[fac])
return;
if(!file_out[fac])
return;
2016-08-26 06:38:15 +02:00
if(file[fac].is_open())
file[fac].close();
2016-08-26 06:38:15 +02:00
file[fac].clear();
file[fac].exceptions(std::ios::badbit | std::ios::failbit);
open(fac);
});
}
2016-08-26 06:38:15 +02:00
void
ircd::log::close()
2016-08-26 06:38:15 +02:00
{
for_each<facility>([](const facility &fac)
{
if(file[fac].is_open())
file[fac].close();
2016-08-26 06:38:15 +02:00
});
}
void
ircd::log::flush()
{
2016-08-26 06:38:15 +02:00
for_each<facility>([](const facility &fac)
{
2016-08-26 06:38:15 +02:00
file[fac].flush();
});
}
2016-08-26 06:38:15 +02:00
void
ircd::log::open(const facility &fac)
2016-08-26 06:38:15 +02:00
try
{
const auto &mode(std::ios::app);
file[fac].open(fname[fac], mode);
}
catch(const std::exception &e)
{
char buf[BUFSIZE];
2016-08-26 06:38:15 +02:00
snprintf(buf, sizeof(buf), "!!! Opening log file [%s] failed: %s",
fname[fac],
e.what());
2016-08-26 06:38:15 +02:00
std::cerr << buf << std::endl;
throw;
}
ircd::log::console_quiet::console_quiet(const bool &showmsg)
{
if(showmsg)
notice("Log messages are now quieted at the console");
std::copy(begin(console_out), end(console_out), begin(quieted_out));
std::copy(begin(console_err), end(console_err), begin(quieted_err));
std::fill(begin(console_out), end(console_out), false);
std::fill(begin(console_err), end(console_err), false);
}
ircd::log::console_quiet::~console_quiet()
{
std::copy(begin(quieted_out), end(quieted_out), begin(console_out));
std::copy(begin(quieted_err), end(quieted_err), begin(console_err));
}
ircd::log::log::log(const std::string &name)
2016-08-26 06:38:15 +02:00
:name{name}
{
}
ircd::log::log::log(const std::string &name,
2016-08-26 06:38:15 +02:00
const char &snote)
2016-11-29 16:23:38 +01:00
:log{name}
2016-08-26 06:38:15 +02:00
{
}
2016-08-26 06:38:15 +02:00
void
ircd::log::mark(const char *const &msg)
2016-08-26 06:38:15 +02:00
{
for_each<facility>([&msg]
(const auto &fac)
{
mark(fac, msg);
});
}
void
ircd::log::mark(const facility &fac,
const char *const &msg)
{
static const auto name{"*"s};
vlog(fac, name, "mark", {});
}
namespace ircd::log
{
void vlog_threadsafe(const facility &fac, const std::string &name, const char *const &fmt, const va_rtti &ap);
}
/// ircd::log is not thread-safe. This internal function is called when the
/// normal vlog() detects it's not on the main IRCd thread. It then generates
/// the formatted log message on this thread, and posts the message to the
/// main IRCd event loop which is running on the main thread.
void
ircd::log::vlog_threadsafe(const facility &fac,
const std::string &name,
const char *const &fmt,
const va_rtti &ap)
{
// Generate the formatted message on this thread first
std::string buf
{
fmt::vsnstringf(1024, fmt, ap)
};
// The reference to name has to be safely maintained
ircd::post([fac, buf(std::move(buf)), &name]
2016-08-26 06:38:15 +02:00
{
slog(fac, [&buf, &name]
(std::ostream &s)
{
s << std::left << std::setw(9) << name << std::right << " :" << buf;
});
2016-08-26 06:38:15 +02:00
});
}
2016-08-26 06:38:15 +02:00
void
ircd::log::vlog(const facility &fac,
const char *const &fmt,
const va_rtti &ap)
2016-08-26 06:38:15 +02:00
{
static const auto name{"ircd"s};
vlog(fac, name, fmt, ap);
2016-08-26 06:38:15 +02:00
}
2016-08-26 06:38:15 +02:00
void
ircd::log::vlog(const facility &fac,
const std::string &name,
const char *const &fmt,
const va_rtti &ap)
2016-08-26 06:38:15 +02:00
{
if(!is_main_thread())
{
vlog_threadsafe(fac, name, fmt, ap);
return;
}
2016-08-26 06:38:15 +02:00
char buf[1024];
2017-03-18 04:24:25 +01:00
fmt::vsnprintf(buf, sizeof(buf), fmt, ap);
2016-08-26 06:38:15 +02:00
slog(fac, [&buf, &name]
(std::ostream &s)
{
2017-03-18 04:24:25 +01:00
s << std::left << std::setw(9) << name << std::right << " :" << buf;
2016-08-26 06:38:15 +02:00
});
}
2016-08-26 06:38:15 +02:00
void
ircd::log::slog(const facility &fac,
const std::function<void (std::ostream &)> &closure)
{
2016-08-26 06:38:15 +02:00
if(!file[fac].is_open() && !console_out[fac] && !console_err[fac])
return;
assert_main_thread();
const unwind always_newline([&fac]
2016-08-26 06:38:15 +02:00
{
suffix(fac);
});
2016-08-26 06:38:15 +02:00
//TODO: XXX: Add option toggle for smalldate()
char date[64];
prefix(fac, microtime(date));
2016-08-26 06:38:15 +02:00
if(console_err[fac])
closure(std::cerr);
if(console_out[fac])
closure(std::cout);
if(file[fac].is_open())
closure(file[fac]);
}
void
ircd::log::prefix(const facility &fac,
const string_view &date)
{
2016-08-26 06:38:15 +02:00
const auto console_prefix([&fac, &date]
(auto &stream)
{
stream << date << ' ';
2016-08-26 06:38:15 +02:00
if(console_ansi[fac])
stream << console_ansi[fac];
2016-08-26 06:38:15 +02:00
stream << std::setw(8) << std::right << reflect(fac);
2016-08-26 06:38:15 +02:00
if(console_ansi[fac])
stream << "\033[0m ";
else
stream << ' ';
});
if(console_err[fac])
console_prefix(std::cerr);
if(console_out[fac])
console_prefix(std::cout);
file[fac] << date << ' ' << reflect(fac) << ' ';
}
void
ircd::log::suffix(const facility &fac)
{
2016-08-26 06:38:15 +02:00
const auto console_newline([&fac]
(auto &stream)
{
if(console_flush[fac])
stream << std::endl;
else
stream << '\n';
});
if(console_err[fac])
console_newline(std::cerr);
if(console_out[fac])
console_newline(std::cout);
// If the user's own code triggered an exception while streaming we still want to
// provide a newline. But if the exception is an actual log file exception don't
// touch the stream anymore.
if(unlikely(std::current_exception() && !file[fac].good()))
return;
2016-08-26 06:38:15 +02:00
if(!file[fac].is_open())
return;
2016-08-26 06:38:15 +02:00
if(file_flush[fac])
file[fac] << std::endl;
else
file[fac] << '\n';
}
const char *
ircd::log::reflect(const facility &f)
{
2016-08-26 06:38:15 +02:00
switch(f)
{
case facility::DEBUG: return "DEBUG";
case facility::INFO: return "INFO";
case facility::NOTICE: return "NOTICE";
case facility::WARNING: return "WARNING";
case facility::ERROR: return "ERROR";
case facility::CRITICAL: return "CRITICAL";
case facility::_NUM_: break; // Allows -Wswitch to remind developer to add reflection here
};
return "??????";
}
2016-08-13 05:05:54 +02:00
2016-08-26 06:38:15 +02:00
const char *
ircd::smalldate(const time_t &ltime)
{
2016-11-29 16:23:38 +01:00
static const size_t MAX_DATE_STRING
{
32 // maximum string length for a date string (ircd_defs.h)
};
2016-08-26 06:38:15 +02:00
struct tm lt;
localtime_r(&ltime, &lt);
2016-11-29 16:23:38 +01:00
static char buf[MAX_DATE_STRING];
2016-08-26 06:38:15 +02:00
snprintf(buf, sizeof(buf), "%d/%d/%d %02d.%02d",
lt.tm_year + 1900,
lt.tm_mon + 1,
lt.tm_mday,
lt.tm_hour,
lt.tm_min);
2016-08-26 06:38:15 +02:00
return buf;
}