From 7a945f47b12cea0c7e856f1c4dae643cab25527a Mon Sep 17 00:00:00 2001 From: peri4 Date: Fri, 13 Sep 2024 23:05:24 +0300 Subject: [PATCH] PILog works --- libs/main/application/pilog.cpp | 96 ++++++++++++++++++++++++++++++--- libs/main/application/pilog.h | 66 ++++++++++++++++++++--- main.cpp | 20 +++++++ 3 files changed, 168 insertions(+), 14 deletions(-) diff --git a/libs/main/application/pilog.cpp b/libs/main/application/pilog.cpp index 7a47a462..e5c2a4ff 100644 --- a/libs/main/application/pilog.cpp +++ b/libs/main/application/pilog.cpp @@ -20,6 +20,7 @@ #include "pilog.h" #include "pidir.h" +#include "piliterals_string.h" #include "piliterals_time.h" #include "pitime.h" @@ -71,12 +72,16 @@ PILog::PILog(): PIThread(), log_ts(&log_file) { - split_time = 8_h; + setName("PILog"); + split_time = 8_h; + timestamp_format = "yyyy-MM-dd hh:mm:ss.zzz"; + setLineFormat("t - c: m"); + CONNECTU(PICout::Notifier::object(), finished, this, coutDone); } PILog::~PILog() { - stopAndWait(); + stop(); } @@ -89,16 +94,91 @@ void PILog::setDir(const PIString & d) { } -void PILog::enqueue(const PIString & msg) { +void PILog::setLineFormat(const PIString & f) { + line_format = f; + line_format_p = line_format; + line_format_p.replace("t", "${t}").replace("c", "${c}").replace("m", "${m}"); +} + + +PICout PILog::debug(PIObject * context) { + return makePICout(context, Category::Debug); +} + + +PICout PILog::warning(PIObject * context) { + return makePICout(context, Category::Warning); +} + + +PICout PILog::error(PIObject * context) { + return makePICout(context, Category::Error); +} + + +void PILog::stop() { + while (true) { + log_mutex.lock(); + bool done = queue.isEmpty(); + log_mutex.unlock(); + if (done) break; + piMinSleep(); + } + PIThread::stopAndWait(); +} + + +void PILog::coutDone(int id, PIString * buffer) { + cout_mutex.lock(); + if (!cout_cat_by_id.contains(id)) { + cout_mutex.unlock(); + return; + } + auto cat = cout_cat_by_id.take(id, PILog::Category::Debug); + cout_mutex.unlock(); + if (!buffer) return; + enqueue(*buffer, cat); + delete buffer; +} + + +PICout PILog::makePICout(PIObject * context, Category cat) { + cout_mutex.lock(); + int id = ++cout_id; + auto buffer = new PIString(); + cout_cat_by_id[id] = cat; + cout_mutex.unlock(); + if (context) { + *buffer = "["_a + context->className(); + if (context->name().isNotEmpty()) *buffer += " \"" + context->name() + "\""; + *buffer += "] "; + } + return PICout::withExternalBuffer(buffer, id, PICoutManipulators::AddSpaces); +} + + +void PILog::enqueue(const PIString & msg, Category cat) { if (log_file.isClosed()) return; - auto cur_dt = PIDateTime::fromSystemTime(PISystemTime::current()); + auto t = PIDateTime::fromSystemTime(PISystemTime::current()); PIMutexLocker ml(log_mutex); - queue.enqueue(cur_dt.toString("hh:mm:ss.zzz") + " - " + msg); + queue.enqueue({cat, t, msg}); +} + + +PIString PILog::entryToString(const Entry & e) const { + static PIStringList categories{"debug", "warn ", "error"}; + PIString t = e.time.toString(timestamp_format); + PIString ret = line_format_p; + ret.replace("${t}", t).replace("${c}", categories[static_cast(e.cat)]).replace("${m}", e.msg); + return ret; } void PILog::newFile() { - log_file.open(log_dir + "/" + PIDateTime::current().toString("yyyy_MM_dd__hh_mm_ss__p" + PIString::fromNumber(++part_number) + ".txt"), + PIString aname = app_name; + if (aname.isNotEmpty()) aname += "__"; + log_file.open(log_dir + "/" + aname + PIDateTime::current().toString("yyyy_MM_dd__hh_mm_ss") + ".log." + + PIString::fromNumber(++part_number), PIIODevice::ReadWrite); } @@ -123,6 +203,8 @@ void PILog::run() { } auto qi = queue.dequeue(); log_mutex.unlock(); - log_ts << qi << "\n"; + auto str = entryToString(qi); + log_ts << str << "\n"; + piCout << str; } } diff --git a/libs/main/application/pilog.h b/libs/main/application/pilog.h index 14445ef3..968aba00 100644 --- a/libs/main/application/pilog.h +++ b/libs/main/application/pilog.h @@ -41,26 +41,78 @@ public: PILog(); ~PILog(); + //! \~english Returns prefix for filename. + PIString applicationName() const { return app_name; } + + //! \~english Set prefix for filename. Should be set \b before \a setDir()! + void setApplicationName(const PIString & n) { app_name = n; } + + + //! \~english Returns directory for log files. PIString dir() const { return log_dir; } + + //! \~english Set directory for log files. Should be set \b after \a setApplicationName()! void setDir(const PIString & d); - PISystemTime splitTime() const { return split_time; } - void setSplitTime(PISystemTime st) { split_time = st; } - void enqueue(const PIString & msg); + //! \~english Returns lifetime for file. + PISystemTime fileSplitTime() const { return split_time; } + + //! \~english Set lifetime for file. Each "st" interval new file will be created. + void setFileSplitTime(PISystemTime st) { split_time = st; } + + + //! \~english Returns timestamp format for line. + PIString timestampFormat() const { return timestamp_format; } + + //! \~english Set timestamp format for line. Default is "yyyy-MM-dd hh:mm:ss.zzz". + void setTimestampFormat(const PIString & f) { timestamp_format = f; } + + + //! \~english Returns line format. + PIString lineFormat() const { return line_format; } + + //! \~english Set line format. "t" is timestamp, "c" is category and "m" is message. Default is "t - c: m". + void setLineFormat(const PIString & f); + + PICout debug(PIObject * context = nullptr); + PICout warning(PIObject * context = nullptr); + PICout error(PIObject * context = nullptr); + + //! \~english Write all queued lines and stop. Also called in destructor. + void stop(); private: + enum class Category { + Debug, + Warning, + Error + }; + + EVENT_HANDLER2(void, coutDone, int, id, PIString *, buff); + + PICout makePICout(PIObject * context, Category cat); + void enqueue(const PIString & msg, Category cat = Category::Debug); + + struct Entry { + Category cat; + PIDateTime time; + PIString msg; + }; + + PIString entryToString(const Entry & e) const; void newFile(); void run() override; - PIMutex log_mutex; + PIMutex log_mutex, cout_mutex; PIFile log_file; PIIOTextStream log_ts; PITimeMeasurer split_tm; PISystemTime split_time; - PIString log_dir; - PIQueue queue; - int part_number = -1; + PIString log_dir, timestamp_format, line_format, line_format_p, app_name; + PIQueue queue; + PIMap cout_cat_by_id; + int part_number = -1, cout_id = -1; }; #endif diff --git a/main.cpp b/main.cpp index a9c31875..de5a652e 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,7 @@ #include "piintrospection_server.h" #include "piiostream.h" #include "pijson.h" +#include "pilog.h" #include "pimathbase.h" #include "pip.h" #include "pivaluetree_conversions.h" @@ -67,6 +68,25 @@ protected: int main(int argc, char * argv[]) { + PILog log; + log.setApplicationName("test"); + log.setDir("logs"); + // log.setTimestampFormat("hh-mm-ss"); + // log.setLineFormat("[c] m (t)"); + log.start(); + + // log.enqueue("debug msg"); + // log.enqueue("warn msg with ${c}", PILog::Category::Warning); + // log.enqueue("ERROR${m}${t}", PILog::Category::Error); + + log.debug(&log) << "some msg"; + piMSleep(50); + log.warning() << "another!"; + piMSleep(50); + log.error() << "blahblahblag"; + + // log.stop(); + return 0; /*PIPeer p("123"); piCout << "start ...";