QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCLoggingCategoryManager.cc
Go to the documentation of this file.
2
3#include <QtCore/QSettings>
4#include <QtCore/QStringList>
5#include <QtQml/QJSEngine>
6
9
10QGC_LOGGING_CATEGORY(QGCLoggingCategoryRegisterLog, "Utilities.QGCLoggingCategoryManager")
11
12QLoggingCategory::CategoryFilter QGCLoggingCategoryManager::s_previousFilter = nullptr;
13
15
20
27
28QGCLoggingCategoryManager* QGCLoggingCategoryManager::create(QQmlEngine* qmlEngine, QJSEngine* jsEngine)
29{
30 Q_UNUSED(qmlEngine);
31 Q_UNUSED(jsEngine);
32 init();
33 QJSEngine::setObjectOwnership(s_managerInstance, QJSEngine::CppOwnership);
34 return s_managerInstance;
35}
36
37QGCLoggingCategoryManager::QGCLoggingCategoryManager() : QObject()
38{
39 s_managerInstance = this;
40
41 _treeModel = new LoggingCategoryTreeModel(this);
42 _flatModel = new LoggingCategoryFlatModel(this);
43
44 _filteredFlatModel.setSourceModel(_flatModel);
45 _filteredFlatModel.setFilterRole(static_cast<int>(LoggingCategoryFlatModel::Roles::FullNameRole));
46 _filteredFlatModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
47
48 QSettings settings;
49 settings.beginGroup(kFilterRulesSettingsGroup);
50 for (const QString& key : settings.childKeys()) {
51 const QVariant val = settings.value(key);
52 if (val.toBool()) {
53 _enabledCategories.insert(key);
54 }
55 }
56
57 const auto categories =
58 qgcObserveLoggingCategories(this, [this](const QString& category) { registerCategory(category); });
59 for (const auto& category : categories)
60 registerCategory(category);
61}
62
63void QGCLoggingCategoryManager::registerCategory(const QString& fullCategory)
64{
65 const QStringList segments = fullCategory.split(QLatin1Char('.'), Qt::SkipEmptyParts);
66 if (segments.isEmpty()) {
67 return;
68 }
69
70 const QString shortName = segments.last();
71
72 bool enabled;
73 {
74 QReadLocker locker(&_filterLock);
75 enabled = _isCategoryEnabled(fullCategory);
76 }
77 auto* categoryItem = new QGCLoggingCategoryItem(shortName, fullCategory, enabled, this);
78 _flatModel->insertSorted(categoryItem);
79 _treeModel->insertCategory(segments, categoryItem);
80}
81
82void QGCLoggingCategoryManager::setCategoryEnabled(const QString& fullCategoryName, bool enable)
83{
84 qCDebug(QGCLoggingCategoryRegisterLog) << "Set category enabled" << fullCategoryName << enable;
85
86 {
87 QWriteLocker locker(&_filterLock);
88 if (enable) {
89 _enabledCategories.insert(fullCategoryName);
90 } else {
91 _enabledCategories.remove(fullCategoryName);
92 }
93 }
94
95 QSettings settings;
96 settings.beginGroup(kFilterRulesSettingsGroup);
97 if (enable) {
98 settings.setValue(fullCategoryName, true);
99 } else {
100 settings.remove(fullCategoryName);
101 }
102
103 QLoggingCategory::installFilter(_categoryFilter);
104
105 _refreshItemStates();
106
108}
109
110void QGCLoggingCategoryManager::_refreshItemStates()
111{
112 QList<std::pair<QGCLoggingCategoryItem*, bool>> updates;
113 QSet<QGCLoggingCategoryItem*> seen;
114 {
115 QReadLocker locker(&_filterLock);
116 for (int i = 0; i < _flatModel->count(); ++i) {
117 auto* item = _flatModel->at(i);
118 seen.insert(item);
119 updates.emplace_back(item, _isCategoryEnabled(item->fullCategory));
120 }
121 // Tree intermediate (group) nodes are not in the flat model; leaf nodes are.
122 // Only enqueue tree items that were not already covered above.
123 _treeModel->forEachItem([this, &updates, &seen](QGCLoggingCategoryItem* treeItem) {
124 if (!seen.contains(treeItem)) {
125 updates.emplace_back(treeItem, _isCategoryEnabled(treeItem->fullCategory));
126 }
127 });
128 }
129 for (auto& [item, enabled] : updates) {
130 item->setEnabledFromManager(enabled);
131 }
132}
133
134bool QGCLoggingCategoryManager::isCategoryEnabled(const QString& fullCategoryName) const
135{
136 QReadLocker locker(&_filterLock);
137 return _isCategoryEnabled(fullCategoryName);
138}
139
140bool QGCLoggingCategoryManager::_isCategoryEnabled(const QString& fullCategoryName) const
141{
142 if (_commandLineFullLogging) {
143 return true;
144 }
145
146 const QString normalized =
147 fullCategoryName.endsWith('.') ? fullCategoryName.left(fullCategoryName.size() - 1) : fullCategoryName;
148
149 for (const QString& cmdCat : std::as_const(_commandLineCategories)) {
150 if (normalized == cmdCat || normalized.startsWith(cmdCat + '.')) {
151 return true;
152 }
153 }
154
155 if (_enabledCategories.contains(fullCategoryName)) {
156 return true;
157 }
158
159 for (const QString& cat : std::as_const(_enabledCategories)) {
160 if (cat.endsWith('.')) {
161 const QString prefix = cat.left(cat.size() - 1);
162 if (normalized == prefix || normalized.startsWith(prefix + '.')) {
163 return true;
164 }
165 }
166 }
167
168 return false;
169}
170
171void QGCLoggingCategoryManager::installFilter(const QString& commandLineLoggingOptions)
172{
173 if (!commandLineLoggingOptions.isEmpty()) {
174 const QStringList categoryList = commandLineLoggingOptions.split(',', Qt::SkipEmptyParts);
175
176 QWriteLocker locker(&_filterLock);
177 if (!categoryList.isEmpty() && categoryList.first() == QStringLiteral("full")) {
178 _commandLineFullLogging = true;
179 } else {
180 for (const QString& category : categoryList) {
181 _commandLineCategories.insert(category.trimmed());
182 }
183 }
184 }
185
186 _refreshItemStates();
187
188 s_previousFilter = QLoggingCategory::installFilter(_categoryFilter);
189
190 qCDebug(QGCLoggingCategoryRegisterLog) << "Category filter installed";
191}
192
193void QGCLoggingCategoryManager::_categoryFilter(QLoggingCategory* category)
194{
195 if (s_previousFilter) {
196 s_previousFilter(category);
197 }
198
199 const QString categoryName = QString::fromLatin1(category->categoryName());
200
201 if (categoryName.startsWith(QLatin1String("qt."))) {
202 if (categoryName.startsWith(QLatin1String("qt.qml.connections"))) {
203 category->setEnabled(QtDebugMsg, false);
204 category->setEnabled(QtWarningMsg, false);
205 }
206 return;
207 }
208
209 // Leave "default" (uncategorized qDebug()) and "qml" (QML console.log/warn/error)
210 // to Qt's built-in filter, which enables debug messages in Debug builds by default.
211 // Without this, the QGC filter would suppress them at the default level (Warning).
212 if (categoryName == QLatin1String("default") || categoryName == QLatin1String("qml")) {
213 return;
214 }
215
216 auto* manager = instance();
217
218 QReadLocker locker(&manager->_filterLock);
219 const bool enabled = manager->_isCategoryEnabled(categoryName);
220
221 category->setEnabled(QtDebugMsg, enabled);
222 category->setEnabled(QtInfoMsg, enabled);
223}
224
226{
227 qCDebug(QGCLoggingCategoryRegisterLog) << "Disabling all categories";
228
229 {
230 QWriteLocker locker(&_filterLock);
231 _enabledCategories.clear();
232 }
233
234 QSettings settings;
235 settings.beginGroup(kFilterRulesSettingsGroup);
236 settings.remove(QString());
237
238 _refreshItemStates();
239
240 QLoggingCategory::installFilter(_categoryFilter);
241
243}
244
246{
247 QReadLocker locker(&_filterLock);
248 QStringList result;
249 result.reserve(_enabledCategories.size());
250 for (const QString& cat : _enabledCategories) {
251 result.append(cat.endsWith('.') ? cat + QLatin1Char('*') : cat);
252 }
253 result.sort();
254 return result;
255}
256
258{
259 _filteredFlatModel.setFilterFixedString(text);
260}
261
262/*===========================================================================*/
263// QGCLoggingCategoryItem definitions live here — they call back into the Manager,
264// and moving them avoids pulling QGCLoggingCategoryManager.h into LoggingCategoryModel.h.
265
266QGCLoggingCategoryItem::QGCLoggingCategoryItem(const QString& shortCategory_, const QString& fullCategory_,
267 bool enabled_, QObject* parent)
268 : QObject(parent), shortCategory(shortCategory_), fullCategory(fullCategory_), _enabled(enabled_)
269{
270 connect(this, &QGCLoggingCategoryItem::enabledChanged, this, [this]() {
271 if (!_updatingFromManager) {
273 }
274 });
275}
276
278{
279 if (enabled != _enabled) {
280 _enabled = enabled;
281 emit enabledChanged();
282 }
283}
284
286{
287 if (enabled != _enabled) {
288 _updatingFromManager = true;
289 _enabled = enabled;
290 emit enabledChanged();
291 _updatingFromManager = false;
292 }
293}
static QGCLoggingCategoryManager * s_managerInstance
QStringList qgcObserveLoggingCategories(QObject *context, std::function< void(const QString &)> observer)
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void insertSorted(QGCLoggingCategoryItem *item)
QGCLoggingCategoryItem * at(int i) const
void forEachItem(const std::function< void(QGCLoggingCategoryItem *)> &fn)
void insertCategory(const QStringList &pathSegments, QGCLoggingCategoryItem *item)
QGCLoggingCategoryItem(const QString &shortCategory_, const QString &fullCategory_, bool enabled_, QObject *parent=nullptr)
void registerCategory(const QString &category)
Q_INVOKABLE void setFilterText(const QString &text)
static QGCLoggingCategoryManager * create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
Q_INVOKABLE void setCategoryEnabled(const QString &fullCategoryName, bool enable)
Q_INVOKABLE bool isCategoryEnabled(const QString &fullCategoryName) const
static QGCLoggingCategoryManager * instance()
void installFilter(const QString &commandLineLoggingOptions=QString())