1 /*
2 * Copyright (C) 2019-2021 Ashar Khan <ashar786khan@gmail.com>
3 *
4 * This file is part of CP Editor.
5 *
6 * CP Editor is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * I will not be responsible if CP Editor behaves in unexpected way and
12 * causes your ratings to go down and or lose any important contest.
13 *
14 * Believe Software is "Software" and it isn't immune to bugs.
15 *
16 */
17
18 #include "Core/EventLogger.hpp"
19 #include "Util/FileUtil.hpp"
20 #include "generated/portable.hpp"
21 #include "generated/version.hpp"
22 #include <QDateTime>
23 #include <QDesktopServices>
24 #include <QDir>
25 #include <QFile>
26 #include <QLibraryInfo>
27 #include <QProcess>
28 #include <QStandardPaths>
29 #include <QSysInfo>
30 #include <QUrl>
31
32 namespace Core
33 {
34
35 QFile Log::logFile;
36 QTextStream Log::logStream;
37
38 const int Log::NUMBER_OF_LOGS_TO_KEEP = 50;
39 const int Log::MAXIMUM_FUNCTION_NAME_SIZE = 30;
40 const int Log::MAXIMUM_FILE_NAME_SIZE = 30;
41 const QString Log::LOG_DIR_NAME = "cpeditorLogFiles";
42 const QString Log::LOG_FILE_NAME = "cpeditor";
43
init(int instance,bool dumptoStderr)44 void Log::init(int instance, bool dumptoStderr)
45 {
46 logStream.setDevice(&logFile);
47 if (!dumptoStderr)
48 {
49 // get the path to the log file
50 auto path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
51 LOG_ERR_IF(path.isEmpty(), "Failed to get writable temp location");
52
53 QDir dir(path);
54 dir.mkdir(LOG_DIR_NAME);
55 if (dir.cd(LOG_DIR_NAME))
56 {
57 // keep NUMBER_OF_LOGS_TO_KEEP log files
58 auto entries = dir.entryList({LOG_FILE_NAME + "*.log"}, QDir::Files, QDir::Time);
59 for (int i = NUMBER_OF_LOGS_TO_KEEP; i < entries.length(); ++i)
60 dir.remove(entries[i]);
61
62 // open the log file
63 logFile.setFileName(dir.filePath(QString("%1-%2-%3.log")
64 .arg(LOG_FILE_NAME)
65 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"))
66 .arg(instance)));
67 logFile.open(QIODevice::WriteOnly | QFile::Text);
68 LOG_ERR_IF(!logFile.isOpen() || !logFile.isWritable(), "Failed to open file" << logFile.fileName());
69 }
70 else
71 {
72 LOG_ERR("Failed to open directory" << dir.filePath(LOG_DIR_NAME));
73 }
74 }
75 else
76 {
77 logFile.open(stderr, QIODevice::WriteOnly);
78 }
79 LOG_INFO("Event logger has been initialized successfully");
80 platformInformation();
81 }
82
dateTimeStamp()83 QString Log::dateTimeStamp()
84 {
85 return "[" + QDateTime::currentDateTime().toString(Qt::ISODateWithMs) + "]";
86 }
87
platformInformation()88 void Log::platformInformation()
89 {
90 LOG_INFO("Gathering system information");
91 // Check https://doc.qt.io/qt-5/qsysinfo.html to know what each identifier means
92 LOG_INFO(INFO_OF(QSysInfo::buildAbi()));
93 LOG_INFO(INFO_OF(QSysInfo::buildCpuArchitecture()));
94 LOG_INFO(INFO_OF(QSysInfo::currentCpuArchitecture()));
95 LOG_INFO(INFO_OF(QSysInfo::kernelType()));
96 LOG_INFO(INFO_OF(QSysInfo::kernelVersion()));
97 LOG_INFO(INFO_OF(QSysInfo::prettyProductName()));
98 LOG_INFO(INFO_OF(QSysInfo::productType()));
99 LOG_INFO(INFO_OF(QSysInfo::productVersion()));
100 LOG_INFO(INFO_OF(QLibraryInfo::version().toString()));
101
102 LOG_INFO(INFO_OF(APP_VERSION));
103 LOG_INFO(INFO_OF(DISPLAY_VERSION));
104 LOG_INFO(INFO_OF(GIT_COMMIT_HASH));
105 #ifdef PORTABLE_VERSION
106 LOG_INFO("Portable Version");
107 #else
108 LOG_INFO("Setup Version");
109 #endif
110 #ifdef QT_DEBUG
111 LOG_INFO("Debug Build");
112 #else
113 LOG_INFO("Release Build");
114 #endif
115 LOG_INFO(INFO_OF(__DATE__));
116 LOG_INFO(INFO_OF(__TIME__));
117 }
118
log(const QString & priority,QString funcName,int line,QString fileName)119 QTextStream &Log::log(const QString &priority, QString funcName, int line, QString fileName)
120 {
121 if (!logFile.isOpen() || !logFile.isWritable())
122 logFile.open(stderr, QIODevice::WriteOnly); // dump to stderr if failed to open log file
123 if (funcName.size() > MAXIMUM_FUNCTION_NAME_SIZE)
124 funcName = funcName.right(MAXIMUM_FUNCTION_NAME_SIZE);
125
126 QFileInfo info(fileName);
127 fileName = info.fileName();
128
129 if (fileName.size() > MAXIMUM_FILE_NAME_SIZE)
130 fileName = fileName.right(MAXIMUM_FILE_NAME_SIZE);
131
132 return logStream << dateTimeStamp() << Qt::center << "[" << priority << "]["
133 << qSetFieldWidth(MAXIMUM_FUNCTION_NAME_SIZE) << funcName << qSetFieldWidth(0) << "]["
134 << qSetFieldWidth(MAXIMUM_FILE_NAME_SIZE) << fileName << qSetFieldWidth(0) << Qt::left << "]"
135 << "(" << line << ")::";
136 }
137
revealInFileManager()138 void Log::revealInFileManager()
139 {
140 Util::revealInFileManager(logFile.fileName()).first();
141 }
142
clearOldLogs()143 void Log::clearOldLogs()
144 {
145 auto path = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
146 QDir dir(path);
147 if (dir.cd(LOG_DIR_NAME))
148 {
149 auto entries = dir.entryList({LOG_FILE_NAME + "*.log"}, QDir::Files);
150 for (auto const &e : entries)
151 {
152 if (e != logFile.fileName()) // clear all except the current
153 {
154 dir.remove(e);
155 LOG_INFO("Deleted log file:" << e);
156 }
157 }
158 }
159 }
160
161 } // namespace Core
162