/* PIP - Platform Independent Primitives High-level log Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "pilog.h" #include "pidir.h" #include "piliterals_string.h" #include "piliterals_time.h" #include "pitime.h" //! \class PILog pilog.h //! \details //! \~english \section PILog_sec0 Synopsis //! \~russian \section PILog_sec0 Краткий обзор //! \~english //! This class provide handy parsing of command-line arguments. First you should add //! arguments to %PICLI with function \a addArgument(). Then you can check if there //! is some argument in application command-line with function \a hasArgument(), //! or obtain argument value by \a argumentValue(). //! //! \~russian //! Этот класс предоставляет удобный механизм для разбора аргументов командной строки. //! Сперва необходимо добавить аргументы в %PICLI с помощью методов \a addArgument(). //! Далее можно проверять аргументы на наличие в командной строке методом \a hasArgument(), //! а также получать их значения при помощи \a argumentValue(). //! //! \~english \section PICLI_sec1 Example //! \~russian \section PICLI_sec1 Пример //! \~\code //! int main(int argc, char ** argv) { //! PICLI cli(argc, argv); //! cli.addArgument("console"); //! cli.addArgument("debug"); //! cli.addArgument("Value", "v", "value", true); //! if (cli.hasArgument("console")) //! piCout << "console active"; //! if (cli.hasArgument("debug")) //! piCout << "debug active"; //! piCout << "Value =" << cli.argumentValue("Value"); //! return 0; //! } //! \endcode //! //! \~english These executions are similar: //! \~russian Эти вызовы будут идентичны: //! //! \~\code //! a.out -cd -v 10 //! a.out --value 10 -dc //! a.out -c -v 10 -d //! a.out --console -d -v 10 //! a.out --debug -c --value 10 //! \endcode //! PILog::PILog(): PIThread(), log_ts(&log_file) { 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() { stop(); } void PILog::setDir(const PIString & d) { stopAndWait(); log_dir = d; PIDir::make(log_dir); newFile(); start(); } 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 t = PIDateTime::fromSystemTime(PISystemTime::current()); PIMutexLocker ml(log_mutex); 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() { 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); } void PILog::run() { if (split_tm.elapsed() >= split_time) { split_tm.reset(); newFile(); } log_mutex.lock(); if (queue.isEmpty()) { log_mutex.unlock(); piMSleep(20); return; } log_mutex.unlock(); while (true) { log_mutex.lock(); if (queue.isEmpty()) { log_mutex.unlock(); return; } auto qi = queue.dequeue(); log_mutex.unlock(); auto str = entryToString(qi); log_ts << str << "\n"; piCout << str; } }