Make logsink and filter configurable

This commit is contained in:
badaix 2020-04-18 15:32:30 +02:00
parent ad8332345f
commit a2311dfbfd
7 changed files with 242 additions and 85 deletions

View file

@ -49,10 +49,10 @@ struct ClientSettings
SharingMode sharing_mode{SharingMode::shared};
};
struct LoggingSettings
struct Logging
{
bool debug{false};
std::string debug_logfile{""};
std::string sink{""};
std::string filter{"*:info"};
};
size_t instance{1};
@ -60,7 +60,7 @@ struct ClientSettings
ServerSettings server;
PlayerSettings player;
LoggingSettings logging;
Logging logging;
};
#endif

View file

@ -94,7 +94,10 @@ int main(int argc, char** argv)
OptionParser op("Allowed options");
auto helpSwitch = op.add<Switch>("", "help", "produce help message");
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message");
auto debugOption = op.add<Implicit<string>, Attribute::hidden>("", "debug", "enable debug logging", ""); // TODO: &settings.logging.debug);
op.add<Value<string>>("", "logsink", "log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
auto logfilterOption = op.add<Value<string>>(
"", "logfilter", "log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
settings.logging.filter);
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
#if defined(HAS_ALSA) || defined(WINDOWS)
auto listSwitch = op.add<Switch>("l", "list", "list PCM devices");
@ -175,18 +178,42 @@ int main(int argc, char** argv)
// XXX: Only one metadata option must be set
// TODO: AixLog::Log::init<AixLog::SinkNative>("snapclient", AixLog::Severity::trace, AixLog::Type::special);
if (debugOption->is_set())
settings.logging.filter = logfilterOption->value();
if (logfilterOption->is_set())
{
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::trace, "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
if (!debugOption->value().empty())
AixLog::Log::instance().add_logsink<AixLog::SinkFile>(AixLog::Severity::trace, debugOption->value(),
"%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
for (size_t n = 1; n < logfilterOption->count(); ++n)
settings.logging.filter += "," + logfilterOption->value(n);
}
if (settings.logging.sink.empty())
{
settings.logging.sink = "stdout";
#ifdef HAS_DAEMON
if (daemonOption->is_set())
settings.logging.sink = "system";
#endif
}
AixLog::Filter logfilter;
auto filters = utils::string::split(settings.logging.filter, ',');
for (const auto& filter : filters)
logfilter.add_filter(filter);
string logformat = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)";
if (settings.logging.sink.find("file:") != string::npos)
{
string logfile = settings.logging.sink.substr(settings.logging.sink.find(":") + 1);
AixLog::Log::init<AixLog::SinkFile>(logfilter, logfile, logformat);
}
else if (settings.logging.sink == "stdout")
AixLog::Log::init<AixLog::SinkCout>(logfilter, logformat);
else if (settings.logging.sink == "stderr")
AixLog::Log::init<AixLog::SinkCerr>(logfilter, logformat);
else if (settings.logging.sink == "system")
AixLog::Log::init<AixLog::SinkNative>("snapclient", logfilter);
else if (settings.logging.sink == "null")
AixLog::Log::init<AixLog::SinkNull>();
else
{
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::info, "%Y-%m-%d %H-%M-%S [#severity] (#tag_func)");
}
throw SnapException("Invalid log sink: " + settings.logging.sink);
#ifdef HAS_DAEMON
std::unique_ptr<Daemon> daemon;
@ -311,7 +338,7 @@ int main(int argc, char** argv)
}
catch (const std::exception& e)
{
LOG(ERROR) << "Exception: " << e.what() << std::endl;
LOG(FATAL) << "Exception: " << e.what() << std::endl;
exitcode = EXIT_FAILURE;
}

View file

@ -480,8 +480,8 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
lastUpdate_ = now;
median_ = buffer_.median();
shortMedian_ = shortBuffer_.median();
LOG(INFO, LOG_TAG) << "Chunk: " << age.count() / 100 << "\t" << miniBuffer_.median() / 100 << "\t" << shortMedian_ / 100 << "\t" << median_ / 100
<< "\t" << buffer_.size() << "\t" << cs::duration<cs::msec>(outputBufferDacTime) << "\t" << frame_delta_ << "\n";
LOG(DEBUG, "Stats") << "Chunk: " << age.count() / 100 << "\t" << miniBuffer_.median() / 100 << "\t" << shortMedian_ / 100 << "\t" << median_ / 100
<< "\t" << buffer_.size() << "\t" << cs::duration<cs::msec>(outputBufferDacTime) << "\t" << frame_delta_ << "\n";
frame_delta_ = 0;
}
return (abs(cs::duration<cs::msec>(age)) < 500);

