refactor
This commit is contained in:
+832
@@ -0,0 +1,832 @@
|
||||
#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();
|
||||
}
|
||||
Reference in New Issue
Block a user