From 93938508840cebb599cceaab84345dcc3aef9e12 Mon Sep 17 00:00:00 2001 From: Amar Takhar Date: Mon, 31 May 2010 20:55:29 +0000 Subject: [PATCH] Commit the beginings of a logging api, this is fairly complete however it has no locking. Originally committed to SVN as r4371. --- aegisub/libaegisub/Makefile.am | 1 + aegisub/libaegisub/common/log.cpp | 152 +++++++++++++++++ aegisub/libaegisub/include/libaegisub/log.h | 178 ++++++++++++++++++++ aegisub/libaegisub/lagi_pre.h | 9 + 4 files changed, 340 insertions(+) create mode 100644 aegisub/libaegisub/common/log.cpp create mode 100644 aegisub/libaegisub/include/libaegisub/log.h diff --git a/aegisub/libaegisub/Makefile.am b/aegisub/libaegisub/Makefile.am index b788abaf7..7c69f7c97 100644 --- a/aegisub/libaegisub/Makefile.am +++ b/aegisub/libaegisub/Makefile.am @@ -25,6 +25,7 @@ libaegisub_2_2_la_SOURCES = \ common/mru.cpp \ common/option.cpp \ common/option_visit.cpp \ + common/log.cpp \ common/validator.cpp \ unix/util.cpp \ unix/io.cpp \ diff --git a/aegisub/libaegisub/common/log.cpp b/aegisub/libaegisub/common/log.cpp new file mode 100644 index 000000000..9f55a9ae1 --- /dev/null +++ b/aegisub/libaegisub/common/log.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2010, Amar Takhar +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file log.cpp +/// @brief Logging +/// @ingroup libaegisub + + +#include +#include +#include + +#include "libaegisub/log.h" + +namespace agi { + namespace log { + +/// Global log sink. +LogSink *log = new LogSink(); + + +SinkMessage::SinkMessage(const char *section, Severity severity, const char *file, + const char *func, int line, timeval tv): + section(section), + severity(severity), + file(file), + func(func), + line(line), + tv(tv) { +} + +SinkMessage::~SinkMessage() { +///@todo Memory cleanup +} + + +LogSink::LogSink(): emit(0) { + sink = new Sink(); +} + +LogSink::~LogSink() { +/// @todo This needs to flush all log data to disk on quit. + if (emit) { + for (int i = emitters.size()-1; i >= 0; i--) { + delete emitters[i]; + } + } +} + + +void LogSink::log(SinkMessage *sm) { + sink->push_back(sm); + + if (emit) { + for (int i = emitters.size()-1; i >= 0; i--) { + emitters[i]->log(sm); + } + } +} + + +Emitter::~Emitter() { +} + + +Emitter::Emitter() { +} + +void EmitSTDOUT::log(SinkMessage *sm) { + tm tmtime; + localtime_r(&sm->tv.tv_sec, &tmtime); + + printf("%c %d-%02d-%02d %02d:%02d:%02d %ld %s %s %s:%d %s\n", + Severity_ID[sm->severity], + tmtime.tm_year+1900, + tmtime.tm_mon, + tmtime.tm_mday, + tmtime.tm_hour, + tmtime.tm_min, + tmtime.tm_sec, + sm->tv.tv_usec, + sm->section, + sm->file, + sm->func, + sm->line, + strndup(sm->message, + sm->len)); +} + + +int LogSink::Subscribe(Emitter &em) { + emitters.push_back(&em); + emit = 1; + +} + + +void LogSink::Unsubscribe(const int &id) { + emitters.erase((emitters.begin()-1)+id); + if (emitters.size() == 0) + emit = 0; +} + + +Message::Message(const char *section, + Severity severity, + const char *file, + const char *func, + int line): + len(1024) { + buf = new char[len]; + msg = new std::ostrstream(buf, len); + timeval tv; + gettimeofday(&tv, (struct timezone *)NULL); + sm = new SinkMessage(section, severity, file, func, line, tv); +} + + +Message::~Message() { + sm->message = msg->str(); + sm->len = msg->pcount(); + agi::log::log->log(sm); + delete[] buf; + delete msg; +} + + +void Emitter::Enable() { + id = agi::log::log->Subscribe(*(this)); +} + + +void Emitter::Disable() { + agi::log::log->Unsubscribe(id); +} + + + } // namespace log +} // namespace agi diff --git a/aegisub/libaegisub/include/libaegisub/log.h b/aegisub/libaegisub/include/libaegisub/log.h new file mode 100644 index 000000000..480175a5c --- /dev/null +++ b/aegisub/libaegisub/include/libaegisub/log.h @@ -0,0 +1,178 @@ +// Copyright (c) 2010, Amar Takhar +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// +// $Id$ + +/// @file log.h +/// @brief Logging +/// @ingroup libaegisub + +#ifndef LAGI_PRE +#include +#ifdef __DEPRECATED // Dodge GCC warnings +# undef __DEPRECATED +# include +# define __DEPRECATED +#else +# include +#endif +#include +#endif +//#include + +// These macros below aren't a perm solution, it will depend on how annoying they are through +// actual usage, and also depends on msvc support. +#define LOG_SINK(section, severity) agi::log::Message::Message(section, severity, __FILE__, __FUNCTION__, __LINE__).stream() +#define LOG_E(section) LOG_SINK(section, agi::log::Exception) +#define LOG_A(section) LOG_SINK(section, agi::log::Assert) +#define LOG_W(section) LOG_SINK(section, agi::log::Warning) +#define LOG_I(section) LOG_SINK(section, agi::log::Info) +#define LOG_D(section) LOG_SINK(section, agi::log::Debug) + + +namespace agi { + namespace log { + +/// Severity levels +enum Severity { + Exception, ///< Used when exceptions are thrown + Assert, ///< Fatal and non-fatal assert logging + Warning, ///< Warnings + Info, ///< Information only + Debug ///< Enabled by default when compiled in debug mode. +}; + +/// Short Severity ID +/// Keep this ordered the same as Severity +const char* Severity_ID = "EAWID"; + +/// Container to hold a single message +struct SinkMessage { + /// @brief Constructor + /// @param section Section info + /// @param severity Severity + /// @param file File name + /// @param func Function name + /// @param line Source line + /// @param tv Log time + SinkMessage(const char *section, Severity severity, const char *file, + const char *func, int line, timeval tv); + + // Destructor + ~SinkMessage(); + + const char *section; ///< Section info eg "video/open" "video/seek" etc + Severity severity; ///< Severity + const char *file; ///< Source file + const char *func; ///< Function name + int line; ///< Source line + timeval tv; ///< Time at execution + char *message; ///< Formatted message + size_t len; ///< Message length +}; + +class Emitter; + +/// Message sink for holding all messages +typedef std::deque Sink; + +/// Log sink, single destination for all messages +class LogSink { + /// Size of current sink, this is only an estimate that is used for trimming. + int64_t size; + + /// Log sink + Sink *sink; + + /// List of function pointers to emitters + std::vector emitters; + + /// Whether to enable emitters + bool emit; + +public: + /// Constructor + LogSink(); + + /// Destructor + ~LogSink(); + + /// Insert a message into the sink. + void log(SinkMessage *sm); + + /// @brief Subscribe an emitter. + /// @param Function pointer to an emitter + /// @return ID for this Emitter + int Subscribe(Emitter &em); + + /// @brief Unsubscribe an emitter. + /// @param id ID to remove. + void Unsubscribe(const int &id); + + /// @brief @get the complete (current) log. + /// @param[out] out Reference to a sink. + void GetSink(Sink *s); +}; + + +/// An emitter to produce human readable output for a log sink. +class Emitter { + /// ID for this emitter + int id; + +public: + /// Constructor + Emitter(); + + /// Destructor + virtual ~Emitter(); + + /// Enable (subscribe) + void Enable(); + + /// Disable (unsubscribe) + void Disable(); + + /// Accept a single log entry + virtual void log(SinkMessage *sm)=0; +}; + + +/// Generates a message and submits it to the log sink. +class Message { + char *buf; + const int len; + std::ostrstream *msg; + SinkMessage *sm; + +public: + Message(const char *section, + Severity severity, + const char *file, + const char *func, + int line); + ~Message(); + std::ostream& stream() { return *(msg); } +}; + + +/// Emit log entries to stdout. +class EmitSTDOUT: public Emitter { +public: + void log(SinkMessage *sm); +}; + + + } // namespace log +} // namespace agi diff --git a/aegisub/libaegisub/lagi_pre.h b/aegisub/libaegisub/lagi_pre.h index 04ae7c4b6..893e20fca 100644 --- a/aegisub/libaegisub/lagi_pre.h +++ b/aegisub/libaegisub/lagi_pre.h @@ -17,6 +17,7 @@ #endif // Common C++ +#include #include #include #include @@ -24,6 +25,14 @@ #include #include +#ifdef __DEPRECATED // Dodge GCC warnings +# undef __DEPRECATED +# include +# define __DEPRECATED +#else +# include +#endif + // Cajun #include "libaegisub/cajun/elements.h" #include "libaegisub/cajun/reader.h"