25#include <ArmarXCore/interface/core/ManagedIceObjectDefinitions.h>
27#include <ArmarXGui/gui-plugins/LoggingPlugin/ui_FilterDialog.h>
39#include <boost/algorithm/string/regex.hpp>
41#include <QColorDialog>
43#include <QInputDialog>
55#define USERROLE_LOGTABLEID Qt::UserRole + 1
56#define USERROLE_LOGTABLEPTR Qt::UserRole + 2
57#define USERROLE_BASENAME Qt::UserRole + 3
58#define REGEX_COLORS "\033\\[(\\d|\\w?)[;]?(\\d+)m"
59#define ALL_MESSAGES_FILTER "All Messages"
64 logTable(NULL), loggingPaused(false), verbosityLevel(1), customToolbar(0)
67 qRegisterMetaType<Qt::Orientation>(
"Qt::Orientation");
68 qRegisterMetaType<std::string>(
"std::string");
71 for (
int i = 0; i < eLogLevelCount; i++)
76 ui.cbVerbosityLevel->setCurrentIndex(1);
79 verbosityLevel =
ui.cbVerbosityLevel->currentIndex();
80 connect(
ui.cbVerbosityLevel,
81 QOverload<int>::of(&QComboBox::currentIndexChanged),
83 [
this](
int index) { verbosityLevel = index; });
85 ui.lvFilters->setCurrentItem(
ui.lvFilters->invisibleRootItem()->child(0));
89 ui.lvFilters->setSortingEnabled(
false);
91 pendingEntriesTimer =
new QTimer(
getWidget());
92 pendingEntriesTimer->setInterval(50);
99 filterBadgeTimer =
new QTimer(
getWidget());
100 filterBadgeTimer->setInterval(250);
101 connect(filterBadgeTimer, SIGNAL(timeout()),
this, SLOT(
updateFilterList()));
107 sizes.push_back(400);
108 ui.splitter->setSizes(sizes);
121 qRegisterMetaType<LogMessage>(
"LogMessage");
128 connect(
ui.btnAddFilter, SIGNAL(clicked()),
this, SLOT(
addFilter()));
130 connect(
ui.btnPause, SIGNAL(toggled(
bool)),
this, SLOT(
pauseLogging(
bool)));
132 connect(
ui.btnClearAllLogs, SIGNAL(clicked()),
this, SLOT(
clearAllLogs()));
133 connect(
ui.lvFilters,
134 SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
137 connect(
ui.lvFilters,
138 SIGNAL(itemDoubleClicked(QTreeWidgetItem*,
int)),
151 &QWidget::customContextMenuRequested,
154 QShortcut* deleteLabelShortcut =
new QShortcut(QKeySequence::Delete,
ui.lvLabels);
155 deleteLabelShortcut->setContext(Qt::WidgetShortcut);
159 ui.edtLiveFilter->hide();
160 ui.edtLiveSearch->setFocus();
171 ui.cbVerbosityLevel->setCurrentIndex(settings->value(
"verbosityLevel", 2).toInt());
172 ui.cbAutoComponentFilters->setChecked(settings->value(
"autoFilterAdding",
true).toBool());
179 settings->setValue(
"verbosityLevel",
ui.cbVerbosityLevel->currentIndex());
181 settings->setValue(
"autoFilterAdding",
ui.cbAutoComponentFilters->isChecked());
231 LogViewer::loggingGroupNameToFilterName(
const QString& loggingGroupName)
const
233 return loggingGroupName.isEmpty() ?
"" : (
"#" + loggingGroupName);
237 LogViewer::sortFilterList()
239 ui.lvFilters->sortItems(0, Qt::AscendingOrder);
243 LogViewer::autoFilterId(
const QString& loggingGroupName,
const QString& componentName)
const
245 const QString filler =
246 (loggingGroupName.length() > 0 && componentName.length() > 0) ?
"_" :
"";
247 return loggingGroupNameToFilterName(loggingGroupName) + filler + componentName;
251 LogViewer::ensureAutoFilter(
const QString& filterId,
252 const QString& loggingGroupName,
253 const QString& componentName,
256 if (filterId.isEmpty())
261 const auto it = autoFilterMap.find(filterId);
262 if (it != autoFilterMap.end())
267 if (!mayCreate || !
ui.cbAutoComponentFilters->isChecked() || filterMap.count(filterId))
274 const QString filterName = componentName.length() > 0
276 : loggingGroupNameToFilterName(loggingGroupName);
280 filterId, filterName, loggingGroupName, componentName,
"", eDEBUG,
"",
"",
"");
285 generalFilters.erase(std::remove(generalFilters.begin(), generalFilters.end(), table),
286 generalFilters.end());
287 autoFilterMap.insert(filterId, table);
293 LogViewer::ingestBatch(
LogTable* table,
const std::vector<LogSeq>& seqs)
295 if (!table || seqs.empty())
300 const QString liveFilter = table->getCurrentLiveFilter();
301 MessageType batchMaxLevel = eUNDEFINED;
302 const int rowsAdded = table->getModel()->addEntries(seqs, &batchMaxLevel);
304 if (!table->getModel()->isMaterialised())
308 table->notifyNewMessages(rowsAdded, batchMaxLevel);
312 if (rowsAdded > 0 && !liveFilter.isEmpty())
317 const int count = table->getModel()->rowCount();
318 for (
int r = std::max(0, count - rowsAdded); r < count; r++)
320 table->liveFilterRow(liveFilter, r);
333 const std::string&
tag,
334 MessageType severity,
335 const std::string& message,
336 const std::string& file,
338 const std::string& function,
349 msg.function = function;
365 if (verbosityLevel.load() <= msg.type)
367 std::unique_lock lock(pendingEntriesMutex);
368 if (pendingEntries.size() >= maxPendingEntries)
376 pendingEntries.push_back(msg);
385 if (filterStr.length() == 0)
389 else if (filterStr.length() < 3)
396 logTable->liveFilter(filterStr, startRow);
397 lastLiveSearchEditChangeTime = IceUtil::Time::now();
403 if (searchStr.length() == 0)
406 ui.btnNextItem->setEnabled(
false);
407 ui.btnPreviousItem->setEnabled(
false);
413 if (!
logTable->liveSearch(searchStr))
416 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
419 ui.btnNextItem->setEnabled(
false);
420 ui.btnPreviousItem->setEnabled(
false);
424 ui.btnNextItem->setEnabled(
true);
425 ui.btnPreviousItem->setEnabled(
true);
437 expireOrphanedMarkers();
448 std::map<QString, LogTable*>::iterator it = filterMap.begin();
450 for (; it != filterMap.end(); it++)
452 it->second->getModel()->clearData();
456 markerRegistry.clear();
462 LogViewer::expireOrphanedMarkers()
473 loggingPaused = pause;
477 LogViewer::setupLogTable(
LogTable* logTable)
491 [
this](
LogSeq seq) { markerRegistry.
add(seq); });
499 markerRegistry.
remove(
id);
508 setupLogTable(newLogTable);
510 QTreeWidgetItem* item =
new QTreeWidgetItem(
ui.lvFilters);
512 item->setText(0, standardFilterStr);
517 filterMap[standardFilterStr] = newLogTable;
519 generalFilters.push_back(newLogTable);
520 ui.splitter->addWidget(newLogTable);
528 QString loggingGroup,
529 QString componentFilter,
531 MessageType minimumVerbosity,
532 QString messageFilter,
534 QString functionFilter)
537 if (filterMap.find(filterId) != filterMap.end())
539 QString msg =
"A filter with the id " + filterId +
" exists already";
545 setupLogTable(newLogTable);
552 if (loggingGroup.length())
559 if (componentFilter.length())
565 if (tagFilter.length())
570 if (messageFilter.length())
575 if (fileFilter.length())
580 if (functionFilter.length())
583 functionFilter.toStdString());
587 QString::number(minimumVerbosity).toStdString());
589 QTreeWidgetItem* item =
new QTreeWidgetItem();
590 item->setText(0, filterName);
594 filterMap[filterId] = newLogTable;
598 generalFilters.push_back(newLogTable);
599 Ice::StringSeq children;
600 for (
int i = 0; i <
ui.lvFilters->invisibleRootItem()->childCount(); i++)
602 children.push_back(
ui.lvFilters->invisibleRootItem()->child(i)->text(0).toStdString());
605 QTreeWidgetItem* grpItem = NULL;
606 auto grpFilterId = loggingGroupNameToFilterName(loggingGroup);
607 for (
int i = 0; i <
ui.lvFilters->invisibleRootItem()->childCount(); i++)
609 if (
ui.lvFilters->invisibleRootItem()
612 .toString() == grpFilterId)
614 grpItem =
ui.lvFilters->invisibleRootItem()->child(i);
620 grpItem->addChild(item);
626 ui.lvFilters->insertTopLevelItem(0, item);
631 ui.splitter->addWidget(newLogTable);
638 if (componentName.length() == 0 && loggingGroupName.length() == 0)
643 const QString filterId = autoFilterId(loggingGroupName, componentName);
644 const bool existedBefore = autoFilterMap.contains(filterId);
645 return ensureAutoFilter(filterId, loggingGroupName, componentName,
true) !=
nullptr &&
654 if (!filterDialog.exec())
659 QString filterName = filterDialog.
ui->edtFilterName->text();
661 if (filterMap.find(filterName) != filterMap.end())
663 QString msg =
"A filter with this name already exists";
669 setupLogTable(newLogTable);
673 if (filterDialog.
ui->editComponent->text().length())
679 if (filterDialog.
ui->edtTag->text().length())
682 filterDialog.
ui->edtTag->text().toStdString());
685 if (filterDialog.
ui->edtMessage->text().length())
688 filterDialog.
ui->edtMessage->text().toStdString());
691 if (filterDialog.
ui->edtFile->text().length())
694 filterDialog.
ui->edtFile->text().toStdString());
697 if (filterDialog.
ui->edtFunction->text().length())
700 filterDialog.
ui->edtFunction->text().toStdString());
703 if (filterDialog.
ui->cbVerbosity->currentIndex() != -1)
707 QString::number(filterDialog.
ui->cbVerbosity->currentIndex()).toStdString());
710 QTreeWidgetItem* item =
new QTreeWidgetItem();
711 item->setText(0, filterName);
715 filterMap[filterName] = newLogTable;
718 generalFilters.push_back(newLogTable);
719 ui.lvFilters->insertTopLevelItem(
ui.lvFilters->invisibleRootItem()->childCount(), item);
722 ui.splitter->addWidget(newLogTable);
732 if (filterMap.find(filterId) == filterMap.end())
748 const QString value = QString::fromStdString(activeFilter.value);
750 switch (activeFilter.column)
753 filterDialog.
ui->editComponent->setText(value);
756 filterDialog.
ui->edtTag->setText(value);
759 filterDialog.
ui->cbVerbosity->setCurrentIndex(value.toInt());
762 filterDialog.
ui->edtMessage->setText(value);
765 filterDialog.
ui->edtFile->setText(value);
768 filterDialog.
ui->edtFunction->setText(value);
776 if (!filterDialog.exec())
780 item->setText(0, filterDialog.
ui->edtFilterName->text());
782 logTable->getModel()->resetFilters();
784 if (filterDialog.
ui->editComponent->text().length())
787 filterDialog.
ui->editComponent->text().toStdString());
790 if (filterDialog.
ui->edtTag->text().length())
793 filterDialog.
ui->edtTag->text().toStdString());
796 if (filterDialog.
ui->edtMessage->text().length())
799 filterDialog.
ui->edtMessage->text().toStdString());
802 if (filterDialog.
ui->edtFile->text().length())
805 filterDialog.
ui->edtFile->text().toStdString());
808 if (filterDialog.
ui->edtFunction->text().length())
811 filterDialog.
ui->edtFunction->text().toStdString());
814 if (filterDialog.
ui->cbVerbosity->currentIndex() != -1)
818 QString::number(filterDialog.
ui->cbVerbosity->currentIndex()).toStdString());
825 if (autoFilterMap.remove(filterId) > 0)
830 logTable->getModel()->reapplyAllFilters();
837 if (
ui.lvFilters->invisibleRootItem()->childCount() == 1)
842 auto item =
ui.lvFilters->currentItem();
854 std::map<QString, LogTable*>::iterator it =
857 if (it == filterMap.end())
861 QList<QTreeWidgetItem*> itemsToDelete, iterationList({item});
862 while (!iterationList.isEmpty())
864 auto curItem = iterationList.front();
865 itemsToDelete << curItem;
866 iterationList.pop_front();
867 iterationList << curItem->takeChildren();
869 ui.lvFilters->setCurrentItem(
ui.lvFilters->invisibleRootItem()->child(0));
870 for (
auto& item : itemsToDelete)
872 auto parent = item->parent();
875 parent->removeChild(item);
879 ui.lvFilters->invisibleRootItem()->removeChild(item);
881 std::map<QString, LogTable*>::iterator it =
885 if (it == filterMap.end())
891 generalFilters.erase(
892 std::remove(generalFilters.begin(), generalFilters.end(),
logTable),
893 generalFilters.end());
894 autoFilterMap.remove(it->first);
896 filterBadgeCache.erase(it->first);
913 item->setFont(0, font);
915 if (filterMap.find(filterId) == filterMap.end())
936 ui.edtLiveFilter->setText(
logTable->getLiveFilterStr());
937 ui.edtLiveSearch->setText(
logTable->getModel()->getCurrentSearchStr());
944 QList<QTreeWidgetItem*> itemsToUpdate, iterationList({
ui.lvFilters->invisibleRootItem()});
945 while (!iterationList.isEmpty())
947 auto curItem = iterationList.front();
948 itemsToUpdate << curItem;
949 iterationList.pop_front();
950 for (
int i = 0; i < curItem->childCount(); ++i)
952 iterationList << curItem->child(i);
956 for (
auto item : itemsToUpdate)
959 auto filterIt = filterMap.find(filterId);
960 if (filterIt == filterMap.end())
965 LogTable* curLogtable = filterIt->second;
977 const auto cached = filterBadgeCache.find(filterId);
978 if (cached != filterBadgeCache.end() && cached->second.count == newCount &&
979 cached->second.level == newLevel)
983 filterBadgeCache[filterId] = FilterBadge{newCount, newLevel};
990 item->setFont(0, font);
992 item->setBackgroundColor(0,
ui.lvFilters->palette().base().color());
997 item->setFont(0, font);
999 QString::number(newCount) +
")";
1001 if (newLevel == eWARN)
1003 item->setBackgroundColor(0, QColor(216, 120, 50));
1005 else if (newLevel == eERROR)
1007 item->setBackgroundColor(0, QColor(255, 90, 80));
1009 else if (newLevel == eFATAL)
1011 item->setBackgroundColor(0, QColor(255, 60, 50));
1015 item->setBackgroundColor(0,
ui.lvFilters->palette().base().color());
1019 item->setText(0, newContent);
1020 item->setToolTip(0, newContent);
1025 const unsigned long long dropped = droppedEntryCount.load();
1026 if (dropped != lastReportedDropCount)
1028 lastReportedDropCount = dropped;
1032 QString(
"LogViewer: dropped %1 log messages (ingest overloaded)")
1042 throw LocalException() <<
"Not yet implemented";
1048 ui.lvLabels->clear();
1049 for (
const LogMarker& marker : markerRegistry.markers())
1051 if (!store.contains(marker.seq))
1055 const LogMessage& snapshot = store.at(marker.seq).message;
1058 QPixmap swatch(12, 12);
1059 swatch.fill(marker.color);
1062 IceUtil::Time time = IceUtil::Time::microSeconds(snapshot.time);
1063 std::string timeStr = time.toDateTime();
1064 const auto spacePos = timeStr.find(
' ');
1065 if (spacePos != std::string::npos)
1067 timeStr = timeStr.substr(spacePos + 1);
1070 QString what = QString::fromStdString(snapshot.what);
1071 const int newlinePos = what.indexOf(
'\n');
1072 if (newlinePos >= 0)
1074 what.truncate(newlinePos);
1076 constexpr int maxWhatLength = 60;
1077 if (what.length() > maxWhatLength)
1079 what.truncate(maxWhatLength);
1083 const QString text = QString::fromStdString(timeStr) +
" " +
1084 QString::fromStdString(snapshot.who) +
" " + what;
1086 QListWidgetItem* item =
new QListWidgetItem(QIcon(swatch), text,
ui.lvLabels);
1087 item->setData(Qt::UserRole,
static_cast<qulonglong
>(marker.id));
1088 item->setToolTip(text);
1101 for (
const LogMarker& marker : markerRegistry.markers())
1103 if (marker.id !=
id)
1109 const int row =
logTable->getModel()->rowForSeq(marker.seq);
1112 const QModelIndex
index =
logTable->getModel()->index(row, 0);
1113 logTable->scrollTo(
index, QAbstractItemView::PositionAtCenter);
1124 QListWidgetItem* item =
ui.lvLabels->itemAt(pos);
1132 QAction* recolorAction = menu.addAction(tr(
"Change color..."));
1133 QAction* removeAction = menu.addAction(tr(
"Remove label"));
1134 QAction* chosen = menu.exec(
ui.lvLabels->viewport()->mapToGlobal(pos));
1136 if (chosen == removeAction)
1138 markerRegistry.remove(
id);
1140 else if (chosen == recolorAction)
1143 for (
const LogMarker& marker : markerRegistry.markers())
1145 if (marker.id ==
id)
1147 initial = marker.color;
1151 const QColor color =
1152 QColorDialog::getColor(initial,
getWidget(), tr(
"Choose label color"));
1153 if (color.isValid())
1155 markerRegistry.recolor(
id, color);
1163 QListWidgetItem* item =
ui.lvLabels->currentItem();
1169 markerRegistry.remove(
id);
1176 if (
getState() >= eManagedIceObjectExiting)
1181 std::vector<LogMessage> pendingEntriesTemp;
1183 std::unique_lock lock(pendingEntriesMutex);
1184 if (pendingEntries.size() <= maxEntriesPerTick)
1186 pendingEntriesTemp.swap(pendingEntries);
1192 const auto batchEnd = pendingEntries.begin() + maxEntriesPerTick;
1193 pendingEntriesTemp.assign(std::make_move_iterator(pendingEntries.begin()),
1194 std::make_move_iterator(batchEnd));
1195 pendingEntries.erase(pendingEntries.begin(), batchEnd);
1205 std::vector<LogSeq> wholeBatch;
1206 wholeBatch.reserve(pendingEntriesTemp.size());
1207 QHash<LogTable*, std::vector<LogSeq>> autoBuckets;
1211 for (LogMessage& msg : pendingEntriesTemp)
1214 if (msg.what.find(
'\033') != std::string::npos)
1216 msg.what = boost::regex_replace(msg.what, re,
"");
1221 const LogSeq seq = store.append(msg);
1222 wholeBatch.push_back(seq);
1229 const QString group = QString::fromStdString(msg.group);
1230 const QString
component = QString::fromStdString(msg.who);
1234 const bool mayCreate =
ui.cbVerbosityLevel->currentIndex() <= msg.type;
1236 const QString groupKey = autoFilterId(group, QString());
1237 const QString componentKey = autoFilterId(group,
component);
1239 if (
LogTable* groupTable = ensureAutoFilter(groupKey, group, QString(), mayCreate))
1241 autoBuckets[groupTable].push_back(seq);
1243 if (componentKey != groupKey)
1246 ensureAutoFilter(componentKey, group,
component, mayCreate))
1248 autoBuckets[componentTable].push_back(seq);
1255 const bool autoScroll =
1257 logTable->verticalScrollBar()->value() ==
logTable->verticalScrollBar()->maximum();
1259 for (
LogTable* log : generalFilters)
1261 ingestBatch(log, wholeBatch);
1263 for (
auto it = autoBuckets.begin(); it != autoBuckets.end(); ++it)
1265 ingestBatch(it.key(), it.value());
1280 const LogSeq firstSeq = store.firstSeq();
1281 for (
auto& filterEntry : filterMap)
1283 filterEntry.second->getModel()->dropRowsBefore(firstSeq);
1286 expireOrphanedMarkers();
1295 ui.edtLiveSearch->show();
1296 ui.btnPreviousItem->show();
1297 ui.btnNextItem->show();
1298 ui.edtLiveFilter->hide();
1302 ui.edtLiveSearch->hide();
1303 ui.btnPreviousItem->hide();
1304 ui.btnNextItem->hide();
1305 ui.edtLiveFilter->show();
1315 if (!
logTable->selectNextSearchResult(
false))
1318 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
1329 if (!
logTable->selectNextSearchResult(
true))
1332 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
1343 if (parent != customToolbar->parent())
1345 customToolbar->setParent(parent);
1348 return customToolbar;
1351 customToolbar =
new QToolBar(parent);
1352 customToolbar->setIconSize(QSize(16, 16));
1353 customToolbar->addAction(
1356 return customToolbar;
#define ARMARX_LOG_VERBOSITYSTR
#define ARMARX_LOG_COMPONENTSTR
#define ARMARX_LOG_FILESTR
#define ARMARX_LOG_TAGSTR
#define ARMARX_LOG_MESSAGESTR
#define ARMARX_LOG_LOGGINGGROUPSTR
#define ARMARX_LOG_FUNCTIONSTR
#define USERROLE_BASENAME
#define ALL_MESSAGES_FILTER
#define USERROLE_LOGTABLEID
bool expireBefore(LogSeq firstSeq)
Remove every marker whose line has been dropped from the store, i.e.
LogMarkerId markerIdForSeq(LogSeq seq) const
Id of the marker pinning seq, or 0 if it is not pinned.
void add(LogSeq seq)
Pin the store entry seq, assigning it the next Glasbey color.
void remove(LogMarkerId id)
Remove the marker with the given id (no-op if unknown).
static std::string levelToString(MessageTypeT type)
LogSeq firstSeq() const
Sequence number of the oldest retained entry.
void setMarkerRegistry(LogMarkerRegistry *registry)
Attach the shared pin registry.
void addFilter(const std::string &columnName, const std::string &filter, MatchMode mode=MatchMode::Contains)
void setStore(const LogStore *store)
Attach the shared backing store. Must be called before any ingestion.
MessageType getMaxNewLogLevelType()
LogTableModel * getModel()
void removeLabelRequested(LogSeq seq)
Emitted from the context menu to remove the pin on the given line.
void addLabelRequested(LogSeq seq)
Emitted from the context menu to pin the given line (assign a label).
void pauseLogging(bool pause=false)
void componentConnected()
void selectPreviousSearchResult()
void onInitComponent() override
Pure virtual hook for the subclass.
void editFilter(QTreeWidgetItem *item, int column)
void insertPendingEntries()
bool onClose() override
If you overwrite this method, make sure to call this implementation at the end of your implementation...
void removeSelectedLabel()
Remove the pin currently selected in the Labels panel.
void refreshLabelsPanel()
Rebuild the Labels panel from the pin registry.
QPointer< QWidget > getCustomTitlebarWidget(QWidget *parent) override
getTitleToolbar returns a pointer to the a toolbar widget of this controller.
void selectNextSearchResult()
void removeSelectedFilter()
LogTable * addEmptyFilter()
void removeFilter(QTreeWidgetItem *item)
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
void updateFilterListSignal()
void performLiveSearch(QString searchStr)
void OpenConfigureDialog()
void onConnectComponent() override
Pure virtual hook for the subclass.
void write(const std::string &who, Ice::Long time, const std::string &tag, MessageType severity, const std::string &message, const std::string &file, Ice::Int line, const std::string &function, const Ice::Current &=Ice::emptyCurrent)
void filterSelectionChanged(QTreeWidgetItem *item, QTreeWidgetItem *previous)
bool checkAndAddNewFilter(const QString &loggingGroupName, const QString &componentName)
This function checks, if there are new components in the log and if so, it creates new filters for th...
void onExitComponent() override
Hook for subclass.
void writeLog(const LogMessage &msg, const Ice::Current &=Ice::emptyCurrent) override
void performLiveFilter(QString searchStr, int startRow=0)
void labelClicked(QListWidgetItem *item)
Focus the clicked label's line in the currently shown message view.
void showLabelsContextMenu(const QPoint &pos)
Recolor / remove menu for the Labels panel.
void searchTypeChanged(int index)
void usingTopic(const std::string &name, bool orderedPublishing=false)
Registers a proxy for subscription after initialization.
int getState() const
Retrieve current state of the ManagedIceObject.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::uint64_t LogMarkerId
quint64 LogSeq
Identifies a stored log entry for as long as it is retained.
const LogSender::manipulator flush
A single pinned log line: the store entry it refers to plus the color assigned to it.