833 lines
23 KiB
C++
833 lines
23 KiB
C++
#include "logmanager.h"
|
|
#include <QCoreApplication>
|
|
#include <QStandardPaths>
|
|
#include <QDir>
|
|
#include <QDateTime>
|
|
#include <QThread>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
#include <QDebug>
|
|
|
|
// ============================================================================
|
|
// СТАТИЧЕСКИЕ ПЕРЕМЕННЫЕ
|
|
// ============================================================================
|
|
|
|
LogManager* LogManager::m_instance = nullptr;
|
|
|
|
// ============================================================================
|
|
// МЕТОДЫ LOGENTRY
|
|
// ============================================================================
|
|
|
|
QString LogEntry::toString() const
|
|
{
|
|
QStringList parts;
|
|
|
|
if (!timestamp.isNull()) {
|
|
parts.append(timestamp.toString("hh:mm:ss"));
|
|
}
|
|
|
|
parts.append(levelToString(level));
|
|
|
|
if (!module.isEmpty()) {
|
|
parts.append(module);
|
|
}
|
|
|
|
if (!method.isEmpty()) {
|
|
parts.append(method);
|
|
}
|
|
|
|
parts.append(message);
|
|
|
|
return parts.join(" | ");
|
|
}
|
|
|
|
QString LogEntry::toHtml() const
|
|
{
|
|
QString color = levelToColor(level).name();
|
|
|
|
QString html = QString(
|
|
"<div style=\"margin: 2px 0; padding: 2px; border-left: 3px solid %1;\">"
|
|
"<span style=\"color: gray; font-size: 0.8em;\">%2</span> "
|
|
"<span style=\"color: %1; font-weight: bold;\">[%3]</span> "
|
|
"<span style=\"color: darkblue;\">%4</span> "
|
|
"<span style=\"color: green;\">%5</span>: "
|
|
"<span>%6</span>"
|
|
"</div>")
|
|
.arg(color)
|
|
.arg(timestamp.toString("hh:mm:ss"))
|
|
.arg(levelToString(level))
|
|
.arg(module)
|
|
.arg(method)
|
|
.arg(message.toHtmlEscaped());
|
|
|
|
return html;
|
|
}
|
|
|
|
QString LogEntry::toCsv() const
|
|
{
|
|
return QString("\"%1\",\"%2\",\"%3\",\"%4\",\"%5\"")
|
|
.arg(timestamp.toString("yyyy-MM-dd hh:mm:ss"))
|
|
.arg(levelToString(level))
|
|
.arg(module)
|
|
.arg(method)
|
|
.arg(message);
|
|
}
|
|
|
|
QString LogEntry::toJson() const
|
|
{
|
|
QJsonObject obj;
|
|
obj["timestamp"] = timestamp.toString(Qt::ISODate);
|
|
obj["level"] = levelToString(level);
|
|
obj["module"] = module;
|
|
obj["method"] = method;
|
|
obj["message"] = message;
|
|
obj["threadId"] = static_cast<qint64>(threadId);
|
|
|
|
if (!sourceFile.isEmpty()) {
|
|
obj["sourceFile"] = sourceFile;
|
|
obj["sourceLine"] = sourceLine;
|
|
}
|
|
|
|
QJsonDocument doc(obj);
|
|
return doc.toJson(QJsonDocument::Compact);
|
|
}
|
|
|
|
QString LogEntry::levelToString(LogLevel level)
|
|
{
|
|
switch (level) {
|
|
case LogLevel::Info: return "INFO";
|
|
case LogLevel::Warning: return "WARNING";
|
|
case LogLevel::Error: return "ERROR";
|
|
case LogLevel::Debug: return "DEBUG";
|
|
case LogLevel::Critical: return "CRITICAL";
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
QColor LogEntry::levelToColor(LogLevel level)
|
|
{
|
|
switch (level) {
|
|
case LogLevel::Info: return Qt::darkGreen;
|
|
case LogLevel::Warning: return Qt::darkYellow;
|
|
case LogLevel::Error: return Qt::red;
|
|
case LogLevel::Debug: return Qt::gray;
|
|
case LogLevel::Critical: return Qt::darkRed;
|
|
default: return Qt::black;
|
|
}
|
|
}
|
|
|
|
QString LogEntry::formatDateTime(const QDateTime& dt)
|
|
{
|
|
return dt.toString("dd.MM.yyyy hh:mm:ss.zzz");
|
|
}
|
|
|
|
// ============================================================================
|
|
// РЕАЛИЗАЦИЯ LOGMANAGER
|
|
// ============================================================================
|
|
|
|
LogManager::LogManager(QObject* parent)
|
|
: QObject(parent)
|
|
, m_maxEntries(10000)
|
|
{
|
|
// Настройки по умолчанию
|
|
m_settings.logToFile = true;
|
|
m_settings.logToConsole = true;
|
|
m_settings.logToDatabase = false;
|
|
|
|
// Стандартный путь к файлу логов
|
|
QString appDataPath = QStandardPaths::writableLocation(
|
|
QStandardPaths::AppDataLocation);
|
|
QDir dir(appDataPath);
|
|
if (!dir.exists()) {
|
|
dir.mkpath(".");
|
|
}
|
|
m_settings.logFilePath = dir.filePath("application.log");
|
|
|
|
// Цвета по умолчанию
|
|
m_settings.colors[LogLevel::Info] = Qt::darkGreen;
|
|
m_settings.colors[LogLevel::Warning] = Qt::darkYellow;
|
|
m_settings.colors[LogLevel::Error] = Qt::red;
|
|
m_settings.colors[LogLevel::Debug] = Qt::gray;
|
|
m_settings.colors[LogLevel::Critical] = Qt::darkRed;
|
|
|
|
m_settings.showTimestamp = true;
|
|
m_settings.showModule = true;
|
|
m_settings.showMethod = true;
|
|
}
|
|
|
|
LogManager::~LogManager()
|
|
{
|
|
if (m_logFile.isOpen()) {
|
|
m_logFile.close();
|
|
}
|
|
}
|
|
|
|
LogManager* LogManager::instance()
|
|
{
|
|
if (!m_instance) {
|
|
m_instance = new LogManager();
|
|
}
|
|
return m_instance;
|
|
}
|
|
|
|
void LogManager::initialize(const LogSettings& settings)
|
|
{
|
|
if (m_instance) {
|
|
m_instance->updateSettings(settings);
|
|
} else {
|
|
m_instance = new LogManager();
|
|
m_instance->updateSettings(settings);
|
|
}
|
|
}
|
|
|
|
void LogManager::cleanup()
|
|
{
|
|
if (m_instance) {
|
|
delete m_instance;
|
|
m_instance = nullptr;
|
|
}
|
|
}
|
|
|
|
void LogManager::log(LogLevel level, const QString& module,
|
|
const QString& method, const QString& message)
|
|
{
|
|
log(level, module, method, message, "", 0);
|
|
}
|
|
|
|
void LogManager::log(LogLevel level, const QString& module,
|
|
const QString& method, const QString& message,
|
|
const QString& sourceFile, int sourceLine)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
// Создаем запись
|
|
LogEntry entry;
|
|
entry.timestamp = QDateTime::currentDateTime();
|
|
entry.level = level;
|
|
entry.module = module;
|
|
entry.method = method;
|
|
entry.message = message;
|
|
entry.threadId = reinterpret_cast<qint64>(QThread::currentThreadId());
|
|
|
|
if (!sourceFile.isEmpty()) {
|
|
entry.sourceFile = sourceFile;
|
|
entry.sourceLine = sourceLine;
|
|
}
|
|
|
|
// Добавляем в список
|
|
m_entries.append(entry);
|
|
|
|
// Ограничиваем количество записей
|
|
if (m_entries.size() > m_maxEntries) {
|
|
m_entries.removeFirst();
|
|
}
|
|
|
|
// Записываем в файл
|
|
if (m_settings.logToFile && !m_settings.logFilePath.isEmpty()) {
|
|
writeToFile(entry);
|
|
}
|
|
|
|
// Выводим в консоль
|
|
if (m_settings.logToConsole) {
|
|
QString formatted = formatForConsole(entry);
|
|
switch (level) {
|
|
case LogLevel::Error:
|
|
case LogLevel::Critical:
|
|
qCritical().noquote() << formatted;
|
|
break;
|
|
case LogLevel::Warning:
|
|
qWarning().noquote() << formatted;
|
|
break;
|
|
case LogLevel::Debug:
|
|
qDebug().noquote() << formatted;
|
|
break;
|
|
default:
|
|
qInfo().noquote() << formatted;
|
|
break;
|
|
}
|
|
}
|
|
|
|
locker.unlock();
|
|
|
|
// Отправляем сигналы
|
|
emit entryAdded(entry);
|
|
|
|
// Сигналы для конкретных уровней
|
|
switch (level) {
|
|
case LogLevel::Info:
|
|
emit infoAdded(entry);
|
|
break;
|
|
case LogLevel::Warning:
|
|
emit warningAdded(entry);
|
|
break;
|
|
case LogLevel::Error:
|
|
emit errorAdded(entry);
|
|
break;
|
|
case LogLevel::Debug:
|
|
emit debugAdded(entry);
|
|
break;
|
|
case LogLevel::Critical:
|
|
emit criticalAdded(entry);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void LogManager::info(const QString& module, const QString& method,
|
|
const QString& message)
|
|
{
|
|
log(LogLevel::Info, module, method, message);
|
|
}
|
|
|
|
void LogManager::warning(const QString& module, const QString& method,
|
|
const QString& message)
|
|
{
|
|
log(LogLevel::Warning, module, method, message);
|
|
}
|
|
|
|
void LogManager::error(const QString& module, const QString& method,
|
|
const QString& message)
|
|
{
|
|
log(LogLevel::Error, module, method, message);
|
|
}
|
|
|
|
void LogManager::debug(const QString& module, const QString& method,
|
|
const QString& message)
|
|
{
|
|
log(LogLevel::Debug, module, method, message);
|
|
}
|
|
|
|
void LogManager::critical(const QString& module, const QString& method,
|
|
const QString& message)
|
|
{
|
|
log(LogLevel::Critical, module, method, message);
|
|
}
|
|
|
|
QList<LogEntry> LogManager::allEntries() const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
return m_entries;
|
|
}
|
|
|
|
QList<LogEntry> LogManager::entriesByTime(const QDateTime& from,
|
|
const QDateTime& to) const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QList<LogEntry> result;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
if (entry.timestamp >= from && entry.timestamp <= to) {
|
|
result.append(entry);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QList<LogEntry> LogManager::entriesByLevel(LogLevel level) const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QList<LogEntry> result;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
if (entry.level == level) {
|
|
result.append(entry);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QList<LogEntry> LogManager::entriesByModule(const QString& module) const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QList<LogEntry> result;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
if (entry.module == module) {
|
|
result.append(entry);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QList<LogEntry> LogManager::search(const QString& text, bool caseSensitive) const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QList<LogEntry> result;
|
|
|
|
Qt::CaseSensitivity sensitivity = caseSensitive ?
|
|
Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
if (entry.message.contains(text, sensitivity) ||
|
|
entry.module.contains(text, sensitivity) ||
|
|
entry.method.contains(text, sensitivity)) {
|
|
result.append(entry);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QList<LogEntry> LogManager::filter(const QList<LogLevel>& levels,
|
|
const QString& moduleFilter,
|
|
const QString& methodFilter) const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QList<LogEntry> result;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
// Проверка уровня
|
|
if (!levels.contains(entry.level)) {
|
|
continue;
|
|
}
|
|
|
|
// Проверка модуля
|
|
if (!moduleFilter.isEmpty() && entry.module != moduleFilter) {
|
|
continue;
|
|
}
|
|
|
|
// Проверка метода
|
|
if (!methodFilter.isEmpty() && entry.method != methodFilter) {
|
|
continue;
|
|
}
|
|
|
|
result.append(entry);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void LogManager::clear()
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_entries.clear();
|
|
locker.unlock();
|
|
|
|
emit logCleared();
|
|
}
|
|
|
|
bool LogManager::saveToFile(const QString& filePath, bool append)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
QFile file(filePath);
|
|
QIODevice::OpenMode mode = QIODevice::WriteOnly | QIODevice::Text;
|
|
if (append) {
|
|
mode |= QIODevice::Append;
|
|
}
|
|
|
|
if (!file.open(mode)) {
|
|
emit fileError(QString("Не удалось открыть файл: %1").arg(file.errorString()));
|
|
return false;
|
|
}
|
|
|
|
QTextStream stream(&file);
|
|
stream.setCodec("UTF-8");
|
|
|
|
// Заголовок
|
|
stream << "Дата,Уровень,Модуль,Метод,Сообщение\n";
|
|
|
|
for (const auto& entry : m_entries) {
|
|
stream << entry.toCsv() << "\n";
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
bool LogManager::loadFromFile(const QString& filePath)
|
|
{
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
return false;
|
|
}
|
|
|
|
QTextStream stream(&file);
|
|
stream.setCodec("UTF-8");
|
|
|
|
// Пропускаем заголовок
|
|
QString header = stream.readLine();
|
|
|
|
QList<LogEntry> loadedEntries;
|
|
|
|
while (!stream.atEnd()) {
|
|
QString line = stream.readLine();
|
|
QStringList parts = line.split('"');
|
|
|
|
if (parts.size() < 9) continue;
|
|
|
|
LogEntry entry;
|
|
entry.timestamp = QDateTime::fromString(parts[1], "yyyy-MM-dd hh:mm:ss");
|
|
|
|
QString levelStr = parts[3];
|
|
if (levelStr == "INFO") entry.level = LogLevel::Info;
|
|
else if (levelStr == "WARNING") entry.level = LogLevel::Warning;
|
|
else if (levelStr == "ERROR") entry.level = LogLevel::Error;
|
|
else if (levelStr == "DEBUG") entry.level = LogLevel::Debug;
|
|
else if (levelStr == "CRITICAL") entry.level = LogLevel::Critical;
|
|
|
|
entry.module = parts[5];
|
|
entry.method = parts[7];
|
|
entry.message = parts[9];
|
|
|
|
loadedEntries.append(entry);
|
|
}
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
m_entries = loadedEntries;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LogManager::exportToFormat(const QString& filePath, const QString& format)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
return false;
|
|
}
|
|
|
|
QTextStream stream(&file);
|
|
stream.setCodec("UTF-8");
|
|
|
|
if (format.toLower() == "html") {
|
|
stream << "<!DOCTYPE html>\n<html>\n<head>\n";
|
|
stream << "<meta charset=\"UTF-8\">\n";
|
|
stream << "<title>Логи приложения</title>\n";
|
|
stream << "<style>\n";
|
|
stream << "body { font-family: monospace; }\n";
|
|
stream << ".info { color: darkgreen; }\n";
|
|
stream << ".warning { color: darkorange; }\n";
|
|
stream << ".error { color: red; }\n";
|
|
stream << ".debug { color: gray; }\n";
|
|
stream << ".critical { color: darkred; }\n";
|
|
stream << "</style>\n";
|
|
stream << "</head>\n<body>\n";
|
|
|
|
for (const auto& entry : m_entries) {
|
|
stream << entry.toHtml() << "\n";
|
|
}
|
|
|
|
stream << "</body>\n</html>";
|
|
|
|
} else if (format.toLower() == "json") {
|
|
QJsonArray array;
|
|
for (const auto& entry : m_entries) {
|
|
QJsonObject obj;
|
|
obj["timestamp"] = entry.timestamp.toString(Qt::ISODate);
|
|
obj["level"] = LogEntry::levelToString(entry.level);
|
|
obj["module"] = entry.module;
|
|
obj["method"] = entry.method;
|
|
obj["message"] = entry.message;
|
|
array.append(obj);
|
|
}
|
|
|
|
QJsonDocument doc(array);
|
|
stream << doc.toJson();
|
|
|
|
} else { // TXT (по умолчанию)
|
|
for (const auto& entry : m_entries) {
|
|
stream << entry.toString() << "\n";
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
return true;
|
|
}
|
|
|
|
QMap<LogLevel, int> LogManager::statistics() const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
QMap<LogLevel, int> stats;
|
|
|
|
for (const auto& entry : m_entries) {
|
|
stats[entry.level]++;
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
|
|
int LogManager::count() const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
return m_entries.size();
|
|
}
|
|
|
|
int LogManager::maxEntries() const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
return m_maxEntries;
|
|
}
|
|
|
|
void LogManager::setMaxEntries(int max)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_maxEntries = max;
|
|
|
|
// Удаляем лишние записи
|
|
while (m_entries.size() > m_maxEntries) {
|
|
m_entries.removeFirst();
|
|
}
|
|
}
|
|
|
|
LogSettings LogManager::settings() const
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
return m_settings;
|
|
}
|
|
|
|
void LogManager::updateSettings(const LogSettings& newSettings)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
// Закрываем старый файл, если изменился путь
|
|
if (m_settings.logFilePath != newSettings.logFilePath && m_logFile.isOpen()) {
|
|
m_logFile.close();
|
|
}
|
|
|
|
m_settings = newSettings;
|
|
|
|
// Инициализируем файл
|
|
if (m_settings.logToFile && !m_settings.logFilePath.isEmpty()) {
|
|
if (!m_logFile.isOpen()) {
|
|
m_logFile.setFileName(m_settings.logFilePath);
|
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
|
} else {
|
|
m_logStream.setDevice(&m_logFile);
|
|
m_logStream.setCodec("UTF-8");
|
|
}
|
|
}
|
|
}
|
|
|
|
locker.unlock();
|
|
emit settingsChanged();
|
|
}
|
|
|
|
void LogManager::saveSettings(QSettings& settings)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
settings.beginGroup("Logging");
|
|
settings.setValue("logToFile", m_settings.logToFile);
|
|
settings.setValue("logToConsole", m_settings.logToConsole);
|
|
settings.setValue("logFilePath", m_settings.logFilePath);
|
|
settings.setValue("maxFileSizeMB", m_settings.maxFileSizeMB);
|
|
settings.setValue("maxFileCount", m_settings.maxFileCount);
|
|
settings.setValue("showTimestamp", m_settings.showTimestamp);
|
|
settings.setValue("showModule", m_settings.showModule);
|
|
settings.setValue("showMethod", m_settings.showMethod);
|
|
settings.setValue("maxEntries", m_maxEntries);
|
|
settings.endGroup();
|
|
}
|
|
|
|
void LogManager::loadSettings(QSettings& settings)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
settings.beginGroup("Logging");
|
|
m_settings.logToFile = settings.value("logToFile", true).toBool();
|
|
m_settings.logToConsole = settings.value("logToConsole", true).toBool();
|
|
m_settings.logFilePath = settings.value("logFilePath", m_settings.logFilePath).toString();
|
|
m_settings.maxFileSizeMB = settings.value("maxFileSizeMB", 10).toInt();
|
|
m_settings.maxFileCount = settings.value("maxFileCount", 5).toInt();
|
|
m_settings.showTimestamp = settings.value("showTimestamp", true).toBool();
|
|
m_settings.showModule = settings.value("showModule", true).toBool();
|
|
m_settings.showMethod = settings.value("showMethod", true).toBool();
|
|
m_maxEntries = settings.value("maxEntries", 10000).toInt();
|
|
settings.endGroup();
|
|
}
|
|
|
|
void LogManager::setLogToFileEnabled(bool enabled)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_settings.logToFile = enabled;
|
|
|
|
if (enabled && !m_logFile.isOpen()) {
|
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
|
}
|
|
} else if (!enabled && m_logFile.isOpen()) {
|
|
m_logFile.close();
|
|
}
|
|
}
|
|
|
|
void LogManager::setLogToConsoleEnabled(bool enabled)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_settings.logToConsole = enabled;
|
|
}
|
|
|
|
void LogManager::setLogFilePath(const QString& path)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
if (m_logFile.isOpen()) {
|
|
m_logFile.close();
|
|
}
|
|
|
|
m_settings.logFilePath = path;
|
|
|
|
if (m_settings.logToFile) {
|
|
m_logFile.setFileName(path);
|
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
|
emit fileError(QString("Не удалось открыть файл логов: %1").arg(m_logFile.errorString()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void LogManager::setLevelColors(const QMap<LogLevel, QColor>& colors)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_settings.colors = colors;
|
|
}
|
|
|
|
void LogManager::setLevelColor(LogLevel level, const QColor& color)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_settings.colors[level] = color;
|
|
}
|
|
|
|
void LogManager::setFormat(bool showTimestamp, bool showModule, bool showMethod)
|
|
{
|
|
QMutexLocker locker(&m_mutex);
|
|
m_settings.showTimestamp = showTimestamp;
|
|
m_settings.showModule = showModule;
|
|
m_settings.showMethod = showMethod;
|
|
}
|
|
|
|
void LogManager::writeToFile(const LogEntry& entry)
|
|
{
|
|
if (!m_logFile.isOpen()) {
|
|
return;
|
|
}
|
|
|
|
QString formatted = formatForFile(entry);
|
|
m_logStream << formatted << "\n";
|
|
m_logStream.flush();
|
|
|
|
// Проверяем размер файла
|
|
if (checkFileSize()) {
|
|
rotateLogFile();
|
|
}
|
|
}
|
|
|
|
void LogManager::rotateLogFile()
|
|
{
|
|
if (!m_logFile.isOpen()) {
|
|
return;
|
|
}
|
|
|
|
m_logFile.close();
|
|
|
|
QString basePath = m_settings.logFilePath;
|
|
QFileInfo fi(basePath);
|
|
QString baseName = fi.baseName();
|
|
QString suffix = fi.suffix();
|
|
QString dir = fi.path();
|
|
|
|
// Удаляем самый старый файл
|
|
QString oldestFile = QString("%1/%2.%3.%4")
|
|
.arg(dir)
|
|
.arg(baseName)
|
|
.arg(m_settings.maxFileCount)
|
|
.arg(suffix);
|
|
|
|
if (QFile::exists(oldestFile)) {
|
|
QFile::remove(oldestFile);
|
|
}
|
|
|
|
// Переименовываем остальные файлы
|
|
for (int i = m_settings.maxFileCount - 1; i >= 1; i--) {
|
|
QString oldFile = QString("%1/%2.%3.%4")
|
|
.arg(dir)
|
|
.arg(baseName)
|
|
.arg(i)
|
|
.arg(suffix);
|
|
QString newFile = QString("%1/%2.%3.%4")
|
|
.arg(dir)
|
|
.arg(baseName)
|
|
.arg(i + 1)
|
|
.arg(suffix);
|
|
|
|
if (QFile::exists(oldFile)) {
|
|
QFile::rename(oldFile, newFile);
|
|
}
|
|
}
|
|
|
|
// Переименовываем текущий файл
|
|
QString firstBackup = QString("%1/%2.%3.%4")
|
|
.arg(dir)
|
|
.arg(baseName)
|
|
.arg(1)
|
|
.arg(suffix);
|
|
|
|
QFile::rename(basePath, firstBackup);
|
|
|
|
// Открываем новый файл
|
|
if (!m_logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
emit fileError(QString("Не удалось открыть файл логов после ротации: %1")
|
|
.arg(m_logFile.errorString()));
|
|
}
|
|
}
|
|
|
|
bool LogManager::checkFileSize()
|
|
{
|
|
if (!m_logFile.isOpen()) {
|
|
return false;
|
|
}
|
|
|
|
qint64 size = m_logFile.size();
|
|
qint64 maxSize = m_settings.maxFileSizeMB * 1024 * 1024;
|
|
|
|
return size > maxSize;
|
|
}
|
|
|
|
QString LogManager::formatEntry(const LogEntry& entry) const
|
|
{
|
|
QStringList parts;
|
|
|
|
if (m_settings.showTimestamp) {
|
|
parts.append(entry.timestamp.toString("hh:mm:ss"));
|
|
}
|
|
|
|
parts.append(LogEntry::levelToString(entry.level));
|
|
|
|
if (m_settings.showModule && !entry.module.isEmpty()) {
|
|
parts.append(entry.module);
|
|
}
|
|
|
|
if (m_settings.showMethod && !entry.method.isEmpty()) {
|
|
parts.append(entry.method);
|
|
}
|
|
|
|
parts.append(entry.message);
|
|
|
|
return parts.join(" | ");
|
|
}
|
|
|
|
QString LogManager::formatForConsole(const LogEntry& entry) const
|
|
{
|
|
QString formatted = formatEntry(entry);
|
|
|
|
// Добавляем цвет для консоли (если поддерживается)
|
|
if (m_settings.colors.contains(entry.level)) {
|
|
// Для консоли цвет добавляется через escape-последовательности
|
|
QColor color = m_settings.colors[entry.level];
|
|
// Это упрощенный вариант, можно расширить для реальной консоли
|
|
return formatted;
|
|
}
|
|
|
|
return formatted;
|
|
}
|
|
|
|
QString LogManager::formatForFile(const LogEntry& entry) const
|
|
{
|
|
// Для файла используем CSV формат
|
|
return entry.toCsv();
|
|
}
|