/*
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;
}
}