mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-09 10:39:02 -04:00
Rewrite the entire logging system.
- Much cleaner API for writing to the log. - Adds support for stderr and stdout logging to the core. - Adds support for sql and syslog logging in modules.
This commit is contained in:
parent
0df16f0144
commit
c382faf9c9
2
.github/workflows/ci-alpine.yml
vendored
2
.github/workflows/ci-alpine.yml
vendored
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Run configure
|
||||
run: |
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap log_syslog mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --development --disable-auto-extras --disable-ownership --socketengine ${{ matrix.socketengine }}
|
||||
|
||||
- name: Build core
|
||||
|
2
.github/workflows/ci-linux.yml
vendored
2
.github/workflows/ci-linux.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- name: Run configure
|
||||
run: |
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap log_syslog mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --development --disable-auto-extras --socketengine ${{ matrix.socketengine }}
|
||||
|
||||
- name: Build core
|
||||
|
2
.github/workflows/ci-macos.yml
vendored
2
.github/workflows/ci-macos.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
- name: Run configure
|
||||
run: |
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --enable-extras "argon2 geo_maxmind ldap log_syslog mysql pgsql regex_pcre regex_posix regex_re2 sqlite3 ssl_gnutls ssl_mbedtls ssl_openssl sslrehashsignal"
|
||||
./configure --development --disable-auto-extras --socketengine ${{ matrix.socketengine }}
|
||||
|
||||
- name: Build core
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,6 +17,7 @@
|
||||
/src/modules/m_argon2.cpp
|
||||
/src/modules/m_geo_maxmind.cpp
|
||||
/src/modules/m_ldap.cpp
|
||||
/src/modules/m_log_syslog.cpp
|
||||
/src/modules/m_mysql.cpp
|
||||
/src/modules/m_pgsql.cpp
|
||||
/src/modules/m_regex_pcre.cpp
|
||||
|
3
configure
vendored
3
configure
vendored
@ -395,7 +395,8 @@ if (prompt_bool $interactive, $question, 0) {
|
||||
'm_mysql.cpp' => 'mysql_config --version',
|
||||
'm_pgsql.cpp' => 'pg_config --version',
|
||||
'm_ldap.cpp' => "echo '#include <ldap.h>' | $config{CXX} -E -",
|
||||
'm_regex_pcre.cpp' => 'pkg-config --exists libpcre2-8',
|
||||
'm_log_syslog.cpp' => undef,
|
||||
'm_regex_pcre.cpp' => 'pkg-config --exists libpcre2-8',
|
||||
'm_regex_posix.cpp' => undef,
|
||||
'm_regex_re2.cpp' => 'pkg-config --exists re2',
|
||||
'm_sqlite3.cpp' => 'pkg-config --exists sqlite3',
|
||||
|
@ -865,56 +865,39 @@
|
||||
# where you do not have the ability to set build time configuration. #
|
||||
#<path configdir="conf" datadir="data" logdir="logs" moduledir="modules">
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# Logging
|
||||
# -------
|
||||
#
|
||||
# Logging is covered with the <log> tag, which you may use to change
|
||||
# the behaviour of the logging of the IRCd.
|
||||
#
|
||||
# An example log tag would be:
|
||||
# <log method="file" type="OPER" level="default" target="opers.log">
|
||||
# which would log all information on /OPER (failed and successful) to
|
||||
# a file called opers.log.
|
||||
#
|
||||
# There are many different types which may be used, and modules may
|
||||
# generate their own. A list of useful types:
|
||||
# - USERS - information relating to user connection and disconnection
|
||||
# - OPER - successful and failed oper attempts
|
||||
# - KILL - kill related messages
|
||||
# - FILTER - messages related to filter matches (filter module)
|
||||
# - CONFIG - configuration related messages
|
||||
# - COMMAND - die and restart messages, and messages related to unknown user types
|
||||
# - SOCKET - socket engine informational/error messages
|
||||
# - MODULE - module related messages
|
||||
# - STARTUP - messages related to starting up the server
|
||||
#
|
||||
# You may also log *everything* by using a type of *, and subtract things out
|
||||
# of that by using -TYPE - for example "* -USERINPUT -USEROUTPUT".
|
||||
#
|
||||
# Useful levels are:
|
||||
# - default (general messages, including errors)
|
||||
# - sparse (misc error messages)
|
||||
# - debug (debug messages)
|
||||
#
|
||||
# Some types only produce output in the debug level, those are:
|
||||
# - BANCACHE - ban cache debug messages
|
||||
# - CHANNELS - information relating to joining/creating channels
|
||||
# - CULLLIST - debug messages related to issues with removing users
|
||||
# - RESOLVER - DNS related debug messages
|
||||
# - CONNECTCLASS - Connection class debug messages
|
||||
# - USERINPUT
|
||||
# - USEROUTPUT
|
||||
#
|
||||
# If your server is producing a high levels of log messages you can also set the
|
||||
# flush="[positive number]" attribute to specify how many log messages should be
|
||||
# buffered before flushing to disk. You should probably not specify this unless
|
||||
# you are having problems.
|
||||
#
|
||||
# The following log tag is highly default and uncustomised. It is recommended you
|
||||
# sort out your own log tags. This is just here so you get some output.
|
||||
#-#-#-#-#-#-#-#-#-#-#-# LOGGING CONFIGURATION #-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# #
|
||||
# The <log> tag allows you to define a list of targets to write log #
|
||||
# messages to. #
|
||||
# #
|
||||
# method - The method to use when logging. This can be set to "file" #
|
||||
# to log to a file, "stderr" to log to the standard error #
|
||||
# stream, or "stdout" to log to the standard output stream. #
|
||||
# You can also set it to a log method provided by a module. #
|
||||
# #
|
||||
# level - The level of messages to write to this logger. Can be set #
|
||||
# to "error", "warning", "normal", or "debug". #
|
||||
# #
|
||||
# type - A space-delimited list of log types to write to this logger. #
|
||||
# See https://docs.inspircd.org/4/configuration/#log for a #
|
||||
# full list of log types. You can also use * to include every #
|
||||
# log type and then -TYPE to exclude specific unwanted types. #
|
||||
# #
|
||||
# target - If the method is set to "file" then the name of the file #
|
||||
# to write log messages to. #
|
||||
|
||||
<log method="file" type="* -USERINPUT -USEROUTPUT" level="default" target="ircd.log">
|
||||
<log method="file"
|
||||
level="normal"
|
||||
type="* -USERINPUT -USEROUTPUT"
|
||||
target="inspircd.log">
|
||||
|
||||
#<log method="stderr"
|
||||
# level="normal"
|
||||
# type="* -USERINPUT -USEROUTPUT">
|
||||
|
||||
#<log method="stdout"
|
||||
# level="normal"
|
||||
# type="* -USERINPUT -USEROUTPUT">
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#- WHOWAS OPTIONS -#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# #
|
||||
|
@ -1366,6 +1366,32 @@
|
||||
# opers are to be authenticated via LDAP, so in case this module is #
|
||||
# not loaded the oper accounts are still protected by a password. #
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# SQL logging module: Allows writing messages to an SQL database.. #
|
||||
#<module name="log_sql">
|
||||
#
|
||||
# This module adds the following fields to the <log> tag:
|
||||
#
|
||||
# dbid - The id for the <database> tag that defines your database
|
||||
# connection details.
|
||||
# query - A custom query to use when inserting logs into the database.
|
||||
#
|
||||
#<log method="sql"
|
||||
# level="normal"
|
||||
# type="* -USERINPUT -USEROUTPUT"
|
||||
# dbid="sql-log"
|
||||
# query="INSERT INTO ircd_log (time, type, message) VALUES (FROM_UNIXTIME($time), '$type', '$message');">
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# Syslog logging module: Allows writing messages to the system log. #
|
||||
# This module is in extras. Re-run configure with: #
|
||||
# ./configure --enable-extras log_syslog
|
||||
#<module name="log_syslog">
|
||||
#
|
||||
#<log method="syslog"
|
||||
# level="normal"
|
||||
# type="* -USERINPUT -USEROUTPUT">
|
||||
|
||||
#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
|
||||
# Map hiding module: replaces /MAP and /LINKS output to users with a #
|
||||
# message to see a website, set by maphide="https://test.org/map" in #
|
||||
|
6
docs/sql/log_sql/mysql.sql
Normal file
6
docs/sql/log_sql/mysql.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS `ircd_log` (
|
||||
`time` datetime,
|
||||
`type` varchar(50),
|
||||
`message` text
|
||||
);
|
||||
|
6
docs/sql/log_sql/pgsql.sql
Normal file
6
docs/sql/log_sql/pgsql.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS "ircd_log" (
|
||||
"time" datetime,
|
||||
"type" varchar(50),
|
||||
"message" text
|
||||
);
|
||||
|
5
docs/sql/log_sql/schema.dbml
Normal file
5
docs/sql/log_sql/schema.dbml
Normal file
@ -0,0 +1,5 @@
|
||||
Table ircd_log {
|
||||
time datetime
|
||||
type varchar(50)
|
||||
message text
|
||||
}
|
6
docs/sql/log_sql/sqlite.sql
Normal file
6
docs/sql/log_sql/sqlite.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE "ircd_log" (
|
||||
"time" datetime,
|
||||
"type" varchar(50),
|
||||
"message" text
|
||||
);
|
||||
|
@ -103,6 +103,7 @@ CoreExport extern InspIRCd* ServerInstance;
|
||||
#include "inspstring.h"
|
||||
#include "protocol.h"
|
||||
#include "bancache.h"
|
||||
#include "logging.h"
|
||||
|
||||
/** This class contains various STATS counters
|
||||
* It is used by the InspIRCd class, which internally
|
||||
@ -226,7 +227,7 @@ public:
|
||||
|
||||
/** LogManager handles logging.
|
||||
*/
|
||||
LogManager Logs;
|
||||
Log::Manager Logs;
|
||||
|
||||
/** ModuleManager contains everything related to loading/unloading
|
||||
* modules.
|
||||
|
350
include/logging.h
Normal file
350
include/logging.h
Normal file
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* InspIRCd -- Internet Relay Chat Daemon
|
||||
*
|
||||
* Copyright (C) 2022 Sadie Powell <sadie@witchery.services>
|
||||
*
|
||||
* This file is part of InspIRCd. InspIRCd is free software: you can
|
||||
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Log
|
||||
{
|
||||
class Method;
|
||||
class FileMethod;
|
||||
|
||||
class Engine;
|
||||
class FileEngine;
|
||||
class StreamEngine;
|
||||
|
||||
class Manager;
|
||||
|
||||
typedef std::shared_ptr<Method> MethodPtr;
|
||||
|
||||
enum class Level
|
||||
: uint8_t
|
||||
{
|
||||
/** A critical message which must be investigated. */
|
||||
ERROR = 0,
|
||||
|
||||
/** An important message which should be investigated. */
|
||||
WARNING = 1,
|
||||
|
||||
/** A general message which is useful to have on record. */
|
||||
NORMAL = 2,
|
||||
|
||||
/** A debug message that we might want to store when testing. */
|
||||
DEBUG = 3,
|
||||
|
||||
/** A sensitive message that we should not store lightly. */
|
||||
RAWIO = 4,
|
||||
};
|
||||
}
|
||||
|
||||
/** Base class for logging methods. */
|
||||
class CoreExport Log::Method
|
||||
: public Cullable
|
||||
{
|
||||
public:
|
||||
virtual ~Method() = default;
|
||||
|
||||
/** Determines whether this logging method accepts cached messages. */
|
||||
virtual bool AcceptsCachedMessages() const { return true; }
|
||||
|
||||
/** Writes a message to the logger.
|
||||
* @param level The level at which the log message was written.
|
||||
* @param type The component which wrote the log message.
|
||||
* @param message The message which was written to the log.
|
||||
*/
|
||||
virtual void OnLog(Level level, const std::string& type, const std::string& message) = 0;
|
||||
};
|
||||
|
||||
/** A logger that writes to a file stream. */
|
||||
class CoreExport Log::FileMethod final
|
||||
: public Method
|
||||
{
|
||||
private:
|
||||
/** Whether to autoclose the file on exit. */
|
||||
bool autoclose;
|
||||
|
||||
/** The file to which the log is written. */
|
||||
FILE* file;
|
||||
|
||||
/** How often the file stream should be flushed. */
|
||||
const unsigned long flush;
|
||||
|
||||
/** The number of lines which have been written since the file stream was created. */
|
||||
unsigned long lines = 0;
|
||||
|
||||
/** The name the underlying file. */
|
||||
const std::string name;
|
||||
|
||||
public:
|
||||
FileMethod(const std::string& n, FILE* fh, unsigned long fl, bool ac);
|
||||
~FileMethod() override;
|
||||
|
||||
/** @copydoc Log::Method::AcceptsCachedMessages */
|
||||
bool AcceptsCachedMessages() const override { return false; }
|
||||
|
||||
/** @copydoc Log::Method::OnLog */
|
||||
void OnLog(Level level, const std::string& type, const std::string& message) override;
|
||||
};
|
||||
|
||||
/** Base class for logging engines. */
|
||||
class CoreExport Log::Engine
|
||||
: public DataProvider
|
||||
{
|
||||
protected:
|
||||
Engine(Module* Creator, const std::string& Name);
|
||||
|
||||
public:
|
||||
virtual ~Engine();
|
||||
|
||||
/** Creates a new logger from the specified config.
|
||||
* @param tag The config tag to configure the logger with.
|
||||
*/
|
||||
virtual MethodPtr Create(std::shared_ptr<ConfigTag> tag) = 0;
|
||||
|
||||
};
|
||||
|
||||
/** A logger which writes to a file. */
|
||||
class CoreExport Log::FileEngine final
|
||||
: public Engine
|
||||
{
|
||||
public:
|
||||
FileEngine(Module* Creator);
|
||||
|
||||
/** @copydoc Log::Engine::Create */
|
||||
MethodPtr Create(std::shared_ptr<ConfigTag> tag) override;
|
||||
};
|
||||
|
||||
/** A logger which writes to a stream. */
|
||||
class CoreExport Log::StreamEngine final
|
||||
: public Engine
|
||||
{
|
||||
private:
|
||||
FILE* file;
|
||||
|
||||
public:
|
||||
StreamEngine(Module* Creator, const std::string& Name, FILE* fh);
|
||||
|
||||
/** @copydoc Log::Engine::Create */
|
||||
MethodPtr Create(std::shared_ptr<ConfigTag> tag) override;
|
||||
};
|
||||
|
||||
/** Manager for the logging system. */
|
||||
class CoreExport Log::Manager final
|
||||
{
|
||||
private:
|
||||
/** A log message which has been cached until modules load. */
|
||||
struct CachedMessage final
|
||||
{
|
||||
/** The level the message was logged at. */
|
||||
Level level;
|
||||
|
||||
/** The type of the message that was logged. */
|
||||
std::string type;
|
||||
|
||||
/** The message that was logged. */
|
||||
std::string message;
|
||||
|
||||
CachedMessage(Level l, const std::string& t, const std::string& m);
|
||||
};
|
||||
|
||||
/** Encapsulates information about a logger. */
|
||||
struct Info final
|
||||
{
|
||||
/** Whether the logger was read from the server config. */
|
||||
bool config;
|
||||
|
||||
/** The minimum log level that this logger accepts. */
|
||||
Level level;
|
||||
|
||||
/** The types of log message that this logger accepts. */
|
||||
TokenList types;
|
||||
|
||||
/** The handler for this logger type. */
|
||||
MethodPtr method;
|
||||
|
||||
/** The engine which created this logger. */
|
||||
const Engine* engine;
|
||||
|
||||
Info(Level l, TokenList t, MethodPtr m, bool c, const Engine* e);
|
||||
};
|
||||
|
||||
/** The log messages we have cached for modules. */
|
||||
std::vector<CachedMessage> cache;
|
||||
|
||||
/** Whether we have just started up and need to cache messages until modules are loaded. */
|
||||
bool caching = true;
|
||||
|
||||
/** The logger engine that writes to a file. */
|
||||
Log::FileEngine filelog;
|
||||
|
||||
/** A logger that writes to stderr. */
|
||||
Log::StreamEngine stderrlog;
|
||||
|
||||
/** A logger that writes to stdout. */
|
||||
Log::StreamEngine stdoutlog;
|
||||
|
||||
/** The currently registered loggers. */
|
||||
std::vector<Info> loggers;
|
||||
|
||||
/** Whether we are currently logging to a file. */
|
||||
bool logging = false;
|
||||
|
||||
/** Writes a message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param message The message to log.
|
||||
* */
|
||||
void Write(Level level, const std::string& type, const std::string& message);
|
||||
|
||||
/** Writes a message to the server logs.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format The message to format and then log.
|
||||
* @param args The arguments to use when formatting the log message
|
||||
* */
|
||||
void Write(Level level, const std::string& type, const char* format, va_list& args) ATTR_NOT_NULL(4) ATTR_PRINTF(4, 0);
|
||||
|
||||
public:
|
||||
Manager();
|
||||
|
||||
/** Closes all loggers which were opened from the config. */
|
||||
void CloseLogs();
|
||||
|
||||
/** Enables writing rawio logs to the standard output stream. */
|
||||
void EnableDebugMode();
|
||||
|
||||
/** Opens loggers that are specified in the config. */
|
||||
void OpenLogs(bool requiremethods);
|
||||
|
||||
/** Registers the core logging services with the event system. */
|
||||
void RegisterServices();
|
||||
|
||||
/** Unloads all loggers that are provided by the specified engine.
|
||||
* @param engine The engine to unload the loggers of.
|
||||
*/
|
||||
void UnloadEngine(const Engine* engine);
|
||||
|
||||
/** Writes an error message to the server log.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Error(const std::string& type, const std::string& message)
|
||||
{
|
||||
Write(Level::ERROR, type, message);
|
||||
}
|
||||
|
||||
/** Writes an error message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Error(const std::string& type, const char* format, ...) ATTR_NOT_NULL(3) ATTR_PRINTF(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Write(Level::ERROR, type, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/** Writes a warning message to the server log.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Warning(const std::string& type, const std::string& message)
|
||||
{
|
||||
Write(Level::WARNING, type, message);
|
||||
}
|
||||
|
||||
/** Writes a warning message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Warning(const std::string& type, const char *format, ...) ATTR_NOT_NULL(3) ATTR_PRINTF(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Write(Level::WARNING, type, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/** Writes a normal message to the server log.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Normal(const std::string& type, const std::string& message)
|
||||
{
|
||||
Write(Level::NORMAL, type, message);
|
||||
}
|
||||
|
||||
/** Writes a normal message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Normal(const std::string& type, const char *format, ...) ATTR_NOT_NULL(3) ATTR_PRINTF(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Write(Level::NORMAL, type, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/** Writes a debug message to the server log.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Debug(const std::string& type, const std::string& message)
|
||||
{
|
||||
Write(Level::DEBUG, type, message);
|
||||
}
|
||||
/** Writes a debug message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void Debug(const std::string& type, const char *format, ...) ATTR_NOT_NULL(3) ATTR_PRINTF(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Write(Level::DEBUG, type, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/** Writes a raw I/O message to the server log.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void RawIO(const std::string& type, const std::string& message)
|
||||
{
|
||||
Write(Level::RAWIO, type, message);
|
||||
}
|
||||
|
||||
/** Writes a raw I/O message to the server log.
|
||||
* @param level The level to log at.
|
||||
* @param type The type of message that is being logged.
|
||||
* @param format A format string to format and then log.
|
||||
* */
|
||||
inline void RawIO(const std::string& type, const char *format, ...) ATTR_NOT_NULL(3) ATTR_PRINTF(3, 4)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
Write(Level::RAWIO, type, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
};
|
@ -670,8 +670,17 @@ void ConfigReaderThread::OnStop()
|
||||
// The description of this server may have changed - update it for WHOIS etc.
|
||||
ServerInstance->FakeClient->server->description = Config->ServerDesc;
|
||||
|
||||
ServerInstance->Logs.CloseLogs();
|
||||
ServerInstance->Logs.OpenFileLogs();
|
||||
try
|
||||
{
|
||||
ServerInstance->Logs.CloseLogs();
|
||||
ServerInstance->Logs.OpenLogs(true);
|
||||
}
|
||||
catch (CoreException& ex)
|
||||
{
|
||||
ServerInstance->Logs.Normal("LOG", "Cannot open log files: " + ex.GetReason());
|
||||
if (user)
|
||||
user->WriteNotice("Cannot open log files: " + ex.GetReason());
|
||||
}
|
||||
|
||||
if (Config->RawLog && !old->RawLog)
|
||||
ServerInstance->Users.ServerNoticeAll("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
|
||||
|
@ -500,13 +500,9 @@ InspIRCd::InspIRCd(int argc, char** argv)
|
||||
<< "See " << rang::style::bold << rang::fg::green << "/INFO" << rang::style::reset << " for contributors & authors" << std::endl
|
||||
<< std::endl;
|
||||
|
||||
Logs.RegisterServices();
|
||||
if (Config->cmdline.forcedebug)
|
||||
{
|
||||
FILE* newstdout = fdopen(dup(STDOUT_FILENO), "w");
|
||||
FileWriter* fw = new FileWriter(newstdout, 1);
|
||||
FileLogStream* fls = new FileLogStream(LOG_RAWIO, fw);
|
||||
Logs.AddLogTypes("*", fls, true);
|
||||
}
|
||||
Logs.EnableDebugMode();
|
||||
|
||||
if (!FindConfigFile(ConfigFileName))
|
||||
{
|
||||
@ -528,7 +524,17 @@ InspIRCd::InspIRCd(int argc, char** argv)
|
||||
*/
|
||||
this->Config->Read();
|
||||
this->Config->Apply(NULL, "");
|
||||
Logs.OpenFileLogs();
|
||||
|
||||
try
|
||||
{
|
||||
Logs.CloseLogs();
|
||||
Logs.OpenLogs(false);
|
||||
}
|
||||
catch (const CoreException& ex)
|
||||
{
|
||||
std::cout << "ERROR: Cannot open log files: " << ex.GetReason() << std::endl << "Exiting..." << std::endl;
|
||||
Exit(EXIT_STATUS_LOG);
|
||||
}
|
||||
|
||||
// If we don't have a SID, generate one based on the server name and the server description
|
||||
if (Config->sid.empty())
|
||||
@ -548,6 +554,17 @@ InspIRCd::InspIRCd(int argc, char** argv)
|
||||
TryBindPorts();
|
||||
|
||||
this->Modules.LoadAll();
|
||||
try
|
||||
{
|
||||
// We reopen logs again after modules to allow module loggers to have a chance to register.
|
||||
Logs.CloseLogs();
|
||||
Logs.OpenLogs(true);
|
||||
}
|
||||
catch (const CoreException& ex)
|
||||
{
|
||||
std::cout << "ERROR: Cannot open log files: " << ex.GetReason() << std::endl << "Exiting..." << std::endl;
|
||||
Exit(EXIT_STATUS_LOG);
|
||||
}
|
||||
|
||||
std::cout << "InspIRCd is now running as '" << Config->ServerName << "'[" << Config->GetSID() << "] with " << SocketEngine::GetMaxFds() << " max open sockets" << std::endl;
|
||||
|
||||
|
247
src/logging.cpp
Normal file
247
src/logging.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* InspIRCd -- Internet Relay Chat Daemon
|
||||
*
|
||||
* Copyright (C) 2022 Sadie Powell <sadie@witchery.services>
|
||||
*
|
||||
* This file is part of InspIRCd. InspIRCd is free software: you can
|
||||
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "inspircd.h"
|
||||
|
||||
Log::FileMethod::FileMethod(const std::string& n, FILE* fh, unsigned long fl, bool ac)
|
||||
: autoclose(ac)
|
||||
, file(fh)
|
||||
, flush(fl)
|
||||
, name(n)
|
||||
{
|
||||
}
|
||||
Log::FileMethod::~FileMethod()
|
||||
{
|
||||
if (autoclose)
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void Log::FileMethod::OnLog(Level level, const std::string& type, const std::string& message)
|
||||
{
|
||||
static time_t prevtime = 0;
|
||||
static std::string timestr;
|
||||
if (prevtime != ServerInstance->Time())
|
||||
{
|
||||
prevtime = ServerInstance->Time();
|
||||
timestr = InspIRCd::TimeString(prevtime);
|
||||
}
|
||||
|
||||
fputs(timestr.c_str(), file);
|
||||
fputs(" ", file);
|
||||
fputs(type.c_str(), file);
|
||||
fputs(": ", file);
|
||||
fputs(message.c_str(), file);
|
||||
#if defined _WIN32
|
||||
fputs("\r\n", file);
|
||||
#else
|
||||
fputs("\n", file);
|
||||
#endif
|
||||
|
||||
if (!(++lines % flush))
|
||||
fflush(file);
|
||||
|
||||
if (ferror(file))
|
||||
throw CoreException(InspIRCd::Format("Unable to write to %s: %s", name.c_str(), strerror(errno)));
|
||||
}
|
||||
|
||||
Log::Engine::Engine(Module* Creator, const std::string& Name)
|
||||
: DataProvider(Creator, "log/" + Name)
|
||||
{
|
||||
}
|
||||
|
||||
Log::Engine::~Engine()
|
||||
{
|
||||
if (creator)
|
||||
ServerInstance->Logs.UnloadEngine(this);
|
||||
}
|
||||
|
||||
Log::FileEngine::FileEngine(Module* Creator)
|
||||
: Engine(Creator, "file")
|
||||
{
|
||||
}
|
||||
|
||||
Log::MethodPtr Log::FileEngine::Create(std::shared_ptr<ConfigTag> tag)
|
||||
{
|
||||
const std::string target = tag->getString("target");
|
||||
if (target.empty())
|
||||
throw CoreException("<log:target> must be specified for file logger at " + tag->source.str());
|
||||
|
||||
const std::string fulltarget = ServerInstance->Config->Paths.PrependLog(InspIRCd::TimeString(ServerInstance->Time(), target.c_str()));
|
||||
FILE* fh = fopen(fulltarget.c_str(), "a");
|
||||
if (!fh)
|
||||
{
|
||||
throw CoreException(InspIRCd::Format("Unable to open %s for file logger at %s: %s",
|
||||
fulltarget.c_str(), tag->source.str().c_str(), strerror(errno)));
|
||||
}
|
||||
|
||||
const unsigned long flush = tag->getUInt("flush", 20, 1);
|
||||
return std::make_shared<FileMethod>(fulltarget, fh, flush, true);
|
||||
}
|
||||
|
||||
Log::StreamEngine::StreamEngine(Module* Creator, const std::string& Name, FILE* fh)
|
||||
: Engine(Creator, Name)
|
||||
, file(fh)
|
||||
{
|
||||
}
|
||||
|
||||
Log::MethodPtr Log::StreamEngine::Create(std::shared_ptr<ConfigTag> tag)
|
||||
{
|
||||
return std::make_shared<FileMethod>(name, file, 1, false);
|
||||
}
|
||||
|
||||
Log::Manager::CachedMessage::CachedMessage(Level l, const std::string& t, const std::string& m)
|
||||
: level(l)
|
||||
, type(t)
|
||||
, message(m)
|
||||
{
|
||||
}
|
||||
|
||||
Log::Manager::Info::Info(Level l, TokenList t, MethodPtr m, bool c, const Engine* e)
|
||||
: config(c)
|
||||
, level(l)
|
||||
, types(std::move(t))
|
||||
, method(std::move(m))
|
||||
, engine(e)
|
||||
{
|
||||
}
|
||||
|
||||
Log::Manager::Manager()
|
||||
: filelog(nullptr)
|
||||
, stderrlog(nullptr, "stderr", stderr)
|
||||
, stdoutlog(nullptr, "stdout", stdout)
|
||||
{
|
||||
}
|
||||
|
||||
void Log::Manager::CloseLogs()
|
||||
{
|
||||
size_t logger_count = loggers.size();
|
||||
loggers.erase(std::remove_if(loggers.begin(), loggers.end(), [](const Info& info) { return info.config; }), loggers.end());
|
||||
Normal("LOG", "Closing the logs; removed %zu/%zu loggers.", logger_count - loggers.size(), logger_count);
|
||||
|
||||
}
|
||||
|
||||
void Log::Manager::EnableDebugMode()
|
||||
{
|
||||
TokenList types = std::string("*");
|
||||
MethodPtr method = stdoutlog.Create(ServerInstance->Config->EmptyTag);
|
||||
loggers.emplace_back(Level::RAWIO, std::move(types), std::move(method), false, &stdoutlog);
|
||||
}
|
||||
|
||||
void Log::Manager::OpenLogs(bool requiremethods)
|
||||
{
|
||||
// If the server is started in debug mode we don't write logs.
|
||||
if (ServerInstance->Config->cmdline.forcedebug)
|
||||
{
|
||||
Normal("LOG", "Not opening loggers because we were started with --debug");
|
||||
ServerInstance->Config->RawLog = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the server is started with logging disabled we don't write logs.
|
||||
if (!ServerInstance->Config->cmdline.writelog)
|
||||
{
|
||||
Normal("LOG", "Not opening loggers because we were started with --nolog");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& [_, tag] : ServerInstance->Config->ConfTags("log"))
|
||||
{
|
||||
const std::string methodstr = tag->getString("method", "file", 1);
|
||||
Log::Engine* engine = ServerInstance->Modules.FindDataService<Log::Engine>("log/" + methodstr);
|
||||
if (!engine)
|
||||
{
|
||||
if (!requiremethods)
|
||||
continue; // We will open this later.
|
||||
|
||||
throw CoreException(methodstr + " is not a valid logging method at " + tag->source.str());
|
||||
}
|
||||
|
||||
const Level level = tag->getEnum("level", Level::NORMAL, {
|
||||
{ "error", Level::ERROR },
|
||||
{ "warning", Level::WARNING },
|
||||
{ "normal", Level::NORMAL },
|
||||
{ "debug", Level::DEBUG },
|
||||
{ "rawio", Level::RAWIO },
|
||||
|
||||
// Deprecated v3 names.
|
||||
{ "sparse", Level::ERROR },
|
||||
{ "verbose", Level::WARNING },
|
||||
{ "default", Level::NORMAL },
|
||||
|
||||
});
|
||||
TokenList types = tag->getString("type", "*", 1);
|
||||
MethodPtr method = engine->Create(tag);
|
||||
loggers.emplace_back(level, std::move(types), method, true, engine);
|
||||
}
|
||||
|
||||
if (requiremethods && caching)
|
||||
{
|
||||
// The server has finished starting up so we can write out any cached log messages.
|
||||
for (const auto& logger : loggers)
|
||||
{
|
||||
if (!logger.method->AcceptsCachedMessages())
|
||||
continue; // Does not support logging.
|
||||
|
||||
for (const auto& message : cache)
|
||||
{
|
||||
if (logger.level >= message.level && logger.types.Contains(message.type))
|
||||
logger.method->OnLog(message.level, message.type, message.message);
|
||||
}
|
||||
}
|
||||
|
||||
cache.clear();
|
||||
cache.shrink_to_fit();
|
||||
caching = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Manager::RegisterServices()
|
||||
{
|
||||
ServiceProvider* coreloggers[] = { &filelog, &stderrlog, &stdoutlog };
|
||||
ServerInstance->Modules.AddServices(coreloggers, sizeof(coreloggers)/sizeof(ServiceProvider*));
|
||||
}
|
||||
|
||||
void Log::Manager::UnloadEngine(const Engine* engine)
|
||||
{
|
||||
size_t logger_count = loggers.size();
|
||||
loggers.erase(std::remove_if(loggers.begin(), loggers.end(), [&engine](const Info& info) { return info.engine == engine; }), loggers.end());
|
||||
Normal("LOG", "The %s log engine is unloading; removed %zu/%zu loggers.", engine->name.c_str(), logger_count - loggers.size(), logger_count);
|
||||
}
|
||||
|
||||
void Log::Manager::Write(Level level, const std::string& type, const std::string& message)
|
||||
{
|
||||
if (logging)
|
||||
return; // Avoid log loops.
|
||||
|
||||
logging = true;
|
||||
for (const auto& logger : loggers)
|
||||
{
|
||||
if (logger.level >= level && logger.types.Contains(type))
|
||||
logger.method->OnLog(level, type, message);
|
||||
}
|
||||
|
||||
if (caching)
|
||||
cache.emplace_back(level, type, message);
|
||||
logging = false;
|
||||
}
|
||||
|
||||
void Log::Manager::Write(Level level, const std::string& type, const char* format, va_list& args)
|
||||
{
|
||||
Write(level, type, InspIRCd::Format(args, format));
|
||||
}
|
94
src/modules/extra/m_log_syslog.cpp
Normal file
94
src/modules/extra/m_log_syslog.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* InspIRCd -- Internet Relay Chat Daemon
|
||||
*
|
||||
* Copyright (C) 2022 Sadie Powell <sadie@witchery.services>
|
||||
*
|
||||
* This file is part of InspIRCd. InspIRCd is free software: you can
|
||||
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "inspircd.h"
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
class SyslogMethod final
|
||||
: public Log::Method
|
||||
{
|
||||
private:
|
||||
// Converts an InspIRCd log level to syslog priority.
|
||||
int LevelToPriority(Log::Level level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Log::Level::ERROR:
|
||||
return LOG_ERR;
|
||||
|
||||
case Log::Level::WARNING:
|
||||
return LOG_WARNING;
|
||||
|
||||
case Log::Level::NORMAL:
|
||||
return LOG_NOTICE;
|
||||
|
||||
case Log::Level::DEBUG:
|
||||
case Log::Level::RAWIO:
|
||||
return LOG_DEBUG;
|
||||
}
|
||||
|
||||
// Should never happen.
|
||||
return LOG_NOTICE;
|
||||
}
|
||||
|
||||
public:
|
||||
void OnLog(Log::Level level, const std::string& type, const std::string& message) override
|
||||
{
|
||||
syslog(LevelToPriority(level), "%s: %s", type.c_str(), message.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class SyslogEngine final
|
||||
: public Log::Engine
|
||||
{
|
||||
public:
|
||||
SyslogEngine(Module* Creator)
|
||||
: Log::Engine(Creator, "syslog")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Log::MethodPtr Create(std::shared_ptr<ConfigTag> tag) override
|
||||
{
|
||||
return std::make_shared<SyslogMethod>();
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleLogSyslog final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
SyslogEngine engine;
|
||||
|
||||
public:
|
||||
ModuleLogSyslog()
|
||||
: Module(VF_VENDOR, "Provides the ability to write logs to syslog.")
|
||||
, engine(this)
|
||||
{
|
||||
openlog("inspircd", LOG_NDELAY|LOG_PID, LOG_USER);
|
||||
}
|
||||
|
||||
~ModuleLogSyslog()
|
||||
{
|
||||
closelog();
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleLogSyslog)
|
118
src/modules/m_log_sql.cpp
Normal file
118
src/modules/m_log_sql.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* InspIRCd -- Internet Relay Chat Daemon
|
||||
*
|
||||
* Copyright (C) 2022 Sadie Powell <sadie@witchery.services>
|
||||
*
|
||||
* This file is part of InspIRCd. InspIRCd is free software: you can
|
||||
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "inspircd.h"
|
||||
#include "modules/sql.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
Module* thismod;
|
||||
}
|
||||
|
||||
class SQLQuery final
|
||||
: public SQL::Query
|
||||
{
|
||||
public:
|
||||
SQLQuery(Module* mod)
|
||||
: SQL::Query(mod)
|
||||
{
|
||||
}
|
||||
|
||||
void OnResult(SQL::Result& res) override
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
void OnError(SQL::Error& error) override
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('a', "Unable to write to SQL log (query error: %s).", error.ToString());
|
||||
}
|
||||
};
|
||||
|
||||
class SQLMethod final
|
||||
: public Log::Method
|
||||
{
|
||||
private:
|
||||
std::string query;
|
||||
dynamic_reference<SQL::Provider> sql;
|
||||
|
||||
public:
|
||||
SQLMethod(const dynamic_reference<SQL::Provider>& s, const std::string& q)
|
||||
: query(q)
|
||||
, sql(s)
|
||||
{
|
||||
}
|
||||
|
||||
void OnLog(Log::Level level, const std::string& type, const std::string& message) override
|
||||
{
|
||||
if (!sql)
|
||||
{
|
||||
ServerInstance->SNO.WriteGlobalSno('a', "Unable to write to SQL log (database %s not available).", sql->GetId().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
SQL::ParamMap params = {
|
||||
{ "level", ConvToStr(static_cast<uint8_t>(level)) },
|
||||
{ "message", message },
|
||||
{ "time", ConvToStr(ServerInstance->Time()) },
|
||||
{ "type", type },
|
||||
};
|
||||
sql->Submit(new SQLQuery(thismod), query, params);
|
||||
}
|
||||
};
|
||||
|
||||
class SQLEngine final
|
||||
: public Log::Engine
|
||||
{
|
||||
public:
|
||||
SQLEngine(Module* Creator)
|
||||
: Log::Engine(Creator, "sql")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Log::MethodPtr Create(std::shared_ptr<ConfigTag> tag) override
|
||||
{
|
||||
dynamic_reference<SQL::Provider> sql(creator, "SQL");
|
||||
const std::string dbid = tag->getString("dbid");
|
||||
if (!dbid.empty())
|
||||
sql.SetProvider("SQL/" + dbid);
|
||||
|
||||
const std::string query = tag->getString("query", "INSERT INTO ircd_log (time, type, message) VALUES (FROM_UNIXTIME($time), '$type', '$message');", 1);
|
||||
return std::make_shared<SQLMethod>(sql, query);
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleLogSQL final
|
||||
: public Module
|
||||
{
|
||||
private:
|
||||
SQLEngine engine;
|
||||
|
||||
public:
|
||||
ModuleLogSQL()
|
||||
: Module(VF_VENDOR, "Provides the ability to write logs to an SQL database.")
|
||||
, engine(this)
|
||||
{
|
||||
thismod = this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleLogSQL)
|
Loading…
x
Reference in New Issue
Block a user