40 sameLine(
const LogMessage& a,
const LogMessage& b)
42 return a.time == b.time &&
a.who == b.who &&
a.what == b.what;
49 columnFromName(
const std::string& name)
89 formatTime(Ice::Long timeMicroSeconds)
91 IceUtil::Time time = IceUtil::Time::microSeconds(timeMicroSeconds);
92 std::string timeStr = time.toDateTime();
93 const auto spacePos = timeStr.find(
' ');
94 if (spacePos != std::string::npos)
96 timeStr = timeStr.substr(spacePos);
98 return QString::fromStdString(timeStr);
104 buildDisplayMessage(
const LogMessage& entry,
int& lineCount)
106 QString whatStr = entry.backtrace.empty()
107 ? QString::fromStdString(entry.what)
108 : QString::fromStdString(entry.what +
"\nBacktrace:\n...");
110 constexpr int maxLinesToShow = 50;
113 for (
int i = 0; i < whatStr.length(); i++)
115 if (whatStr.at(i) ==
'\n')
119 if (lines == maxLinesToShow)
125 if (lines >= maxLinesToShow)
127 whatStr.truncate(pos);
131 lineCount = whatStr.count(
'\n') + 1;
139 maxNewLogLevelType = eUNDEFINED;
145 return logEntries.size();
157 const int& row =
index.row();
158 const int& column =
index.column();
164 std::unique_lock lock(logEntriesMutex);
165 if (row >= (
signed int)logEntries.size() || row < 0)
167 return QString(
"n/A");
170 const LogMessage& entry = logEntries[row].message;
176 if (role == Qt::ToolTipRole)
183 if (!entry.backtrace.empty())
185 whatStr = QString::fromStdString(entry.what +
"\nBacktrace:\n" +
190 whatStr = QString::fromStdString(entry.what);
199 case Qt::DisplayRole:
200 case Qt::ToolTipRole:
202 std::unique_lock lock(logEntriesMutex);
205 if (row >= (
signed int)logEntries.size() || row < 0)
207 return QString(
"n/A");
210 const LogEntry& logEntry = logEntries[row];
211 const LogMessage& entry = logEntry.message;
217 return logEntry.timeString;
222 return QString::fromStdString(entry.who);
227 return QString::fromStdString(entry.tag);
232 if (entry.type != eUNDEFINED)
234 return QString::fromStdString(
245 if (role == Qt::ToolTipRole)
249 return logEntry.displayMessage;
254 if (role == Qt::ToolTipRole)
256 return QString::fromStdString(
257 "Double click to open file in editor: " + entry.file +
":" +
258 QString::number(entry.line).toStdString());
260 else if (!entry.file.empty())
262 return QString::fromStdString(
263 entry.file +
":" + QString::number(entry.line).toStdString());
273 return QString::fromStdString(entry.function);
278 return QString::fromStdString(entry.group);
286 case Qt::DecorationRole:
290 return QIcon(
":icons/document-open-4.ico");
300 std::unique_lock lock(logEntriesMutex);
301 if (row >= 0 && row < (
signed int)logEntries.size() && logEntries[row].injected)
304 font.setItalic(
true);
310 case Qt::BackgroundColorRole:
315 std::unique_lock lock(logEntriesMutex);
316 if (row >= 0 && row < (
signed int)logEntries.size() &&
317 logEntries[row].markerColor.isValid())
319 return logEntries[row].markerColor;
325 if (row >= (
signed int)logEntries.size() || row < 0)
330 const LogMessage& entry = logEntries[row].message;
335 return QColor(200, 200, 250);
338 return QColor(50, 255, 50);
341 return QColor(216, 88, 0);
344 return QColor(255, 64, 64);
347 return QColor(176, 0, 0);
355 if (activeSearchStr.length() == 0)
361 std::unique_lock lock(logEntriesMutex);
364 if (row >= (
signed int)logEntries.size() || row < 0)
369 if (logEntries[row].matchesSearch)
371 return QColor(255, 244, 127);
400 if (searchStr.length() == 0)
407 if (
data(createIndex(row, i), Qt::DisplayRole)
409 .contains(searchStr, Qt::CaseInsensitive))
421 if (QString(logMsg.who.c_str()).contains(searchStr, Qt::CaseInsensitive))
426 if (QString(logMsg.tag.c_str()).contains(searchStr, Qt::CaseInsensitive))
431 if (QString(logMsg.what.c_str()).contains(searchStr, Qt::CaseInsensitive))
436 if (QString(logMsg.backtrace.c_str()).contains(searchStr, Qt::CaseInsensitive))
441 if (QString(logMsg.file.c_str()).contains(searchStr, Qt::CaseInsensitive))
446 if (QString(logMsg.function.c_str()).contains(searchStr, Qt::CaseInsensitive))
451 if (QString::number(logMsg.line).contains(searchStr, Qt::CaseInsensitive))
462 activeSearchStr = searchStr;
467 std::unique_lock lock(logEntriesMutex);
468 const bool hasSearch = searchStr.length() > 0;
469 for (LogEntry& logEntry : logEntries)
471 logEntry.matchesSearch =
476 if (!logEntries.empty())
478 emit dataChanged(
index(0, 0),
index(logEntries.size() - 1, 6));
485 if (role == Qt::DisplayRole)
487 if (orientation == Qt::Horizontal)
520 else if (role == Qt::ToolTipRole)
522 if (orientation == Qt::Horizontal)
542 return QString::fromStdString(
544 std::string(
": Double click cell to open Qtcreator at that location"));
600 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
604 return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
613 QModelIndex leftTop =
index(logEntries.size() - newEntryCount, 0);
614 QModelIndex rightBottom =
index(logEntries.size() - 1, 6);
616 emit dataChanged(leftTop, rightBottom);
618 maxNewLogLevelType = eUNDEFINED;
624 beginInsertRows(QModelIndex(), row, row + count - 1);
625 int newEntries = row + count - logEntries.size();
627 for (
int i = 0; i < newEntries; i++)
629 logEntries.push_back(LogEntry());
638 const std::string& filter,
641 activeFilters.push_back(
LogFilter{columnFromName(columnName), filter, mode});
653 std::unique_lock lock(logEntriesMutex);
654 std::vector<LogEntry> kept;
655 kept.reserve(logEntries.size());
656 for (LogEntry& logEntry : logEntries)
660 kept.push_back(std::move(logEntry));
663 logEntries.swap(kept);
666 maxNewLogLevelType = eUNDEFINED;
677 activeFilters.clear();
680 LogTableModel::LogEntry
681 LogTableModel::buildLogEntry(
const LogMessage& message)
const
684 entry.message = message;
685 entry.timeString = formatTime(message.time);
686 entry.displayMessage = buildDisplayMessage(message, entry.lineCount);
687 entry.matchesSearch =
693 entry.markerColor = marker->color;
702 if (markerRegistry == registry)
708 disconnect(markerRegistry,
nullptr,
this,
nullptr);
710 markerRegistry = registry;
713 connect(markerRegistry,
726 std::vector<LogMarker> markers;
729 markers = markerRegistry->markers();
734 std::unique_lock lock(logEntriesMutex);
737 logEntries.erase(std::remove_if(logEntries.begin(),
739 [](
const LogEntry& e) { return e.injected; }),
743 for (LogEntry& entry : logEntries)
745 entry.markerColor = QColor();
748 if (sameLine(marker.snapshot, entry.message))
750 entry.markerColor = marker.color;
760 const bool present = std::any_of(
763 [&marker](
const LogEntry& e)
764 { return !e.injected && sameLine(marker.snapshot, e.message); });
770 LogEntry injected = buildLogEntry(marker.snapshot);
771 injected.injected =
true;
772 injected.markerColor = marker.color;
775 while (idx < logEntries.size() &&
776 logEntries[idx].message.time <= marker.snapshot.time)
780 logEntries.insert(logEntries.begin() + idx, std::move(injected));
789 std::unique_lock lock(logEntriesMutex);
790 for (std::size_t row = 0; row < logEntries.size(); ++row)
792 if (sameLine(logEntries[row].message, message))
794 return static_cast<int>(row);
803 std::unique_lock lock(logEntriesMutex);
804 for (
const LogEntry& entry : logEntries)
806 if (!entry.injected && sameLine(entry.message, message))
817 std::unique_lock lock(logEntriesMutex);
818 if (row < 0 || row >= (
signed int)logEntries.size())
822 return logEntries[row].markerColor.isValid();
840 LogEntry logEntry = buildLogEntry(entry);
847 beginInsertRows(QModelIndex(), insertRow, insertRow);
849 std::unique_lock lock(logEntriesMutex);
851 if (entry.type > maxNewLogLevelType)
853 maxNewLogLevelType = entry.type;
855 logEntries.push_back(std::move(logEntry));
869 if (entryList.size() == 0)
874 int entriesAdded = 0;
879 for (
const LogMessage& entry : entryList)
886 if (entriesAdded > 0)
912 constexpr double headroomFactor = 1.3;
914 std::size_t currentSize;
916 std::unique_lock lock(logEntriesMutex);
917 currentSize = logEntries.size();
919 if (currentSize <=
static_cast<std::size_t
>(maxEntries * headroomFactor))
924 const int removeCount =
static_cast<int>(currentSize - maxEntries);
927 beginRemoveRows(QModelIndex(), 0, removeCount - 1);
929 std::unique_lock lock(logEntriesMutex);
930 logEntries.erase(logEntries.begin(), logEntries.begin() + removeCount);
938 if (columnName.empty())
943 QString qcolumnName = QString::fromStdString(columnName);
945 for (
int i = 0; i <
columnCount(QModelIndex()); i++)
947 if (
headerData(i, Qt::Horizontal, Qt::DisplayRole).toString().compare(qcolumnName) == 0)
959 int size = (int)logEntries.size();
965 beginRemoveRows(QModelIndex(), 0, size - 1);
967 std::unique_lock lock(logEntriesMutex);
972 maxNewLogLevelType = eUNDEFINED;
979 return logEntries.at(row).message;
985 std::unique_lock lock(logEntriesMutex);
986 if (row < 0 || row >= (
signed int)logEntries.size())
990 return logEntries[row].lineCount;
996 const QString filterStr = QString::fromStdString(filter.
value);
1000 const auto matches = [&filter, &filterStr](
const std::string& field,
1001 Qt::CaseSensitivity cs)
1003 const QString value = QString::fromStdString(field);
1005 : value.contains(filterStr, cs);
1011 return matches(logMsg.group, Qt::CaseSensitive);
1013 return matches(logMsg.who, Qt::CaseInsensitive);
1015 return matches(logMsg.tag, Qt::CaseInsensitive);
1017 return !(logMsg.type < filterStr.toInt());
1019 return matches(logMsg.what, Qt::CaseInsensitive);
1021 return matches(logMsg.file, Qt::CaseInsensitive) ||
1022 filterStr.toInt() == logMsg.line;
1024 return matches(logMsg.function, Qt::CaseInsensitive);
1036 for (
unsigned int i = 0; i < activeFilters.size(); i++)
#define ARMARX_LOG_VERBOSITYSTR
#define ARMARX_LOG_COMPONENTSTR
#define ARMARX_LOG_FILESTR
#define ARMARX_LOG_TAGSTR
#define ARMARX_LOG_TIMESTR
#define ARMARX_LOG_MESSAGESTR
#define ARMARX_LOG_LOGGINGGROUPSTR
#define ARMARX_LOG_FUNCTIONSTR
Shared, session-only store of pinned ("labeled") log lines.
const LogMarker * markerForMessage(const LogMessage &message) const
Marker for a message identical to message, or nullptr.
static std::string levelToString(MessageTypeT type)
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void setMarkerRegistry(LogMarkerRegistry *registry)
Attach the shared pin registry.
void addFilter(const std::string &columnName, const std::string &filter, MatchMode mode=MatchMode::Contains)
bool applyFilters(const LogMessage &logMsg)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
LogTableModel(QObject *parent=0)
bool isRowMarked(int row) const
Whether the given display row is currently pinned (has a label color).
bool setData(const QModelIndex &index, const QVariant &value, int role) override
int addEntries(const std::vector< LogMessage > &entryList, const QString &filterStr)
bool applyFilter(const LogFilter &filter, const LogMessage &logMsg)
bool insertRows(int row, int count, const QModelIndex &parent) override
int lineCountAt(int row) const
Number of display lines of the message column for the given row (precomputed).
int rowForMessage(const LogMessage &message) const
Display row of the (first) line identical to message, or -1 if not shown.
void setMaxEntries(std::size_t maxEntries)
Upper bound on retained log entries.
const LogMessage & getLogEntry(size_t row) const
int columnCount(const QModelIndex &parent=QModelIndex()) const override
void enforceMaxEntries()
Trim the oldest entries once the buffer exceeds its bound (see setMaxEntries).
int getColumn(const std::string &columnName) const
bool addEntry(const LogMessage &entry, int *entriesAdded=NULL)
bool msgContainsString(const LogMessage &logMsg, QString searchStr) const
QVariant data(const QModelIndex &index, int role) const override
bool rowContainsString(int row, const QString &searchStr) const
void refreshMarkers()
Rebuild the set of injected pin rows and re-color native rows (full reset).
void search(const QString &searchStr)
bool containsNativeLine(const LogMessage &message) const
Whether this model holds message as a native (non-injected) row.
double a(double t, double a0, double j)
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
A single pinned log line: a snapshot of the message plus the color assigned to it.