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