View file

@ -3,7 +3,7 @@
/ _\ ( )( \/ )( ) / \ / __)
/ \ )( ) ( / (_/\( O )( (_ \
\_/\_/(__)(_/\_)\____/ \__/ \___/
version 1.3.0
version 1.4.0
https://github.com/badaix/aixlog
This file is part of aixlog
@ -175,6 +175,53 @@ enum class Severity : std::int8_t
};
static Severity to_severity(std::string severity, Severity def = Severity::info)
{
std::transform(severity.begin(), severity.end(), severity.begin(), [](unsigned char c) { return std::tolower(c); });
if (severity == "trace")
return Severity::trace;
else if (severity == "debug")
return Severity::debug;
else if (severity == "info")
return Severity::info;
else if (severity == "notice")
return Severity::notice;
else if (severity == "warning")
return Severity::warning;
else if (severity == "error")
return Severity::error;
else if (severity == "fatal")
return Severity::fatal;
else
return def;
}
static std::string to_string(Severity logSeverity)
{
switch (logSeverity)
{
case Severity::trace:
return "Trace";
case Severity::debug:
return "Debug";
case Severity::info:
return "Info";
case Severity::notice:
return "Notice";
case Severity::warning:
return "Warn";
case Severity::error:
return "Error";
case Severity::fatal:
return "Fatal";
default:
std::stringstream ss;
ss << static_cast<int>(logSeverity);
return ss.str();
}
}
/**
* @brief
* Color constants used for console colors
@ -330,6 +377,10 @@ struct Tag
{
}
Tag(const char* text) : text(text), is_null_(false)
{
}
Tag(const std::string& text) : text(text), is_null_(false)
{
}
@ -345,6 +396,11 @@ struct Tag
return !is_null_;
}
bool operator<(const Tag& other) const
{
return (text < other.text);
}
std::string text;
private:
@ -404,6 +460,59 @@ struct Metadata
Timestamp timestamp;
};
class Filter
{
public:
Filter()
{
}
Filter(Severity severity)
{
add_filter(severity);
}
bool match(const Metadata& metadata) const
{
if (tag_filter_.empty())
return true;
auto iter = tag_filter_.find(metadata.tag);
if (iter != tag_filter_.end())
return (metadata.severity >= iter->second);
iter = tag_filter_.find("*");
if (iter != tag_filter_.end())
return (metadata.severity >= iter->second);
return false;
}
void add_filter(const Tag& tag, Severity severity)
{
tag_filter_[tag] = severity;
}
void add_filter(Severity severity)
{
tag_filter_["*"] = severity;
}
void add_filter(const std::string& filter)
{
auto pos = filter.find(":");
if (pos != std::string::npos)
add_filter(filter.substr(0, pos), to_severity(filter.substr(pos + 1)));
else
add_filter(to_severity(filter));
}
private:
std::map<Tag, Severity> tag_filter_;
};
/**
* @brief
* Abstract log sink
@ -412,7 +521,7 @@ struct Metadata
*/
struct Sink
{
Sink(Severity severity) : severity(severity)
Sink(const Filter& filter) : filter(filter)
{
}
@ -420,7 +529,7 @@ struct Sink
virtual void log(const Metadata& metadata, const std::string& message) = 0;
Severity severity;
Filter filter;
};
/// ostream operators << for the meta data structs
@ -490,31 +599,6 @@ public:
log_sinks_.erase(std::remove(log_sinks_.begin(), log_sinks_.end(), sink), log_sinks_.end());
}
static std::string to_string(Severity logSeverity)
{
switch (logSeverity)
{
case Severity::trace:
return "Trace";
case Severity::debug:
return "Debug";
case Severity::info:
return "Info";
case Severity::notice:
return "Notice";
case Severity::warning:
return "Warn";
case Severity::error:
return "Error";
case Severity::fatal:
return "Fatal";
default:
std::stringstream ss;
ss << logSeverity;
return ss.str();
}
}
protected:
Log() noexcept : last_buffer_(nullptr)
{
@ -536,7 +620,7 @@ protected:
{
for (const auto& sink : log_sinks_)
{
if (metadata_.severity >= sink->severity)
if (sink->filter.match(metadata_))
sink->log(metadata_, get_stream().str());
}
}
@ -591,6 +675,24 @@ private:
std::recursive_mutex mutex_;
};
/**
* @brief
* Null log sink
*
* Discards all log messages
*/
struct SinkNull : public Sink
{
SinkNull() : Sink(Filter())
{
}
void log(const Metadata& metadata, const std::string& message) override
{
}
};
/**
* @brief
* Abstract log sink with support for formatting log message
@ -607,7 +709,7 @@ private:
*/
struct SinkFormat : public Sink
{
SinkFormat(Severity severity, const std::string& format) : Sink(severity), format_(format)
SinkFormat(const Filter& filter, const std::string& format) : Sink(filter), format_(format)
{
}
@ -627,13 +729,13 @@ protected:
size_t pos = result.find("#severity");
if (pos != std::string::npos)
result.replace(pos, 9, Log::to_string(metadata.severity));
result.replace(pos, 9, to_string(metadata.severity));
pos = result.find("#color_severity");
if (pos != std::string::npos)
{
std::stringstream ss;
ss << TextColor(Color::RED) << Log::to_string(metadata.severity) << TextColor(Color::NONE);
ss << TextColor(Color::RED) << to_string(metadata.severity) << TextColor(Color::NONE);
result.replace(pos, 15, ss.str());
}
@ -673,7 +775,7 @@ protected:
*/
struct SinkCout : public SinkFormat
{
SinkCout(Severity severity, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(severity, format)
SinkCout(const Filter& filter, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(filter, format)
{
}
@ -689,7 +791,7 @@ struct SinkCout : public SinkFormat
*/
struct SinkCerr : public SinkFormat
{
SinkCerr(Severity severity, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(severity, format)
SinkCerr(const Filter& filter, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)") : SinkFormat(filter, format)
{
}
@ -705,8 +807,8 @@ struct SinkCerr : public SinkFormat
*/
struct SinkFile : public SinkFormat
{
SinkFile(Severity severity, const std::string& filename, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)")
: SinkFormat(severity, format)
SinkFile(const Filter& filter, const std::string& filename, const std::string& format = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)")
: SinkFormat(filter, format)
{
ofs.open(filename.c_str(), std::ofstream::out | std::ofstream::trunc);
}
@ -734,7 +836,7 @@ protected:
*/
struct SinkOutputDebugString : public Sink
{
SinkOutputDebugString(Severity severity) : Sink(severity)
SinkOutputDebugString(const Filter& filter) : Sink(filter)
{
}
@ -753,7 +855,7 @@ struct SinkOutputDebugString : public Sink
*/
struct SinkUnifiedLogging : public Sink
{
SinkUnifiedLogging(Severity severity) : Sink(severity)
SinkUnifiedLogging(const Filter& filter) : Sink(filter)
{
}
@ -793,7 +895,7 @@ struct SinkUnifiedLogging : public Sink
*/
struct SinkSyslog : public Sink
{
SinkSyslog(const char* ident, Severity severity) : Sink(severity)
SinkSyslog(const char* ident, const Filter& filter) : Sink(filter)
{
openlog(ident, LOG_PID, LOG_USER);
}
@ -842,7 +944,7 @@ struct SinkSyslog : public Sink
*/
struct SinkAndroid : public Sink
{
SinkAndroid(const std::string& ident, Severity severity) : Sink(severity), ident_(ident)
SinkAndroid(const std::string& ident, const Filter& filter) : Sink(filter), ident_(ident)
{
}
@ -899,7 +1001,7 @@ protected:
*/
struct SinkEventLog : public Sink
{
SinkEventLog(const std::string& ident, Severity severity) : Sink(severity)
SinkEventLog(const std::string& ident, const Filter& filter) : Sink(filter)
{
std::wstring wide = std::wstring(ident.begin(), ident.end()); // stijnvdb: RegisterEventSource expands to RegisterEventSourceW which takes wchar_t
event_log = RegisterEventSource(NULL, wide.c_str());
@ -951,16 +1053,16 @@ protected:
*/
struct SinkNative : public Sink
{
SinkNative(const std::string& ident, Severity severity) : Sink(severity), log_sink_(nullptr), ident_(ident)
SinkNative(const std::string& ident, const Filter& filter) : Sink(filter), log_sink_(nullptr), ident_(ident)
{
#ifdef __ANDROID__
log_sink_ = std::make_shared<SinkAndroid>(ident_, severity);
log_sink_ = std::make_shared<SinkAndroid>(ident_, filter);
#elif HAS_APPLE_UNIFIED_LOG_
log_sink_ = std::make_shared<SinkUnifiedLogging>(severity);
log_sink_ = std::make_shared<SinkUnifiedLogging>(filter);
#elif _WIN32
log_sink_ = std::make_shared<SinkEventLog>(ident, severity);
log_sink_ = std::make_shared<SinkEventLog>(ident, filter);
#elif HAS_SYSLOG_
log_sink_ = std::make_shared<SinkSyslog>(ident_.c_str(), severity);
log_sink_ = std::make_shared<SinkSyslog>(ident_.c_str(), filter);
#else
/// will not throw or something. Use "get_logger()" to check for success
log_sink_ = nullptr;
@ -995,7 +1097,7 @@ struct SinkCallback : public Sink
{
using callback_fun = std::function<void(const Metadata& metadata, const std::string& message)>;
SinkCallback(Severity severity, callback_fun callback) : Sink(severity), callback_(callback)
SinkCallback(const Filter& filter, callback_fun callback) : Sink(filter), callback_(callback)
{
}
@ -1033,7 +1135,7 @@ static std::ostream& operator<<(std::ostream& os, const Severity& log_severity)
}
else
{
os << Log::to_string(log_severity);
os << to_string(log_severity);
}
return os;
}

View file

@ -146,10 +146,12 @@ stream = pipe:///tmp/snapfifo?name=default
#
[logging]
# enable debug logging
#debug = false
# log sink [null,system,stdout,stderr,file:<filename>]
# when left empty: if running as daemon "system" else "stdout"
#sink =
# log file name for the debug logs (debug must be enabled)
#debug_logfile =
# log filter <tag>:<level>[,<tag>:<level>]*
# with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]
#filter = *:info
#
###############################################################################

View file

@ -51,16 +51,16 @@ struct ServerSettings
std::vector<std::string> bind_to_address{{"0.0.0.0"}};
};
struct LoggingSettings
struct Logging
{
bool debug{false};
std::string debug_logfile{""};
std::string sink{""};
std::string filter{"*:info"};
};
HttpSettings http;
TcpSettings tcp;
StreamSettings stream;
LoggingSettings logging;
Logging logging;
};
#endif

View file

@ -70,9 +70,11 @@ int main(int argc, char* argv[])
// debug settings
OptionParser conf("");
conf.add<Value<bool>>("", "logging.debug", "enable debug logging", settings.logging.debug, &settings.logging.debug);
conf.add<Value<string>>("", "logging.debug_logfile", "log file name for the debug logs (debug must be enabled)", settings.logging.debug_logfile,
&settings.logging.debug_logfile);
op.add<Value<string>>("", "logging.sink", "log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
auto logfilterOption = op.add<Value<string>>(
"", "logging.filter",
"log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
settings.logging.filter);
// stream settings
conf.add<Value<size_t>>("", "stream.port", "Server port", settings.stream.port, &settings.stream.port);
@ -182,18 +184,42 @@ int main(int argc, char* argv[])
exit(EXIT_SUCCESS);
}
// TODO: AixLog::Log::init<AixLog::SinkNative>("snapserver", AixLog::Severity::trace, AixLog::Type::special);
if (settings.logging.debug)
settings.logging.filter = logfilterOption->value();
if (logfilterOption->is_set())
{
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::trace, "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
if (!settings.logging.debug_logfile.empty())
AixLog::Log::instance().add_logsink<AixLog::SinkFile>(AixLog::Severity::trace, settings.logging.debug_logfile,
"%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
for (size_t n = 1; n < logfilterOption->count(); ++n)
settings.logging.filter += "," + logfilterOption->value(n);
}
if (settings.logging.sink.empty())
{
settings.logging.sink = "stdout";
#ifdef HAS_DAEMON
if (daemonOption->is_set())
settings.logging.sink = "system";
#endif
}
AixLog::Filter logfilter;
auto filters = utils::string::split(settings.logging.filter, ',');
for (const auto& filter : filters)
logfilter.add_filter(filter);
string logformat = "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)";
if (settings.logging.sink.find("file:") != string::npos)
{
string logfile = settings.logging.sink.substr(settings.logging.sink.find(":") + 1);
AixLog::Log::init<AixLog::SinkFile>(logfilter, logfile, logformat);
}
else if (settings.logging.sink == "stdout")
AixLog::Log::init<AixLog::SinkCout>(logfilter, logformat);
else if (settings.logging.sink == "stderr")
AixLog::Log::init<AixLog::SinkCerr>(logfilter, logformat);
else if (settings.logging.sink == "system")
AixLog::Log::init<AixLog::SinkNative>("snapclient", logfilter);
else if (settings.logging.sink == "null")
AixLog::Log::init<AixLog::SinkNull>();
else
{
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::info, "%Y-%m-%d %H-%M-%S [#severity] (#tag_func)");
}
throw SnapException("Invalid log sink: " + settings.logging.sink);
for (const auto& opt : conf.unknown_options())
LOG(WARNING) << "unknown configuration option: " << opt << "\n";