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
ZblLogWorker.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: ZblLogWorker.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 "ZblLogWorker.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 ZblLogWorker::ZblLogWorker(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 ZblLogWorker::zInit(const QString& logSource,
72  ZTableModel* logBuffer)
73 {
74  // WARNING: call this method from the main thread AFTER
75  // moving ZblLogWorker 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  return initialized;
98 
99 }
100 
102 {
103 
104  //ZScopedSettings settings(ZSettings::getBundleSettings(logProfileBundleID));
105 
106  //QString logFileLocation = settings->value("zuble/log-output-dir",
107  // QVariant("data://logs/")).toString();
108 
109  closeLogFile();
110 
111  QString actualLocation = m_logParam.m_logOutputDir;
112 
113  qDebug() << "LOG FILE LOCATION: " << actualLocation;
114 
115  actualLocation = ZApplication::resolvePath(actualLocation, false);
116 
117  qDebug() << "LOG FILE DESTINATION: " << actualLocation;
118 
119  QDir logDir(actualLocation);
120 
121  if(!logDir.exists())
122  {
123  logDir.mkpath(actualLocation);
124 
125  if(!logDir.exists())
126  return false;
127  }
128 
129  QDateTime now = QDateTime::currentDateTime();
130  QString nowString = now.toString("yyyy:MM:dd:hh:mm:ss:zzz");
131 
132  QString logFilePath(QDir::cleanPath(actualLocation));
133 
134  logFilePath += '/';
135 
136  logFilePath += nowString;
137 
138  switch(m_logParam.m_fileFormat)
139  {
140  case LogFormatText:
141  logFilePath += ".log";
142  break;
143  case LogFormatJSON:
144  logFilePath += ".log.json";
145  break;
146  case LogFormatXML:
147  logFilePath += ".log.not-xml";
148  break;
149  }
150 
151  qDebug() << "LOG FILE PATH: " << logFilePath;
152 
153  m_outputFile.setFileName(logFilePath);
154 
155  if(!m_outputFile.open(QFile::WriteOnly | QFile::Truncate | QIODevice::Text))
156  {
157  qCritical() << "WARNING: Can't open log output file: " << logFilePath;
158  return false;
159  }
160 
162  {
163  m_outputStream.setDevice(&m_outputFile);
164  }
166  {
167  if(!writeFileInformation(m_outputFile, nowString, now.timeZone().id()))
168  {
169  qCritical() << "WARNING: Can't write log file information record: "
170  << logFilePath;
171 
172  closeLogFile();
173 
174  return false;
175  }
176  }
177 
178  m_outFileActive = true;
179 
180  return true;
181 }
182 
184 {
185  if(!m_outFileActive)
186  return;
187 
188  m_outputStream.flush();
189 
190  m_outputFile.close();
191 
192  m_outFileActive = false;
193 }
194 
196  QFile& logFile,
197  QString creationTime,
198  QByteArray creationZone)
199 {
200  QUuid uuid(QUuid::createUuid());
201 
202  QByteArray ba(uuid.toByteArray());
203 
204  qint64 bytesWritten = logFile.write(ba);
205 
206  if(bytesWritten == -1)
207  return false;
208 
209  QVariantMap info;
210 
211  QString hostVersion("%1.%2.%3");
212  hostVersion = hostVersion.arg(m_hostVersionMajor);
213  hostVersion = hostVersion.arg(m_hostVersionMinor);
214  hostVersion = hostVersion.arg(m_hostVersionPatch);
215 
216  //.arg(m_hostVersionMinor).arg(m_hostVersionPatch);
217  //hostVersion = hostVersion.arg(m_hostVersionMajor,m_hostVersionMinor,m_hostVersionPatch);
218 
219  info.insert("app-name", m_appName);
220  info.insert("host-name", m_hostName);
221  info.insert("host-version", hostVersion);
222  info.insert("host-build", m_hostVersionBulid);
223 
224  info.insert("creation-zone", creationZone);
225  info.insert("creation-time", creationTime);
226 
227  QJsonObject jsonInfo(QJsonObject::fromVariantMap(info));
228 
229  QJsonDocument doc(jsonInfo);
230 
231  ba = doc.toJson(QJsonDocument::Compact);
232 
233  ba.append('\n');
234 
235  bytesWritten = logFile.write(ba);
236 
237  return (bytesWritten != -1);
238 }
239 
240 
241 void ZblLogWorker::outputLogMessage(QVariant message)
242 {
243  if(!message.canConvert<Zbl::ZblLogMessage>())
244  return;
245 
246  ZblLogMessage logMsg = message.value<Zbl::ZblLogMessage>();
247 
248  bool bLogToFile = m_logParam.m_enableFileOutput && m_outFileActive;
249 
250  if(bLogToFile)
251  {
252  switch(m_logParam.m_fileFormat)
253  {
254  case LogFormatText:
255  outputTextMessage(logMsg);
256  break;
257 
258  case LogFormatJSON:
259  outputJsonMessage(logMsg);
260  break;
261 
262  case LogFormatXML:
263  // NO XML OUTPUT SUPPORT YET
264  break;
265  }
266 
267  }
268 
270  std::cout << formatTextMessage(logMsg).toUtf8().constData()
271  << std::endl;
272 
274  {
275  ZDataRow row;
276 
277  // begin order dependent fields
278 
279  row.append(QVariant(m_nextRecordNumber));
280  row.append(QVariant(logMsg.m_category));
281  row.append(QVariant(m_logSource));
282  row.append(QVariant(logMsg.m_text));
283  row.append(QVariant(logMsg.m_function));
284  row.append(QVariant::fromValue<int>(logMsg.m_line));
285  row.append(QVariant(logMsg.m_file));
286 
287  // end order dependent fields
288 
289  m_logBuffer->appendCells(row);
290  }
291 
293 
294  if(logMsg.m_flush)
295  closeLogFile();
296 }
297 
298 void ZblLogWorker::setOutputParameters(QVariant logParams)
299 {
300  //LogFileFormat format = LogFileFormat(fileType);
301 
302  if(!logParams.canConvert<Zbl::ZblLogParams>())
303  return;
304 
305  ZblLogParams params = logParams.value<Zbl::ZblLogParams>();
306 
307  if(params == m_logParam)
308  return;
309 
310  bool fileParamsChanged = false;
311 
314  fileParamsChanged = true;
315 
316  m_logParam = params;
317 
319  m_logParam.m_fileFormat = LogFormatText; // we don't support XML yet
320 
322  closeLogFile();
323  else if(fileParamsChanged && m_logParam.m_enableFileOutput)
324  createLogFile();
325 
326  emit outputParametersUpdated(logParams);
327 }
328 
330 {
331  m_outputStream << formatTextMessage(msg) << endl;
332 }
333 
335 {
336  QString msgBuffer;
337 
338  QTextStream str(&msgBuffer);
339 
340  switch (msg.m_msgType) {
341  case QtDebugMsg:
342  //str << "\nDebug#";
343  break;
344  case QtWarningMsg:
345  str << "WARNING: ";
346  break;
347  case QtCriticalMsg:
348  str << "CRITICAL: ";
349  break;
350  case QtFatalMsg:
351  str << "FATAL ERROR: ";
352  break;
353  }
354 
355  str << msg.m_category << ": " << msg.m_text;
356 
358  str << "\n>line: " << msg.m_line
359  << " >file: " << msg.m_file
360  << " >function: " << msg.m_function
361  << " >record: " << m_nextRecordNumber
362  << " >source: " << m_logSource;
363 
364  return msgBuffer;
365 }
366 
368 {
369 
370  QJsonArray logRecord;
371 
372  logRecord.push_back(QJsonValue(static_cast<qint64>(m_nextRecordNumber)));
373  logRecord.push_back(QJsonValue(msg.m_category));
374  logRecord.push_back(QJsonValue(QString(m_logSource)));
375  logRecord.push_back(QJsonValue(msg.m_text));
376  logRecord.push_back(QJsonValue(msg.m_function));
377  logRecord.push_back(QJsonValue(msg.m_line));
378  logRecord.push_back(QJsonValue(msg.m_file));
379 
380  QJsonDocument doc;
381 
382  doc.setArray(logRecord);
383 
384  QByteArray ba(doc.toJson(QJsonDocument::Compact));
385 
386  m_outputFile.write(ba);
387 
388  m_outputFile.write("\n");
389 
390  m_outputFile.flush();
391 
392 }
393 
394 
395 
396 
397 } // Zbl
QString m_logSource
String that will be written to the "source" field of the log file.
Definition: ZblLogWorker.h:196
QString m_hostVersionBulid
Build name of the host executable application.
Definition: ZblLogWorker.h:191
quint64 m_nextRecordNumber
A running counter to generate log record numbers.
Definition: ZblLogWorker.h:159
QString m_hostName
Name of the host executable application.
Definition: ZblLogWorker.h:171
ZTableModel * m_logBuffer
Pointer to the log manager&#39;s log record histogram buffer.
Definition: ZblLogWorker.h:151
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...
int m_hostVersionMinor
Minor version of the host executable application.
Definition: ZblLogWorker.h:181
QString formatTextMessage(const ZblLogMessage &msg)
int m_hostVersionMajor
Major version of the host executable application.
Definition: ZblLogWorker.h:176
void outputJsonMessage(const ZblLogMessage &msg)
LogFileFormat m_fileFormat
The current logger output file format.
Definition: ZblLogParams.h:104
void outputParametersUpdated(QVariant logParams)
ZblLogParams m_logParam
Zuble logging output parameters.
Definition: ZblLogWorker.h:127
bool writeFileInformation(QFile &logFile, QString creationTime, QByteArray creationZone)
QString m_appName
Application name that will be written to log file.
Definition: ZblLogWorker.h:165
bool m_enableModelOutput
Enables log output to log histogram buffer.
Definition: ZblLogParams.h:93
Definition: ZAndGate.cpp:6
QTextStream m_outputStream
The log output stream object.
Definition: ZblLogWorker.h:145
void outputLogMessage(QVariant message)
Outputs a log message to the log histogram buffer and/or a file and/or stdout.
bool m_outFileActive
true if output file object is opened and accepting data, false otherwise.
Definition: ZblLogWorker.h:133
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
void outputTextMessage(const ZblLogMessage &msg)
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...
ZblLogWorker(QObject *parent=0)
bool zInit(const QString &logSource, ZTableModel *logBuffer)
Initializes the ZblLogWorker object. This method should be called after the ZblLogWorker object has b...
bool m_enableFileOutput
Enables log output to file.
Definition: ZblLogParams.h:81
QFile m_outputFile
The log output file object.
Definition: ZblLogWorker.h:139
bool m_flush
true = flush output buffers and close streams before returning (used for fatal/abort messages) ...
Definition: ZblLogMessage.h:57
int m_hostVersionPatch
Patch version of the host executable application.
Definition: ZblLogWorker.h:186
bool m_enableLogging
Enables Zuble logging.
Definition: ZblLogParams.h:75
void setOutputParameters(QVariant logParams)
Sets the output parameters for the log worker object.
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
bool m_enableDetails
Logs will include detailed debugging information.
Definition: ZblLogParams.h:99