WARNING: USE THIS SOFTWARE AT YOUR OWN RISK! THIS IS EXPERIMENTAL SOFTWARE NOT INTENDED FOR PRODUCTION USE! Zuble is currently an early stage prototype. As such Zuble is minimally tested and inherently unstable. It is provided for experimental, development, and demonstration purposes only. Zuble QML Types   |  Zuble C++ Classes   |  Zuble Overview
Zuble  0.1
Zuble Framework C++/QML extension API
ZblLogAgent.cpp
Go to the documentation of this file.
1 /*
2  * Zuble - A run-time system for QML/Javascript applications
3  * Copyright (C) 2015 Bob Dinitto
4  *
5  * Filename: ZblLogAgent.cpp
6  * Created on: 11/2/2015
7  * Author: Bob Dinitto
8  *
9  * Zuble is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include "ZblLogAgent.h"
26 #include "ZSettings.h"
27 #include "ZTableModel.h"
28 #include "ZblLogManager.h"
29 #include "ZblLogCategory.h"
30 #include "ZApplication.h"
31 #include "ZblLogMessage.h"
32 #include "ZblApp.h"
33 #include <QDateTime>
34 #include <QTimeZone>
35 #include <QDir>
36 #include <QStringBuilder>
37 #include <QTemporaryFile>
38 #include <QQmlEngine>
39 #include <QJsonArray>
40 #include <QJsonObject>
41 #include <QJsonDocument>
42 #include <QVariant>
43 #include <QUuid>
44 #include <QCoreApplication>
45 #include "zglobal.h"
46 #include <iostream>
47 
48 
49 namespace Zbl {
50 
51 
52 ZblLogAgent::ZblLogAgent(QObject *parent) :
53  QObject(parent),
54  m_logParam(LogFormatJSON,
55  false,
56  false,
57  false,
58  "data://logs/",
59  false,
60  false),
61  m_outFileActive(false),
62  m_nextRecordNumber(0),
63  m_hostVersionMajor(0),
64  m_hostVersionMinor(0),
65  m_hostVersionPatch(0)
66 
67 {
68 
69 }
70 
71 bool ZblLogAgent::zInit(const QString& logSource,
72  ZTableModel* logBuffer)
73 {
74  // WARNING: call this method from the main thread AFTER
75  // moving ZblLogAgent object to background thread.
76 
77  static bool initialized = false;
78 
79  if(initialized)
80  return false;
81  else
82  initialized = true;
83 
84  m_logBuffer = logBuffer;
85  m_logSource = logSource;
86 
87  // TBD: doesn't the following code violate Qt threading policy since qApp is in
88  // the foreground thread and this object is in a background thread?
89 
90  m_appName = qApp->property("org_zuble_appName").toString();
91  m_hostName = qApp->property("org_zuble_host_name").toString();
92  m_hostVersionMajor = qApp->property("org_zuble_host_versionMajor").toInt();
93  m_hostVersionMinor = qApp->property("org_zuble_host_versionMinor").toInt();
94  m_hostVersionPatch = qApp->property("org_zuble_host_versionPatch").toInt();
95  m_hostVersionBulid = qApp->property("org_zuble_host_versionBuild").toString();
96 
97 
98 
99  return initialized;
100 
101 }
102 
104 {
105 
106  //ZScopedSettings settings(ZSettings::getBundleSettings(logProfileBundleID));
107 
108  //QString logFileLocation = settings->value("zuble/log-output-dir",
109  // QVariant("data://logs/")).toString();
110 
111  closeLogFile();
112 
113  QString actualLocation = m_logParam.m_logOutputDir;
114 
115  qDebug() << "LOG FILE LOCATION: " << actualLocation;
116 
117  actualLocation = ZApplication::resolvePath(actualLocation, false);
118 
119  qDebug() << "LOG FILE DESTINATION: " << actualLocation;
120 
121  QDir logDir(actualLocation);
122 
123  if(!logDir.exists())
124  {
125  logDir.mkpath(actualLocation);
126 
127  if(!logDir.exists())
128  return false;
129  }
130 
131  QDateTime now = QDateTime::currentDateTime();
132  QString nowString = now.toString("yyyy:MM:dd:hh:mm:ss:zzz");
133 
134  QString logFilePath(QDir::cleanPath(actualLocation));
135 
136  logFilePath += '/';
137 
138  logFilePath += nowString;
139 
140  switch(m_logParam.m_fileFormat)
141  {
142  case LogFormatText:
143  logFilePath += ".log";
144  break;
145  case LogFormatJSON:
146  logFilePath += ".log.json";
147  break;
148  case LogFormatXML:
149  logFilePath += ".log.not-xml";
150  break;
151  }
152 
153  qDebug() << "LOG FILE PATH: " << logFilePath;
154 
155  m_outputFile.setFileName(logFilePath);
156 
157  if(!m_outputFile.open(QFile::WriteOnly | QFile::Truncate | QIODevice::Text))
158  {
159  qCritical() << "WARNING: Can't open log output file: " << logFilePath;
160  return false;
161  }
162 
164  {
165  m_outputStream.setDevice(&m_outputFile);
166  }
168  {
169  if(!writeFileInformation(m_outputFile, nowString, now.timeZone().id()))
170  {
171  qCritical() << "WARNING: Can't write log file information record: "
172  << logFilePath;
173 
174  closeLogFile();
175 
176  return false;
177  }
178  }
179 
180  m_outFileActive = true;
181 
182  return true;
183 }
184 
186 {
187  if(!m_outFileActive)
188  return;
189 
190  m_outputStream.flush();
191 
192  m_outputFile.close();
193 
194  m_outFileActive = false;
195 }
196 
198  QFile& logFile,
199  QString creationTime,
200  QByteArray creationZone)
201 {
202  QUuid uuid(QUuid::createUuid());
203 
204  QByteArray ba(uuid.toByteArray());
205 
206  qint64 bytesWritten = logFile.write(ba);
207 
208  if(bytesWritten == -1)
209  return false;
210 
211  QVariantMap info;
212 
213  QString hostVersion("%1.%2.%3");
214  hostVersion = hostVersion.arg(m_hostVersionMajor);
215  hostVersion = hostVersion.arg(m_hostVersionMinor);
216  hostVersion = hostVersion.arg(m_hostVersionPatch);
217 
218  //.arg(m_hostVersionMinor).arg(m_hostVersionPatch);
219  //hostVersion = hostVersion.arg(m_hostVersionMajor,m_hostVersionMinor,m_hostVersionPatch);
220 
221  info.insert("app-name", m_appName);
222  info.insert("host-name", m_hostName);
223  info.insert("host-version", hostVersion);
224  info.insert("host-build", m_hostVersionBulid);
225 
226  info.insert("creation-zone", creationZone);
227  info.insert("creation-time", creationTime);
228 
229  QJsonObject jsonInfo(QJsonObject::fromVariantMap(info));
230 
231  QJsonDocument doc(jsonInfo);
232 
233  ba = doc.toJson(QJsonDocument::Compact);
234 
235  ba.append('\n');
236 
237  bytesWritten = logFile.write(ba);
238 
239  return (bytesWritten != -1);
240 }
241 
242 
243 void ZblLogAgent::outputLogMessage(QVariant message)
244 {
245  if(!message.canConvert<Zbl::ZblLogMessage>())
246  return;
247 
248  ZblLogMessage logMsg = message.value<Zbl::ZblLogMessage>();
249 
250  bool bLogToFile = m_logParam.m_enableFileOutput && m_outFileActive;
251 
252  if(bLogToFile)
253  {
254  switch(m_logParam.m_fileFormat)
255  {
256  case LogFormatText:
257  outputTextMessage(logMsg);
258  break;
259 
260  case LogFormatJSON:
261  outputJsonMessage(logMsg);
262  break;
263 
264  case LogFormatXML:
265  // NO XML OUTPUT SUPPORT YET
266  break;
267  }
268 
269  }
270 
272  std::cout << formatTextMessage(logMsg).toUtf8().constData()
273  << std::endl;
274 
276  {
277  ZDataRow row;
278 
279  // begin order dependent fields
280 
281  row.append(QVariant(m_nextRecordNumber));
282  row.append(QVariant(logMsg.m_category));
283  row.append(QVariant(m_logSource));
284  row.append(QVariant(logMsg.m_text));
285  row.append(QVariant(logMsg.m_function));
286  row.append(QVariant::fromValue<int>(logMsg.m_line));
287  row.append(QVariant(logMsg.m_file));
288 
289  // end order dependent fields
290 
291  m_logBuffer->appendCells(row);
292  }
293 
295 
296  if(logMsg.m_flush)
297  closeLogFile();
298 }
299 
300 void ZblLogAgent::setOutputParameters(QVariant logParams)
301 {
302  //LogFileFormat format = LogFileFormat(fileType);
303 
304  if(!logParams.canConvert<Zbl::ZblLogParams>())
305  return;
306 
307  ZblLogParams params = logParams.value<Zbl::ZblLogParams>();
308 
309  if(params == m_logParam)
310  return;
311 
312  bool fileParamsChanged = false;
313 
316  fileParamsChanged = true;
317 
318  m_logParam = params;
319 
321  m_logParam.m_fileFormat = LogFormatText; // we don't support XML yet
322 
324  closeLogFile();
325  else if(fileParamsChanged && m_logParam.m_enableFileOutput)
326  createLogFile();
327 
328  emit outputParametersUpdated(logParams);
329 }
330 
332 {
333  m_outputStream << formatTextMessage(msg) << endl;
334 }
335 
337 {
338  QString msgBuffer;
339 
340  QTextStream str(&msgBuffer);
341 
342  switch (msg.m_msgType) {
343  case QtDebugMsg:
344  //str << "\nDebug#";
345  break;
346  case QtWarningMsg:
347  str << "WARNING: ";
348  break;
349  case QtCriticalMsg:
350  str << "CRITICAL: ";
351  break;
352  case QtFatalMsg:
353  str << "FATAL ERROR: ";
354  break;
355  }
356 
357  str << msg.m_category << ": " << msg.m_text;
358 
360  str << "\n>line: " << msg.m_line
361  << " >file: " << msg.m_file
362  << " >function: " << msg.m_function
363  << " >record: " << m_nextRecordNumber
364  << " >source: " << m_logSource;
365 
366  return msgBuffer;
367 }
368 
370 {
371 
372  QJsonArray logRecord;
373 
374  logRecord.push_back(QJsonValue(static_cast<qint64>(m_nextRecordNumber)));
375  logRecord.push_back(QJsonValue(msg.m_category));
376  logRecord.push_back(QJsonValue(QString(m_logSource)));
377  logRecord.push_back(QJsonValue(msg.m_text));
378  logRecord.push_back(QJsonValue(msg.m_function));
379  logRecord.push_back(QJsonValue(msg.m_line));
380  logRecord.push_back(QJsonValue(msg.m_file));
381 
382  QJsonDocument doc;
383 
384  doc.setArray(logRecord);
385 
386  QByteArray ba(doc.toJson(QJsonDocument::Compact));
387 
388  m_outputFile.write(ba);
389 
390  m_outputFile.write("\n");
391 
392  m_outputFile.flush();
393 
394 }
395 
396 
397 
398 
399 } // Zbl
void setOutputParameters(QVariant logParams)
Sets the output parameters for the log worker object.
Q_INVOKABLE void appendCells(QVariant data, bool truncateModel=false)
Asynchronously converts an array of cell data into one or more rows of model data and appends it to t...
QFile m_outputFile
The log output file object.
Definition: ZblLogAgent.h:136
void outputLogMessage(QVariant message)
Outputs a log message to the log histogram buffer and/or a file and/or stdout.
LogFileFormat m_fileFormat
The current logger output file format.
Definition: ZblLogParams.h:104
QString m_hostName
Name of the host executable application.
Definition: ZblLogAgent.h:173
void outputTextMessage(const ZblLogMessage &msg)
bool writeFileInformation(QFile &logFile, QString creationTime, QByteArray creationZone)
ZTableModel * m_logBuffer
Pointer to the log manager&#39;s log record histogram buffer.
Definition: ZblLogAgent.h:152
bool m_enableModelOutput
Enables log output to log histogram buffer.
Definition: ZblLogParams.h:93
int m_hostVersionPatch
Patch version of the host executable application.
Definition: ZblLogAgent.h:198
void outputParametersUpdated(QVariant logParams)
void outputJsonMessage(const ZblLogMessage &msg)
ZblLogParams m_logParam
Zuble logging output parameters.
Definition: ZblLogAgent.h:127
Definition: ZAndGate.cpp:6
QString m_appName
Application name that will be written to log file.
Definition: ZblLogAgent.h:167
QTextStream m_outputStream
The log output stream object.
Definition: ZblLogAgent.h:144
ZblLogAgent(QObject *parent=nullptr)
Definition: ZblLogAgent.cpp:52
This two dimensional table model is used to store and manipulate data.
Definition: ZTableModel.h:96
QString m_logOutputDir
Directory in which log files are created.
Definition: ZblLogParams.h:110
quint64 m_nextRecordNumber
A running counter to generate log record numbers.
Definition: ZblLogAgent.h:160
bool zInit(const QString &logSource, ZTableModel *logBuffer)
Initializes the ZblLogAgent object. This method should be called after the ZblLogAgent object has bee...
Definition: ZblLogAgent.cpp:71
This class contains the Zuble logging parameters that control log output.
Definition: ZblLogParams.h:38
static QString resolvePath(const QString &path, bool includeUrlScheme=true)
Converts relative file paths into canonical file paths. Paths prefixed with prefix are mapped relativ...
bool m_enableFileOutput
Enables log output to file.
Definition: ZblLogParams.h:81
QString m_logSource
String that will be written to the "source" field of the log file.
Definition: ZblLogAgent.h:183
int m_hostVersionMinor
Minor version of the host executable application.
Definition: ZblLogAgent.h:193
bool m_flush
true = flush output buffers and close streams before returning (used for fatal/abort messages) ...
Definition: ZblLogMessage.h:57
bool m_outFileActive
true if output file object is opened and accepting data, false otherwise.
Definition: ZblLogAgent.h:204
QString m_hostVersionBulid
Build name of the host executable application.
Definition: ZblLogAgent.h:178
bool m_enableLogging
Enables Zuble logging.
Definition: ZblLogParams.h:75
int m_hostVersionMajor
Major version of the host executable application.
Definition: ZblLogAgent.h:188
This class is used to transfer log messages between threads.
Definition: ZblLogMessage.h:38
bool m_enableStdOutput
Enables log output to stdout.
Definition: ZblLogParams.h:87
QList< QVariant > ZDataRow
Represents a single row (or column for column headers) of data cell values for a single role...
Definition: ZTableModel.h:57
QString formatTextMessage(const ZblLogMessage &msg)
bool m_enableDetails
Logs will include detailed debugging information.
Definition: ZblLogParams.h:99