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
ZblLogManager.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: ZblLogManager.cpp
6  * Created on: 4/19/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 "ZblLogManager.h"
26 #include "ZApplication.h"
27 #include "ZblException.h"
28 #include "ZTableModel.h"
29 #include "ZSettings.h"
30 #include "zglobal.h"
31 #include "ZblApp.h"
32 #include "ZblLogWorker.h"
33 #include "ZblLogMessage.h"
34 #include <QMutexLocker>
35 #include <QFile>
36 #include <QJsonDocument>
37 #include <QJsonObject>
38 #include <QJsonArray>
39 #include <QCoreApplication>
40 #include <QDateTime>
41 #include <QDir>
42 //#include <QStringBuilder>
43 #include <QTemporaryFile>
44 #include <QStringBuilder>
45 #include <QVariant>
46 
47 
48 namespace Zbl
49 {
50 
51 ZblLogManager* ZblLogManager::m_zLog = nullptr;
52 
53 
55 
56 const QString ZblLogManager::m_zubleSettingsBundleId = "default";
57 const QString ZblLogManager::m_currentLogSettingsBundleId = "current-log-profile";
59 
61 
62 // WARNING: m_logBufRoleCount must match number of log buffer roles
64 
65 const QString ZblLogManager::m_logFilterRulesKey = "log-filter-rules";
66 
68 
69 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
70 "<appsettings\n"
71 "xmlns=\"http://zuble.org/schema/zuble/settings\"\n"
72 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
73 "\n"
74 "xsi:schemaLocation=\"http://zuble.org/schema/zuble/settings zublesettings01.xsd\">\n"
75 " <organization>zuble.org</organization> \n"
76 " <name>Zuble</name> \n"
77 " <setgroups> \n"
78 " <setgroup xml:id=\"log-histogram\"> \n"
79 " <name>Log Histogram</name> \n"
80 " </setgroup> \n"
81 " <setgroup xml:id=\"log-output\"> \n"
82 " <name>Log Output</name> \n"
83 " </setgroup> \n"
84 " <setgroup xml:id=\"log-input\"> \n"
85 " <name>Log Input</name> \n"
86 " </setgroup> \n"
87 " </setgroups> \n"
88 " <settings> \n"
89 " <setting xml:id=\"histogram-enabled\" group=\"log-histogram\"> \n"
90 " <name>Enabled</name> \n"
91 " <type>bool</type> \n"
92 " <default>false</default> \n"
93 " <status> \n"
94 " <state value=\"false\">Disabled</state> \n"
95 " <state value=\"true\">Enabled</state> \n"
96 " </status> \n"
97 " <desc>Enable the log histogram buffer.</desc> \n"
98 " </setting> \n"
99 " <setting xml:id=\"histogram-buffer-size\" group=\"log-histogram\"> \n"
100 " <name>Buffer Size</name> \n"
101 " <type>string</type> \n"
102 " <default>1000</default> \n"
103 " <desc>Set the number of log records the histogram will hold.</desc> \n"
104 " </setting> \n"
105 " <setting xml:id=\"log-file-enabled\" group=\"log-output\"> \n"
106 " <name>Log To File</name> \n"
107 " <type>bool</type> \n"
108 " <default>false</default> \n"
109 " <status> \n"
110 " <state value=\"false\">Disabled</state> \n"
111 " <state value=\"true\">Enabled</state> \n"
112 " </status> \n"
113 " <desc>Send log output to a file.</desc> \n"
114 " </setting> \n"
115 " <setting xml:id=\"log-stdout-enabled\" group=\"log-output\"> \n"
116 " <name>Log To stdout</name> \n"
117 " <type>bool</type> \n"
118 " <default>true</default> \n"
119 " <status> \n"
120 " <state value=\"false\">Disabled</state> \n"
121 " <state value=\"true\">Enabled</state> \n"
122 " </status> \n"
123 " <desc>Send log output to stdout.</desc> \n"
124 " </setting> \n"
125 " <setting xml:id=\"log-file-format\" group=\"log-output\"> \n"
126 " <name>Log File Format</name> \n"
127 " <type>choice</type> \n"
128 " <default>text</default> \n"
129 " <choiceItems> \n"
130 " <choiceItem> \n"
131 " <name>Plain Text</name> \n"
132 " <desc>Output simple utf8 text strings.</desc> \n"
133 " <value>text</value> \n"
134 " </choiceItem> \n"
135 " <choiceItem> \n"
136 " <name>XML</name> \n"
137 " <desc>Output XML-encoded log records.</desc> \n"
138 " <value>xml</value> \n"
139 " </choiceItem> \n"
140 " <choiceItem> \n"
141 " <name>JSON</name> \n"
142 " <desc>Output JSON-encoded log records.</desc> \n"
143 " <value>json</value> \n"
144 " </choiceItem> \n"
145 " </choiceItems> \n"
146 " <desc>Select the log record output format.</desc> \n"
147 " </setting> \n"
148 " <setting xml:id=\"show-record-details\" group=\"log-input\"> \n"
149 " <name>Show Details</name> \n"
150 " <type>bool</type> \n"
151 " <default>false</default> \n"
152 " <status> \n"
153 " <state value=\"false\">Disabled</state> \n"
154 " <state value=\"true\">Enabled</state> \n"
155 " </status> \n"
156 " <desc>Include file name, line number and function with each log record.</desc> \n"
157 " </setting> \n"
158 " <setting xml:id=\"timestamps-enabled\" group=\"log-input\"> \n"
159 " <name>Enable Timestamps</name> \n"
160 " <type>bool</type> \n"
161 " <default>false</default> \n"
162 " <status> \n"
163 " <state value=\"false\">Disabled</state> \n"
164 " <state value=\"true\">Enabled</state> \n"
165 " </status> \n"
166 " <desc>Include a timestamp with each log record.</desc> \n"
167 " </setting> \n"
168 " <setting xml:id=\"thread-id-enabled\" group=\"log-input\"> \n"
169 " <name>Enable Thread Identifiers</name> \n"
170 " <type>bool</type> \n"
171 " <default>false</default> \n"
172 " <status> \n"
173 " <state value=\"false\">Disabled</state> \n"
174 " <state value=\"true\">Enabled</state> \n"
175 " </status> \n"
176 " <desc>Include a thread identifier with each log record.</desc> \n"
177 " </setting> \n"
178 " </settings> \n"
179 " </appsettings> \n";
180 
181 
183  QObject(parent),
184  m_logParam(LogFormatJSON,
185  false,
186  false,
187  false,
188  "data://logs/",
189  false,
190  false),
191  m_logBuffer(nullptr),
192  m_logWorker(nullptr),
193  m_logRules(nullptr),
194  m_logSettings(nullptr),
195  m_profileSettings(nullptr),
196  m_syncProfiles(true),
197  m_loadingProfile(false),
198  m_LogSource("Zuble"),
199  m_originalMsgHandler(nullptr)
200 {
201 
202 }
203 
205 {
206  m_logSettings->deleteLater();
207 
208  stopLogThread();
209 }
210 
211 
213 {
214  if(m_zLog)
215  throw ZblException(
216  "Zbl::ZblLogManager::zInstance programming error: "
217  "Singleton ZblLogManager object already initialized!");
218 
219  zDebug() << "ZblLogManager initialization thread = " << ZApplication::getCurrentThreadAddress();
220 
221  // create the one and only log manager
222 
224 
225  m_zLog = new ZblLogManager(parent);
226 
227  // set up logger thread
228 
230 
231  // create the background worker object
232 
234 
235  qDebug() << "Log worker object initial thread: "
237 
238  QVariant vModules = qApp->property("org_zuble_logModules");
239 
241 
242  ZblLogOutput* module;
243 
244  foreach(module,modules)
245  {
246  if(qobject_cast<ZblLogOutput*>(module))
247  qDebug() << "WE GOT SOMETHING!!!!";
248 
249  if(qobject_cast<ZblLogTextFormatterIF*>(module))
250  qDebug() << "WE GOT A WINNER!!!!!";
251 
252  module->setParent(m_zLog->m_logWorker);
253  }
254 
255  // move the worker object to the background thread
256 
257  m_zLog->m_logWorker->moveToThread(&m_zLog->m_logThread);
258 
259  qDebug() << "Log worker object background thread: "
261 
264  Qt::BlockingQueuedConnection);
265 
266  // set up the log histogram buffer
267 
269 
272 
274 
276 
278 
280 
281  // enable "qml" category TBD: expose as options to QML user
282 
283  QLoggingCategory::setFilterRules(QStringLiteral("qml.debug=true"));
284  QLoggingCategory::setFilterRules(QStringLiteral("qml.info=true"));
285  QLoggingCategory::setFilterRules(QStringLiteral("qml.warning=true"));
286  QLoggingCategory::setFilterRules(QStringLiteral("qml.critical=true"));
287 
288  return *m_zLog;
289 }
290 
292 {
293  static bool alreadyCalled = false;
294 
295  if(alreadyCalled)
296  {
297  zDebug() << "Programming error - setApplicationObject must only be called once.";
298  return;
299  }
300 
301  alreadyCalled = true;
302 
304  {
307  }
308 
309  connect(appObject, &ZApplication::settingsBundleAvailable,
311 }
312 
313 
315 {
316  zDebug() << "SETTINGS BUNDLE AVAILABLE: " << bundleId;
317 
318  if( bundleId == m_zubleSettingsBundleId)
319  {
321  //updateLogProfileSettings(); // Why call this when loadCurrentLogProfile() calls it too?
323  }
324  else if(bundleId == m_currentLogSettingsBundleId)
325  {
326  zDebug() << "Current log settings bundle available for id: "
328  //updateLogProfileSettings();
330  }
331 
332 }
333 
335 {
336  zDebug() << "Updating application settings bundle";
337 
338  if(m_logSettings)
339  {
341 
342  m_logSettings->deleteLater();
343  }
344 
346 
348 
350 }
351 
353 {
354  zDebug() << "Updating log profile settings bundle";
355 
357  {
359  m_profileSettings->deleteLater();
360  }
361 
363 
365 
367 
369 }
370 
371 
373 {
374  // TBD: it's gonna get a lot hotter in here real soon
375 
376  // loop creating the metadata from the modules
377 
378  QVariant vModules = qApp->property("org_zuble_logModules");
379 
381 
382  ZblLogOutput* module;
383 
384  foreach(module,modules)
385  {
386  if(qobject_cast<ZblLogOutput*>(module))
387  qDebug() << "CREATING SETTINGS METADATA FOR LOG OUTPUT MODULE!!!!";
388 
389  if(qobject_cast<ZblLogTextFormatterIF*>(module))
390  qDebug() << "OUTPUT FORMATTER!!!!!";
391  else
392  qDebug() << "OUTPUT TARGET!!!!!";
393 
394  }
395 
396 
397  return m_profileMetadata;
398 }
399 
401 {
402  if(m_loadingProfile)
403  return false;
404 
405  m_loadingProfile = true;
406 
407  QString logProfilePath = ZApplication::resolvePath(
408  m_logSettings->value("zuble/log-profile-path").toString(),
409  false);
410 
411  QString actualPath = logProfilePath;
412 
413  if(logProfilePath.isEmpty())
414  {
415  qWarning() << "Zuble log manager can't locate applicaiton setting: "
416  "zuble/log-profile-path";
417  }
418  else
419  {
420  m_syncProfiles = m_logSettings->value("zuble/sync-log-profile",
421  QVariant(true)).toBool();
422 
423  if(!m_syncProfiles)
424  {
425  actualPath =
426  createTemporaryLogProfile("~current-log-settings-XXXXXX", true);
427 
428  QFile logProfile(logProfilePath);
429 
430  if(logProfile.exists())
431  {
432  if(!logProfile.copy(actualPath))
433  {
434  qWarning() << "Zuble log manager can't decouple log profile";
435  actualPath = logProfilePath;
436  }
437  }
438  }
439 
442  actualPath, true);
443 
445 
446  QVariant filterRules = m_profileSettings->value(m_logFilterRulesKey);
447 
448  if(filterRules.isNull())
449  {
450  saveFilterRules();
451  }
452  else
453  {
455  }
456  }
457 
458  m_loadingProfile = false;
459 
460  return true;
461 }
462 
464  const QString& nameTemplate,
465  bool remove)
466 {
467  QString logProfileDir(ZApplication::resolvePath("data://log-profiles/", false));
468  QString logProfilePath;
469 
470  QDir qDir;
471 
472  if(!qDir.mkpath(logProfileDir))
473  {
474  qCritical() << "Zuble can't create log profile directory: " << logProfileDir;
475 
476  logProfilePath = QDir::tempPath();
477  logProfilePath += '/';
478  }
479  else
480  {
481  logProfilePath = logProfileDir;
482  }
483 
484  logProfilePath += nameTemplate;
485 
486 
487  qDebug() << "log profile path = " << logProfilePath;
488 
489  QTemporaryFile temp(logProfilePath);
490 
491  if(!temp.open())
492  {
493  qWarning() << "Zuble can't open temporary log profile.";
494  }
495  else
496  {
497  logProfilePath = temp.fileName();
498  qDebug() << "temporary log profile = " << logProfilePath;
499 
500  temp.setAutoRemove(remove);
501  temp.close();
502  }
503 
504  return logProfilePath;
505 }
506 
508 {
509  bool logging = false;
510 
511  // check command line argument
512 
513  QStringList args(QCoreApplication::arguments());
514  const int argCount = args.length();
515 
516  for(int i=0; i<argCount; i++)
517  {
518  if(args.at(i) == "-zl")
519  {
520  logging = true;
521  break;
522  }
523  }
524  return logging;
525 }
526 
527 
528 #if 0
529 bool ZblLogManager::updateMessageHandlerStatus()
530 {
531  bool captureLogMessages = false;
532 
533  // check command line argument
534 
535  QStringList args(QCoreApplication::arguments());
536  const int argCount = args.length();
537 
538  for(int i=0; i<argCount; i++)
539  {
540  if(args.at(i) == "-zl")
541  {
542  captureLogMessages = true;
543  break;
544  }
545  }
546 
547  // if command line doesn't enable check the application log settings
548 
549  if(!captureLogMessages)
550  {
552 
553  captureLogMessages =
554  settings->value("zuble/enable-zuble-logging", captureLogMessages).toBool();
555  }
556 
557  // set a timer for flushing the histogram buffer
558 
559  m_zLog->m_timerID = m_zLog->startTimer(200);
560 
561  // install the message handler if zuble logging is enabled
562 
563  if(captureLogMessages)
564  qInstallMessageHandler(ZblLogManager::zMessageHandler);
565 
566  return captureLogMessages;
567 }
568 #endif
569 
571 {
572  if(!m_zLog)
573  throw ZblException(
574  "Zbl::ZblLogManager::zInstance programming error: "
575  "Singleton ZblLogManager object not initialized! "
576  "Call ZblLogManager::zInit before calling this method.");
577 
578  return *m_zLog;
579 }
580 
581 void ZblLogManager::zMessageHandler(QtMsgType msgType,
582  const QMessageLogContext& logCtx,
583  const QString& text)
584 {
585  bool fatal = msgType == QtFatalMsg ? true : false;
586 
587  //static int recursionCounter = 0;
588 
589  //Qt::ConnectionType con;
590 
591 #if 1
592  QThread* workerThread = &m_zLog->m_logThread;
593 
594  QThread* thisThread = QThread::currentThread();
595 
596  if(workerThread != thisThread)
597 #else
599 #endif
600  {
601  ZblLogMessage msg;
602  msg.m_msgType = msgType;
603  msg.m_text = text;
604  msg.m_line = logCtx.line;
605  msg.m_file = logCtx.file;
606  msg.m_function = logCtx.function;
607  msg.m_category = logCtx.category;
608  msg.m_flush = fatal;
609 
610  QVariant zMsg = QVariant::fromValue<Zbl::ZblLogMessage>(msg);
611 
612  // block if fatal so message will be output before aborting program (with luck)
613 
614  Qt::ConnectionType conType = fatal ? Qt::BlockingQueuedConnection : Qt::QueuedConnection;
615 
616  QMetaObject::invokeMethod(m_zLog->m_logWorker,"outputLogMessage",
617  conType,
618  Q_ARG(QVariant, zMsg));
619  }
620 
621 
622  //std::cout << "hello there\n";
623  //std::cout << text.toUtf8().data() << std::endl;
624 
625  if(fatal)
626  {
627  //std::cout << "FATAL ERROR: " << text.toUtf8().data() << std::endl;
628 
630  abort();
631  }
632 
633 }
634 
635 void ZblLogManager::timerEvent(QTimerEvent * event)
636 {
637  killTimer(m_timerID);
638 
640 
641  m_timerID = startTimer(2000);
642 }
643 
644 
646 {
647  return m_logBuffer;
648 }
649 
651 {
652  return m_logBufRoleCount;
653 }
654 
655 
657 {
658  return m_logRules;
659 }
660 
661 
662 #if 0
663 void ZblLogManager::appendModelColumn(
664  int columnNumber,
665  const QByteArray& columnName,
666  ZTableModel& model,
667  ZRoleNames& roles)
668 {
669  roles.insert(columnNumber, columnName);
670  model->addRole(columnNumber);
671 }
672 #endif
673 
674 bool ZblLogManager::registerLogCategory(const QString logCategory)
675 {
676  bool retval = false;
677 
678  QString actualName(logCategory);
679 
681  {
682  if(logCategory.indexOf('.') != -1)
683  {
684  //actualName.replace('.', QLatin1String("$"));
685  actualName.replace('.', QLatin1Char('$'));
686 
687  zWarning() << "Logging category name: " << logCategory
688  << "can't contain the period character (dot \".\"), "
689  "converting name to: " << actualName;
690 
691  }
692  zDebug() << "Registering log category: " << actualName;
693 
694  ZblLogCategory category(actualName);
695  category.registerCategory();
696  }
697 
698  ZBL_SLOT_END_RETURN(retval, retval,
700 }
701 
702 
703 
705 {
706  QObject* p = parent ? parent : m_zLog;
707 
708  ZTableModel* logBuffer = new ZTableModel(p);
709 
710  if(!logBuffer)
711  return NULL;
712 
713  //QQmlEngine::setObjectOwnership(m_logRules, QQmlEngine::CppOwnership);
714 
715  logBuffer->setColumnCount(1);
716 
717  //WARNING: number of roles added here must match m_logBufRoleCount!
718 
719  logBuffer->addRole(LogFieldRecordNum, "RecordNumber");
720  logBuffer->addRole(LogFieldCategory, "Category");
721  logBuffer->addRole(LogFieldSource, "Source");
722  logBuffer->addRole(LogFieldMessage, "Message");
723  logBuffer->addRole(LogFieldFunction, "Function");
724  logBuffer->addRole(LogFieldLine, "Line");
725  logBuffer->addRole(LogFieldFile, "File");
726 
727  if(logBuffer->getRoleCount() != m_logBufRoleCount)
728  {
729  zCritical() << "Zuble programing error: log buffer role count mismatch!";
730  }
731 
732  return logBuffer;
733 
734 }
735 
736 
737 
739 {
740  // this thread runs the message pump for the logger while
741  // the Zuble applicaiton is initializing in the main thread.
742 
743  // if(!m_logRules)
744  // throw ZblException("Logging rules data model not initialized! "
745  // "Call createRuleModel() before calling this method.");
746 
747  // TBD: why are we making this connection?
748 
749  //connect(&m_logThread, SIGNAL(finished()), m_logRules,
750  // SLOT(deleteLater()));
751 
752  //connect(this, &Controller::operate, worker, &Worker::doWork);
753  //connect(worker, &Worker::resultReady, this, &Controller::handleResults);
754 
755  if(m_logThread.isRunning())
756  return false;
757 
758  m_logThread.start();
759 
760 
761  return true;
762 }
763 
765 {
766  m_logThread.quit();
767  m_logThread.wait();
768 }
769 
770 
771 
772 
773 void ZblLogManager::onLogProfileModified(const QString& path)
774 {
775  // Zuble's basic log settings have been modified
776  zDebug() << "LOGGER GOT CURRENT LOG PROFILE MODIFIED SIGNAL!";
777 
779 }
780 
782 {
783  ZblLogParams logParams;
784 
785  // from Zuble settings
786 
787  logParams.m_enableLogging = m_logSettings->
788  value("zuble/enable-zuble-logging").toBool();
789  logParams.m_logOutputDir = m_logSettings->
790  value("zuble/log-output-dir").toString();
791 
792  // from log profile
793 
795  value("log-output/log-file-enabled").toBool();
797  value("log-output/log-stdout-enabled").toBool();
799  value("log-histogram/histogram-enabled").toBool();
800  logParams.m_enableDetails = m_profileSettings->
801  value("log-input/show-record-details").toBool();
803  value("log-output/log-file-format").toString().toLower());
804 
805 
806 
807 
808  // TBD: we are not saving/restoring previous message handler should we?
809 
811  if(!logParams.m_enableLogging)
812  qInstallMessageHandler(nullptr);
813 
814  QVariant vParams(QVariant::fromValue<ZblLogParams>(logParams));
815 
816  QMetaObject::invokeMethod(m_logWorker,"setOutputParameters",
817  Qt::QueuedConnection,
818  Q_ARG(QVariant, vParams));
819 }
820 
822 {
823  // TBD: this is woefully inadequate! we need to be able to turn logging
824  // both on AND off and to avoid re-loading message handler inadvertently.
825  // I just hate when this happens!
826 
827  if(!logParams.canConvert<Zbl::ZblLogParams>())
828  return;
829 
830  ZblLogParams params = logParams.value<Zbl::ZblLogParams>();
831 
832  if(params == m_logParam)
833  return;
834 
836  {
837  if(params.m_enableLogging)
838  {
839  m_logParam = params;
840  qInstallMessageHandler(ZblLogManager::zMessageHandler);
841  return;
842  }
843  }
844  m_logParam = params;
845 }
846 
847 
848 void ZblLogManager::onZubleSettingsModified(const QString& path)
849 {
850  // Zuble's basic log settings have been modified
851  zDebug() << "LOGGER GOT ZUBLE SETTINGS MODIFIED SIGNAL!";
852 
853  if(m_logSettings->getFileName() == path)
854  {
855 
856 #if 1
857  bool sync = m_logSettings->value("zuble/sync-log-profile",
858  QVariant(false)).toBool();
859 
860  if(m_syncProfiles != sync)
862 #endif
863 
865  }
866 
867 }
868 
869 
870 #if 0
871 QString ZblLogManager::getLogRuleBooleanString(int dataRole, int rowNumber)
872 {
873  return toLiterallyTrue(m_logRules->getValue(dataRole, rowNumber).toBool());
874 }
875 #endif
876 
878 {
879  // TBD: metarules are log filter rules loaded from the zblconfig.xml file.
880 
881  zCritical() << "Sorry, populateMetaRules method not yet implemented.";
882 
883  return false;
884 }
885 
886 
887 
888 bool ZblLogManager::loadFilterRules(const QString& logProfileBundleID)
889 {
890  ZScopedSettings settings(ZSettings::getBundleSettings(logProfileBundleID));
891 
892  if(!settings)
893  {
894  qWarning() << "Log Manager can't load log profile: " << logProfileBundleID;
895  return false;
896  }
897 
898  QVariantMap vFilterRules = settings->value(m_logFilterRulesKey).toMap();
899 
900  if(vFilterRules.isEmpty())
901  {
902  qWarning() << "Log Manager can't load current filter rules.";
903  return false;
904  }
905 
906  try
907  {
908  QJsonObject filterRules = QJsonObject::fromVariantMap(vFilterRules);
909 
910  if(filterRules.isEmpty())
911  {
912  qWarning() << "Log Manager can't locate previous filter rules.";
913  return false;
914  }
915 
916  m_logRules->putJsonData(filterRules);
917 
919  }
920  catch(ZblException ex)
921  {
922  qWarning() << "Error loading current filter fules: " << ex.message();
923  return false;
924  }
925 
926  return true;
927 }
928 
930 {
931 
932  // both the category and the logging level must be enabled to turn logging on
933 
934  const QString category(m_logRules->getValue(RuleCategory, rowNumber).toString());
935  const bool isCategoryEnabled = m_logRules->getValue(RuleEnabled, rowNumber).toBool();
936 
937  QString rules
938  = getQtLogRule(category, "warning",
939  isCategoryEnabled && m_logRules->getValue(RuleWarning, rowNumber).toBool())
940  % getQtLogRule(category, "critical",
941  isCategoryEnabled && m_logRules->getValue(RuleCritical, rowNumber).toBool())
942  % getQtLogRule(category, "debug",
943  isCategoryEnabled && m_logRules->getValue(RuleDebug, rowNumber).toBool());
944  return rules;
945 }
946 
947 QString ZblLogManager::getQtLogRule(const QString& category, const QString& level, bool enabled)
948 {
949  return QString(category + '.' + level + "="
950  + toLiterallyTrue(enabled) + '\n');
951 }
952 
954 {
955  const int categoryCount = m_logRules->rowCount();
956 
957  QString rules;
958 
959  for(int i = 0; i < categoryCount; i++)
960  {
961  rules = rules % getQtLogRulesForRow(i);
962  }
963 
964  QLoggingCategory::setFilterRules(rules);
965 }
966 
968 {
969  if(m_logRules)
970  {
971  qWarning() << "logging rule model already exists";
972  return false;
973  }
974 
975  ZTableModel* ruleModel = new ZTableModel(this);
976 
977  QQmlEngine::setObjectOwnership(ruleModel, QQmlEngine::CppOwnership);
978 
979  ruleModel->setColumnCount(1);
980 
981  ruleModel->addRole(RuleCategory,"category");
982  ruleModel->addRole(RuleRegistered,"registered");
983  ruleModel->addRole(RuleSaved,"saved");
984  ruleModel->addRole(RuleEnabled,"active");
985  ruleModel->addRole(RuleWarning,"warning");
986  ruleModel->addRole(RuleCritical,"critical");
987  ruleModel->addRole(RuleDebug,"debug");
988 
989  const int roleCount = 7;
990 
991 
992  //ZDataRow categoryRow;
993  m_modelCategoryRow.append(QVariant(""));
994 
995  //ZDataRow booleanRow;
996  m_modelBooleanRow.append(QVariant(false));
997 
998  QList<int> roles = ruleModel->roles();
999 
1000  for(int i=0; i<roleCount; i++)
1001  {
1002  const int nextRole = roles.at(i);
1003 
1004  if(nextRole != RuleCategory)
1005  m_modelRoleRow.insert(nextRole, m_modelBooleanRow);
1006  }
1007 
1009 
1010  //QMetaObject::Connection conExec =
1012  Qt::QueuedConnection);
1013 
1014  m_logRules = ruleModel;
1015 
1016  return true;
1017 }
1018 
1019 bool ZblLogManager::appendRuleModelCategory(const QString& logCategory)
1020 {
1021  if(!m_logRules)
1022  return false;
1023 
1024  //LOCK_ZLOGCATEGORIES
1025 
1026  //const int categoryIndex = m_logRules->findNextItemRow(0,logCategory,false);
1027 
1028 // if(categoryIndex != -1)
1029 // return false;
1030 
1031  ZDataRow categoryRow = m_modelCategoryRow;
1032 
1033  ZRoleRow roleRow = m_modelRoleRow;
1034 
1035  categoryRow.replace(0, QVariant(logCategory));
1036 
1037  roleRow.insert(RuleCategory, categoryRow);
1038 
1039  m_logRules->appendMissingRow(roleRow, logCategory);
1040 
1041  return true;
1042 }
1043 
1044 void ZblLogManager::onRuleModelModified(int dataRole, int rowNumber)
1045 {
1046  // get category name, message type, enabled/disabled
1047  // concatenate rule string
1048  // call QLogCategory::setFilterRules()
1049 
1050 
1051  //QString rules = getQtLogRulesForRow(rowNumber);
1052  //QLoggingCategory::setFilterRules(rules);
1053 
1055 
1056  saveFilterRules();
1057 }
1058 
1060 {
1061  m_logRules->sync();
1062 
1063  QJsonObject ruleData = m_logRules->getJsonData();
1064 
1065  //ZSettings* settings = ZSettings::getBundleSettings(m_currentLogSettingsBundleId);
1066 
1068 
1071 
1072  QVariant rulesmap = ruleData.toVariantMap();
1073 
1075 
1076  settings->setValue(m_logFilterRulesKey, QVariant(ruleData.toVariantMap()));
1077 
1078  settings->sync();
1079 }
1080 
1082 {
1084 
1085  const int categoryCount = zLogCategories.count();
1086 
1087  for(int j=0; j<categoryCount; j++)
1088  {
1089  appendRuleModelCategory(zLogCategories.at(j));
1090  }
1091 
1092  saveFilterRules();
1093 
1094  qDebug() << "Begin dumping logging rule model.";
1095 
1096  if(m_logRules)
1098 
1099  qDebug() << "end dumping logging rule model.";
1100 
1101 }
1102 
1104 {
1105  LogFileFormat format = LogFormatText;
1106 
1107  if(logFileFormat == "json")
1108  {
1109  format = LogFormatJSON;
1110  }
1111  else if(logFileFormat == "xml")
1112  {
1113  zWarning() << "Sorry, XML log format not yet implemented. "
1114  "Using text format instead.";
1115  }
1116  else if(logFileFormat != "text")
1117  {
1118  zWarning() << "Invalid log format: " << logFileFormat <<
1119  "Should be one of: \"text\", \"json\". Using text format.";
1120  }
1121 
1122  return format;
1123 }
1124 
1125 } // Zbl
void onSettingsBundleAvailable(QString bundleId)
Responds to newly loaded settings bundles.
The Zuble Log Manager is a singleton object that controls logging in Zuble applications.
Definition: ZblLogManager.h:70
static const QString m_profileMetadata
Log profile metadata defines logger settings that are stored in a log profile.
void tableModified(int dataRole, int rowNumber)
Sent when a table cell has been modified.
QString getFileName() const
Definition: ZSettings.cpp:142
Q_INVOKABLE void appendMissingRow(ZRoleRow roleRow, const QString &keyText, int keyColumn=0)
Appends the specified row if no rows contain the specified text in the specified column. Otherwise does nothing.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
QAbstractTableModel override.
Definition: ZTableModel.cpp:65
ZblLogWorker * m_logWorker
A background thread worker object to handle logging rules and log record output.
void populateRegisteredRules()
Appends all log categories that have so far been registered with a call to ZblLogManager::registerLog...
void updateApplicationSettings()
void setQtLogRulesFromModel()
Sets Qt&#39;s internal logging rules from data contained in log manager&#39;s logging rules model...
Q_INVOKABLE QObject * getLogBuffer()
Obtain the log buffer data model.
void onLogWorkerOutputParametersUpdated(QVariant logParams)
Synchronizes log manager and log worker output parameters.
static LogFileFormat fileFormatFromString(const QString &logFileFormat)
static ZblLogManager & zInit(QObject *parent)
Construct, initialize and obtain a reference to Zuble&#39;s log manager object.
QString toLiterallyTrue(bool value)
Definition: zglobal.h:76
#define Z_FAC_JS
Definition: zglobal.h:123
bool populateMetaRules()
Appends all log categories defined in the Zuble application&#39;s zblconfig.xml configuration file to the...
bool loadCurrentLogProfile()
Replaces the current log settings with those from the specified file.
Q_INVOKABLE QJsonObject getJsonData() const
Copies data from the table model into a newly created JSON object and returns it. ...
bool registerLogCategory(const QString logCategory)
Adds the specified log category to the registered log category list.
bool appendRuleModelCategory(const QString &logCategory)
Adds a new category to the rule model. If the category already exists in the rule model the request t...
int getRoleCount() const
Returns the number of roles defined for this model.
enum Zbl::logFileFormat LogFileFormat
Specifies the output format for Zuble log files.
Q_INVOKABLE void truncate(int rowCount, bool removeFromFront=false)
void setApplicationObject(ZApplication *appObject)
Connects the log manager to appropriate Zuble core plugin signals and attempts to load the current lo...
#define ZBL_REGISTER_LOGGED_OBJECT
Definition: zglobal.h:104
Q_INVOKABLE void dumpModelData() const
dumps diagnostic information about the model to the debug output.
static int m_maxBufferSize
The number of logging records the circular log buffer will attempt to hold.
bool m_loadingProfile
true when log manager is currently loading a log profile, false otherwise
LogFileFormat m_fileFormat
The current logger output file format.
Definition: ZblLogParams.h:104
bool loadFilterRules(const QString &logProfileBundleID)
Loades the log filter rules from the specified log profile into the log rule model.
void outputParametersUpdated(QVariant logParams)
const QString & message() const
void onRuleModelModified(int dataRole, int rowNumber)
Responds to changes in the rule model by updating Qt&#39;s internal rule model and the current log settin...
Q_INVOKABLE bool setFileWatcher(bool enabled)
Enables or disables the file system watcher. This method must be called to enable this object to emit...
Definition: ZSettings.cpp:386
ZDataRow m_modelBooleanRow
A data row to use as a template for constructing role rows.
ZDataRow m_modelCategoryRow
A data row to use as a template for constructing role rows.
void updateLogProfileSettings()
Loads a new copy of the log profile settings from the settings file.
Q_INVOKABLE QList< int > roles() const
Determines which roles are in the data set. This method may block the current thread.
ZblLogParams m_logParam
Current Zuble logging parameters.
Q_INVOKABLE QVariant getValue(int role, int row)
Obtains the value of the specified data cell.
static bool containsBundle(const QString &id)
Definition: ZSettings.cpp:525
bool m_enableModelOutput
Enables log output to log histogram buffer.
Definition: ZblLogParams.h:93
logFileFormat
Specifies the output format for Zuble log files.
Definition: zglobal.h:197
Definition: ZAndGate.cpp:6
ZBL_DECLARE_LOGGED_OBJECT QString getQtLogRule(const QString &category, const QString &level, bool enabled)
Returns a Qt logging rule string for the given log category and level.
static ZblLogManager & zInstance()
Obtain a reference to Zuble&#39;s log manager object.
ZTableModel * createLogBufferModel(QObject *parent=NULL)
Creates a new log record data model by appending the appropriate data rules. The caller assumes owner...
QMap< int, QList< QVariant > > ZRoleRow
Represents a single row (or column for column headers) of data cell values for multiple roles...
Definition: ZTableModel.h:63
#define ZBL_SLOT_BEGIN_TRY
Definition: zglobal.h:128
Abstract base class for Zuble log output modules.
Definition: ZblLogOutput.h:54
static const QString m_defaultOutputFilePath
TBD: currently unused!
#define ZBL_DEFINE_LOGGED_OBJECT(class_name)
Definition: zglobal.h:99
void settingsModified(const QString &path)
Sent whenever the settings file is modified by this or other programs.
#define zWarning()
Definition: zglobal.h:111
ZTableModel * m_logBuffer
Zuble&#39;s log buffer provides a viewable record of recent log activity.
This two dimensional table model is used to store and manipulate data.
Definition: ZTableModel.h:96
Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: ZSettings.cpp:1124
ZTableModel * m_logRules
A data model containing the current logging rules in effect for each known log category.
QString m_logOutputDir
Directory in which log files are created.
Definition: ZblLogParams.h:110
void saveFilterRules()
Saves the current state of the logging rules data model to the current log profile.
QString getQtLogRulesForRow(int rowNumber)
Returns a Qt logging rule string for all logging levels for the specified row (log category) in the l...
#define zDebug()
Definition: zglobal.h:113
virtual void timerEvent(QTimerEvent *event)
Timer is used to flush excess log records from the log record buffer.
Q_INVOKABLE int getLogBufferRoleCount()
Obtain the number of roles in the log buffer data model.
This class contains the Zuble logging parameters that control log output.
Definition: ZblLogParams.h:38
bool m_syncProfiles
true if all application instances update the same log profile, false if each instance updates a separ...
static const QString m_zubleSettingsBundleId
The bundle ID for the zuble application settings = "default".
Q_INVOKABLE void putJsonData(QJsonObject data)
Writes data from the specified JSON object into the data table. Previous contents of the data table a...
void settingsBundleAvailable(QString bundleId)
This signal is sent by the settings bundle manager when an application settings bundle is added to th...
static QString getObjectThreadAddress(QObject *object)
Returns the human-readable memory address of the specified object&#39;s thread.
bool startLogThread()
Starts log manager&#39;s background thread.
void onLogProfileModified(const QString &path)
Responds to changes to the current log settings by replacing the log profile with the current log set...
static QString createTemporaryLogProfile(const QString &nameTemplate, bool remove=false)
Creates an empty file with a unique file name in the log profile directory.
QHash< int, QByteArray > ZRoleNames
Maps role numbers to role names as a hash table object.
Definition: ZTableModel.h:44
void onZubleSettingsModified(const QString &path)
Responds to changes in the Zuble application settingsby updating log manager&#39;s internal state...
static QString resolvePath(const QString &path, bool includeUrlScheme=true)
Converts relative file paths into canonical file paths. Paths prefixed with prefix are mapped relativ...
Q_INVOKABLE void setColumnCount(int count)
Sets the number of columns the table will contain. This method may block the current thread...
bool inObjectThread(const QObject &object)
Definition: zglobal.h:173
Zuble&#39;s Singleton Application Object.
Definition: ZApplication.h:54
static ZblLogManager * m_zLog
The one and only ZblLogManager object in this process.
Zuble objects embed this class to save local logging state.
#define LOCK_ZLOGCATEGORIES
Definition: zglobal.h:115
void stopLogThread()
Stops log manager&#39;s background thread.
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
friend class ZblLogWorker
Definition: ZblLogManager.h:74
static const int m_logBufRoleCount
The number of roles contained in a log file record.
Zuble&#39;s Qt Exception Object.
Definition: ZblException.h:45
static QString getCurrentThreadAddress()
Returns the human-readable memory address of the current thread.
bool m_flush
true = flush output buffers and close streams before returning (used for fatal/abort messages) ...
Definition: ZblLogMessage.h:57
QScopedPointer< ZSettings > ZScopedSettings
Definition: ZSettings.h:516
static QString getLogSettingsMetadata()
Returns an xml data structure that defines the zuble application&#39;s log settings.
static void zMessageHandler(QtMsgType msgType, const QMessageLogContext &logCtx, const QString &text)
Zuble&#39;s Qt message handler.
static ZSettings * getBundleSettings(const QString &id)
Returns the ZSettings object for the specified settings bundle ID. Caller takes ownership of the ZSet...
Definition: ZSettings.cpp:645
ZSettings * m_logSettings
A ZSettings object to track the application settings for changes that control Zuble logging...
QString m_LogSource
String inserted into the "source" field of log records.
bool createRuleModel()
Creates the log manager&#39;s log rules data model and initializes associated variables used to optimize ...
ZSettings * m_profileSettings
A ZSettings object to track the current log profile for changes to the profile settings.
static void registerLogCategory()
Definition: ZTableModel.cpp:47
ZblLogManager(QObject *parent=0)
Q_INVOKABLE bool addRole(int roleNumber)
Adds the specified role to the data model. This method may block the current thread.
bool m_enableLogging
Enables Zuble logging.
Definition: ZblLogParams.h:75
ZRoleRow m_modelRoleRow
A role row to use as a template for adding logging rules to the rule model.
bool isCommandLineLogging()
Returns true if -zl option is passed in on command line.
Q_INVOKABLE void sync()
Blocks thread until model&#39;s event queue has been processed.
QList< ZblLogOutput * > zLogModuleList
Definition: ZblLogOutput.h:60
static bool insertBundle(const QString &id, const QString &metaPath, QSettings::Scope scope=QSettings::UserScope, const QString organization=QString(), const QString application=QString())
Adds the specified settings bundle to the settings repository.
Definition: ZSettings.cpp:530
This class is used to transfer log messages between threads.
Definition: ZblLogMessage.h:38
static const QString m_logFilterRulesKey
The log filter rules key = "log-filter-rules".
#define ZBL_SLOT_END_RETURN(return_success, return_failed, facility, code, error_message)
Definition: zglobal.h:141
bool m_enableStdOutput
Enables log output to stdout.
Definition: ZblLogParams.h:87
#define zCritical()
Definition: zglobal.h:112
QThread m_logThread
A background thread to handle logging rules and log record output.
QList< QVariant > ZDataRow
Represents a single row (or column for column headers) of data cell values for a single role...
Definition: ZTableModel.h:57
static const QString m_currentLogSettingsBundleId
The bundle ID for the current logging profile = "current-log-profile".
bool m_enableDetails
Logs will include detailed debugging information.
Definition: ZblLogParams.h:99
int m_timerID
ID for the log manager&#39;s event timer.
Q_INVOKABLE QObject * getLogRules()
Obtain the logging rules data model.
void setLogOutputParameters()
Gathers the application and log profile settings that affect log output and synchronizes those parame...