25#include <ArmarXCore/interface/core/ManagedIceObjectDefinitions.h>
27#include <ArmarXGui/gui-plugins/LoggingPlugin/ui_FilterDialog.h>
37#include <boost/algorithm/string/regex.hpp>
39#include <QColorDialog>
41#include <QInputDialog>
53#define USERROLE_LOGTABLEID Qt::UserRole + 1
54#define USERROLE_LOGTABLEPTR Qt::UserRole + 2
55#define USERROLE_BASENAME Qt::UserRole + 3
56#define REGEX_COLORS "\033\\[(\\d|\\w?)[;]?(\\d+)m"
57#define ALL_MESSAGES_FILTER "All Messages"
62 logTable(NULL), loggingPaused(false), verbosityLevel(1), customToolbar(0)
65 qRegisterMetaType<Qt::Orientation>(
"Qt::Orientation");
66 qRegisterMetaType<std::string>(
"std::string");
69 for (
int i = 0; i < eLogLevelCount; i++)
74 ui.cbVerbosityLevel->setCurrentIndex(1);
77 verbosityLevel =
ui.cbVerbosityLevel->currentIndex();
78 connect(
ui.cbVerbosityLevel,
79 QOverload<int>::of(&QComboBox::currentIndexChanged),
81 [
this](
int index) { verbosityLevel = index; });
83 ui.lvFilters->setCurrentItem(
ui.lvFilters->invisibleRootItem()->child(0));
84 ui.lvFilters->setSortingEnabled(
true);
85 ui.lvFilters->sortByColumn(0, Qt::AscendingOrder);
86 pendingEntriesTimer =
new QTimer(
getWidget());
87 pendingEntriesTimer->setInterval(50);
96 ui.splitter->setSizes(sizes);
109 qRegisterMetaType<LogMessage>(
"LogMessage");
116 connect(
ui.btnAddFilter, SIGNAL(clicked()),
this, SLOT(
addFilter()));
118 connect(
ui.btnPause, SIGNAL(toggled(
bool)),
this, SLOT(
pauseLogging(
bool)));
120 connect(
ui.btnClearAllLogs, SIGNAL(clicked()),
this, SLOT(
clearAllLogs()));
121 connect(
ui.lvFilters,
122 SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
125 connect(
ui.lvFilters,
126 SIGNAL(itemDoubleClicked(QTreeWidgetItem*,
int)),
139 &QWidget::customContextMenuRequested,
142 QShortcut* deleteLabelShortcut =
new QShortcut(QKeySequence::Delete,
ui.lvLabels);
143 deleteLabelShortcut->setContext(Qt::WidgetShortcut);
147 ui.edtLiveFilter->hide();
148 ui.edtLiveSearch->setFocus();
159 ui.cbVerbosityLevel->setCurrentIndex(settings->value(
"verbosityLevel", 2).toInt());
160 ui.cbAutoComponentFilters->setChecked(settings->value(
"autoFilterAdding",
true).toBool());
167 settings->setValue(
"verbosityLevel",
ui.cbVerbosityLevel->currentIndex());
169 settings->setValue(
"autoFilterAdding",
ui.cbAutoComponentFilters->isChecked());
219 LogViewer::loggingGroupNameToFilterName(
const QString& loggingGroupName)
const
221 return loggingGroupName.isEmpty() ?
"" : (
"#" + loggingGroupName);
232 const std::string&
tag,
233 MessageType severity,
234 const std::string& message,
235 const std::string& file,
237 const std::string& function,
248 msg.function = function;
264 if (verbosityLevel.load() <= msg.type)
266 std::unique_lock lock(pendingEntriesMutex);
267 pendingEntries.push_back(msg);
276 if (filterStr.length() == 0)
280 else if (filterStr.length() < 3)
287 logTable->liveFilter(filterStr, startRow);
288 lastLiveSearchEditChangeTime = IceUtil::Time::now();
294 if (searchStr.length() == 0)
297 ui.btnNextItem->setEnabled(
false);
298 ui.btnPreviousItem->setEnabled(
false);
304 if (!
logTable->liveSearch(searchStr))
307 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
310 ui.btnNextItem->setEnabled(
false);
311 ui.btnPreviousItem->setEnabled(
false);
315 ui.btnNextItem->setEnabled(
true);
316 ui.btnPreviousItem->setEnabled(
true);
328 expireOrphanedMarkers();
335 std::map<QString, LogTable*>::iterator it = filterMap.begin();
337 for (; it != filterMap.end(); it++)
339 it->second->getModel()->clearData();
343 markerRegistry.clear();
349 LogViewer::expireOrphanedMarkers()
354 bool nativeSomewhere =
false;
355 for (
const auto& [filterId, table] : filterMap)
357 if (table->getModel()->containsNativeLine(marker.snapshot))
359 nativeSomewhere =
true;
363 if (!nativeSomewhere)
365 markerRegistry.remove(marker.id);
373 loggingPaused = pause;
377 LogViewer::setupLogTable(
LogTable* logTable)
387 [
this](
const LogMessage& message) { markerRegistry.
add(message); });
391 [
this](
const LogMessage& message)
395 markerRegistry.
remove(marker->id);
404 setupLogTable(newLogTable);
406 QTreeWidgetItem* item =
new QTreeWidgetItem(
ui.lvFilters);
408 item->setText(0, standardFilterStr);
413 filterMap[standardFilterStr] = newLogTable;
414 ui.splitter->addWidget(newLogTable);
422 QString loggingGroup,
423 QString componentFilter,
425 MessageType minimumVerbosity,
426 QString messageFilter,
428 QString functionFilter)
431 if (filterMap.find(filterId) != filterMap.end())
433 QString msg =
"A filter with the id " + filterId +
" exists already";
439 setupLogTable(newLogTable);
446 if (loggingGroup.length())
453 if (componentFilter.length())
459 if (tagFilter.length())
464 if (messageFilter.length())
469 if (fileFilter.length())
474 if (functionFilter.length())
477 functionFilter.toStdString());
481 QString::number(minimumVerbosity).toStdString());
483 QTreeWidgetItem* item =
new QTreeWidgetItem();
484 item->setText(0, filterName);
488 filterMap[filterId] = newLogTable;
489 Ice::StringSeq children;
490 for (
int i = 0; i <
ui.lvFilters->invisibleRootItem()->childCount(); i++)
492 children.push_back(
ui.lvFilters->invisibleRootItem()->child(i)->text(0).toStdString());
495 QTreeWidgetItem* grpItem = NULL;
496 auto grpFilterId = loggingGroupNameToFilterName(loggingGroup);
497 for (
int i = 0; i <
ui.lvFilters->invisibleRootItem()->childCount(); i++)
499 if (
ui.lvFilters->invisibleRootItem()
502 .toString() == grpFilterId)
504 grpItem =
ui.lvFilters->invisibleRootItem()->child(i);
510 grpItem->addChild(item);
516 ui.lvFilters->insertTopLevelItem(0, item);
520 ui.splitter->addWidget(newLogTable);
527 if (componentName.length() == 0 && loggingGroupName.length() == 0)
532 if (!
ui.cbAutoComponentFilters->isChecked())
536 QString filler = (loggingGroupName.length() > 0 && componentName.length() > 0) ?
"_" :
"";
537 auto filterId = loggingGroupNameToFilterName(loggingGroupName) + filler + componentName;
538 auto filterName = componentName.length() > 0
540 : loggingGroupNameToFilterName(loggingGroupName);
542 if (filterMap.count(filterId))
547 addFilter(filterId, filterName, loggingGroupName, componentName,
"", eDEBUG,
"",
"",
"");
556 if (!filterDialog.exec())
561 QString filterName = filterDialog.
ui->edtFilterName->text();
563 if (filterMap.find(filterName) != filterMap.end())
565 QString msg =
"A filter with this name already exists";
571 setupLogTable(newLogTable);
575 if (filterDialog.
ui->editComponent->text().length())
581 if (filterDialog.
ui->edtTag->text().length())
584 filterDialog.
ui->edtTag->text().toStdString());
587 if (filterDialog.
ui->edtMessage->text().length())
590 filterDialog.
ui->edtMessage->text().toStdString());
593 if (filterDialog.
ui->edtFile->text().length())
596 filterDialog.
ui->edtFile->text().toStdString());
599 if (filterDialog.
ui->edtFunction->text().length())
602 filterDialog.
ui->edtFunction->text().toStdString());
605 if (filterDialog.
ui->cbVerbosity->currentIndex() != -1)
609 QString::number(filterDialog.
ui->cbVerbosity->currentIndex()).toStdString());
612 QTreeWidgetItem* item =
new QTreeWidgetItem();
613 item->setText(0, filterName);
617 filterMap[filterName] = newLogTable;
618 ui.lvFilters->insertTopLevelItem(
ui.lvFilters->invisibleRootItem()->childCount(), item);
620 ui.splitter->addWidget(newLogTable);
630 if (filterMap.find(filterId) == filterMap.end())
646 const QString value = QString::fromStdString(activeFilter.value);
648 switch (activeFilter.column)
651 filterDialog.
ui->editComponent->setText(value);
654 filterDialog.
ui->edtTag->setText(value);
657 filterDialog.
ui->cbVerbosity->setCurrentIndex(value.toInt());
660 filterDialog.
ui->edtMessage->setText(value);
663 filterDialog.
ui->edtFile->setText(value);
666 filterDialog.
ui->edtFunction->setText(value);
674 if (!filterDialog.exec())
678 item->setText(0, filterDialog.
ui->edtFilterName->text());
680 logTable->getModel()->resetFilters();
682 if (filterDialog.
ui->editComponent->text().length())
685 filterDialog.
ui->editComponent->text().toStdString());
688 if (filterDialog.
ui->edtTag->text().length())
691 filterDialog.
ui->edtTag->text().toStdString());
694 if (filterDialog.
ui->edtMessage->text().length())
697 filterDialog.
ui->edtMessage->text().toStdString());
700 if (filterDialog.
ui->edtFile->text().length())
703 filterDialog.
ui->edtFile->text().toStdString());
706 if (filterDialog.
ui->edtFunction->text().length())
709 filterDialog.
ui->edtFunction->text().toStdString());
712 if (filterDialog.
ui->cbVerbosity->currentIndex() != -1)
716 QString::number(filterDialog.
ui->cbVerbosity->currentIndex()).toStdString());
719 logTable->getModel()->reapplyAllFilters();
726 if (
ui.lvFilters->invisibleRootItem()->childCount() == 1)
731 auto item =
ui.lvFilters->currentItem();
743 std::map<QString, LogTable*>::iterator it =
746 if (it == filterMap.end())
750 QList<QTreeWidgetItem*> itemsToDelete, iterationList({item});
751 while (!iterationList.isEmpty())
753 auto curItem = iterationList.front();
754 itemsToDelete << curItem;
755 iterationList.pop_front();
756 iterationList << curItem->takeChildren();
758 ui.lvFilters->setCurrentItem(
ui.lvFilters->invisibleRootItem()->child(0));
759 for (
auto& item : itemsToDelete)
761 auto parent = item->parent();
764 parent->removeChild(item);
768 ui.lvFilters->invisibleRootItem()->removeChild(item);
770 std::map<QString, LogTable*>::iterator it =
774 if (it == filterMap.end())
796 item->setFont(0, font);
798 if (filterMap.find(filterId) == filterMap.end())
819 ui.edtLiveFilter->setText(
logTable->getLiveFilterStr());
820 ui.edtLiveSearch->setText(
logTable->getModel()->getCurrentSearchStr());
827 QList<QTreeWidgetItem*> itemsToUpdate, iterationList({
ui.lvFilters->invisibleRootItem()});
828 while (!iterationList.isEmpty())
830 auto curItem = iterationList.front();
831 itemsToUpdate << curItem;
832 iterationList.pop_front();
833 for (
int i = 0; i < curItem->childCount(); ++i)
835 iterationList << curItem->child(i);
839 for (
auto item : itemsToUpdate)
842 auto filterIt = filterMap.find(filterId);
843 if (filterIt == filterMap.end())
848 LogTable* curLogtable = filterIt->second;
858 item->setFont(0, font);
860 item->setBackgroundColor(0,
ui.lvFilters->palette().base().color());
865 item->setFont(0, font);
871 item->setBackgroundColor(0, QColor(216, 120, 50));
875 item->setBackgroundColor(0, QColor(255, 90, 80));
879 item->setBackgroundColor(0, QColor(255, 60, 50));
883 item->setBackgroundColor(0,
ui.lvFilters->palette().base().color());
887 item->setText(0, newContent);
888 item->setToolTip(0, newContent);
895 throw LocalException() <<
"Not yet implemented";
901 ui.lvLabels->clear();
902 for (
const LogMarker& marker : markerRegistry.markers())
905 QPixmap swatch(12, 12);
906 swatch.fill(marker.color);
909 IceUtil::Time time = IceUtil::Time::microSeconds(marker.snapshot.time);
910 std::string timeStr = time.toDateTime();
911 const auto spacePos = timeStr.find(
' ');
912 if (spacePos != std::string::npos)
914 timeStr = timeStr.substr(spacePos + 1);
917 QString what = QString::fromStdString(marker.snapshot.what);
918 const int newlinePos = what.indexOf(
'\n');
921 what.truncate(newlinePos);
923 constexpr int maxWhatLength = 60;
924 if (what.length() > maxWhatLength)
926 what.truncate(maxWhatLength);
930 const QString text = QString::fromStdString(timeStr) +
" " +
931 QString::fromStdString(marker.snapshot.who) +
" " + what;
933 QListWidgetItem* item =
new QListWidgetItem(QIcon(swatch), text,
ui.lvLabels);
934 item->setData(Qt::UserRole,
static_cast<qulonglong
>(marker.id));
935 item->setToolTip(text);
948 for (
const LogMarker& marker : markerRegistry.markers())
956 const int row =
logTable->getModel()->rowForMessage(marker.snapshot);
959 const QModelIndex
index =
logTable->getModel()->index(row, 0);
960 logTable->scrollTo(
index, QAbstractItemView::PositionAtCenter);
971 QListWidgetItem* item =
ui.lvLabels->itemAt(pos);
979 QAction* recolorAction = menu.addAction(tr(
"Change color..."));
980 QAction* removeAction = menu.addAction(tr(
"Remove label"));
981 QAction* chosen = menu.exec(
ui.lvLabels->viewport()->mapToGlobal(pos));
983 if (chosen == removeAction)
985 markerRegistry.remove(
id);
987 else if (chosen == recolorAction)
990 for (
const LogMarker& marker : markerRegistry.markers())
994 initial = marker.color;
999 QColorDialog::getColor(initial,
getWidget(), tr(
"Choose label color"));
1000 if (color.isValid())
1002 markerRegistry.recolor(
id, color);
1010 QListWidgetItem* item =
ui.lvLabels->currentItem();
1016 markerRegistry.remove(
id);
1023 if (
getState() >= eManagedIceObjectExiting)
1028 std::vector<LogMessage> pendingEntriesTemp;
1030 std::unique_lock lock(pendingEntriesMutex);
1031 pendingEntriesTemp.swap(pendingEntries);
1035 unsigned int size = pendingEntriesTemp.size();
1039 for (
unsigned int i = 0; i < size; i++)
1041 LogMessage& msg = pendingEntriesTemp[i];
1043 if (msg.what.find(
'\033') != std::string::npos)
1045 msg.what = boost::regex_replace(msg.what, re,
"");
1048 if (!
getWidget() ||
ui.cbVerbosityLevel->currentIndex() > msg.type)
1056 bool autoScroll =
false;
1058 if (
logTable->verticalScrollBar()->value() ==
logTable->verticalScrollBar()->maximum())
1063 std::map<QString, LogTable*>::iterator it = filterMap.begin();
1065 for (; it != filterMap.end(); ++it)
1075 for (r = row; r < count; r++)
1080 bool autoScroll =
false;
1082 if (log->verticalScrollBar()->value() == log->verticalScrollBar()->maximum())
1090 log->scrollToBottom();
1102 expireOrphanedMarkers();
1112 ui.edtLiveSearch->show();
1113 ui.btnPreviousItem->show();
1114 ui.btnNextItem->show();
1115 ui.edtLiveFilter->hide();
1119 ui.edtLiveSearch->hide();
1120 ui.btnPreviousItem->hide();
1121 ui.btnNextItem->hide();
1122 ui.edtLiveFilter->show();
1132 if (!
logTable->selectNextSearchResult(
false))
1135 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
1146 if (!
logTable->selectNextSearchResult(
true))
1149 "Could not find '" +
logTable->getModel()->getCurrentSearchStr() +
1160 if (parent != customToolbar->parent())
1162 customToolbar->setParent(parent);
1165 return customToolbar;
1168 customToolbar =
new QToolBar(parent);
1169 customToolbar->setIconSize(QSize(16, 16));
1170 customToolbar->addAction(
1173 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
const LogMarker * markerForMessage(const LogMessage &message) const
Marker for a message identical to message, or nullptr.
const LogMarker * add(const LogMessage &snapshot)
Pin the given message.
std::vector< LogMarker > markers() const
Ordered (insertion-order) view of all markers.
void remove(LogMarkerId id)
Remove the marker with the given id (no-op if unknown).
static std::string levelToString(MessageTypeT type)
void setMarkerRegistry(LogMarkerRegistry *registry)
Attach the shared pin registry.
void addFilter(const std::string &columnName, const std::string &filter, MatchMode mode=MatchMode::Contains)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
int addEntries(const std::vector< LogMessage > &entryList, const QString &filterStr)
MessageType getMaxNewLogLevelType()
void liveFilterRow(const QString &filterStr, int row)
LogTableModel * getModel()
QString getCurrentLiveFilter() const
void removeLabelRequested(const LogMessage &message)
Emitted from the context menu to remove the pin on the given line.
void addLabelRequested(const LogMessage &message)
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
const LogSender::manipulator flush
A single pinned log line: a snapshot of the message plus the color assigned to it.