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
13// QtMsgType is not severity-ordered (QtInfoMsg=4 > QtCriticalMsg=2).
14static int severityRank(int qtMsgType)
15{
16 switch (qtMsgType) {
17 case QtDebugMsg: return 0;
18 case QtInfoMsg: return 1;
19 case QtWarningMsg: return 2;
20 case QtCriticalMsg: return 3;
21 case QtFatalMsg: return 4;
22 default: return 2;
23 }
24}
25
26QLoggingCategory::CategoryFilter QGCLoggingCategoryManager::s_previousFilter = nullptr;
27
29
30// Shared with QGCLoggingCategory.cc — registrations that land before the manager exists
31// are buffered here, then replayed from the manager ctor. Owned as a heap QStringList*
32// rather than a QStringList value so we can delete+null it exactly once, avoiding
33// static-destructor ordering hazards.
35{
36 static QMutex m;
37 return m;
38}
39
41{
42 static QStringList* p = new QStringList;
43 return p;
44}
45
46QGCLoggingCategoryManager* QGCLoggingCategoryManager::instance()
47{
48 return s_managerInstance;
49}
50
51void QGCLoggingCategoryManager::init()
52{
53 if (!s_managerInstance) {
55 }
56}
57
58QGCLoggingCategoryManager* QGCLoggingCategoryManager::create(QQmlEngine* qmlEngine, QJSEngine* jsEngine)
59{
60 Q_UNUSED(qmlEngine);
61 Q_UNUSED(jsEngine);
62 init();
63 QJSEngine::setObjectOwnership(s_managerInstance, QJSEngine::CppOwnership);
64 return s_managerInstance;
65}
66
67QGCLoggingCategoryManager::QGCLoggingCategoryManager() : QObject()
68{
69 s_managerInstance = this;
70
71 _treeModel = new LoggingCategoryTreeModel(this);
72 _flatModel = new LoggingCategoryFlatModel(this);
73
74 _filteredFlatModel.setSourceModel(_flatModel);
75 _filteredFlatModel.setFilterRole(static_cast<int>(LoggingCategoryFlatModel::Roles::FullNameRole));
76 _filteredFlatModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
77
78 QSettings settings;
79 settings.beginGroup(kFilterRulesSettingsGroup);
80 for (const QString& key : settings.childKeys()) {
81 const QVariant val = settings.value(key);
82 if (val.typeId() == QMetaType::Bool) {
83 // Backward compat: true → Debug, false → Warning
84 _categoryLevels.insert(key, val.toBool() ? QtDebugMsg : QtWarningMsg);
85 } else {
86 _categoryLevels.insert(key, val.toInt());
87 }
88 }
89
90 // Replay categories that registered before the manager was constructed
91 {
92 QMutexLocker locker(&qgcLoggingEarlyMutex());
94 for (const QString& cat : std::as_const(*qgcLoggingEarlyPending())) {
95 registerCategory(cat);
96 }
98 qgcLoggingEarlyPending() = nullptr;
99 }
100 }
101}
102
103void QGCLoggingCategoryManager::registerCategory(const QString& fullCategory)
104{
105 const QStringList segments = fullCategory.split(QLatin1Char('.'), Qt::SkipEmptyParts);
106 if (segments.isEmpty()) {
107 return;
108 }
109
110 const QString shortName = segments.last();
111
112 int level;
113 {
114 QReadLocker locker(&_filterLock);
115 level = _resolvedLevel(fullCategory);
116 }
117 auto* categoryItem = new QGCLoggingCategoryItem(shortName, fullCategory, level, this);
118 _flatModel->insertSorted(categoryItem);
119 _treeModel->insertCategory(segments, fullCategory, categoryItem);
120}
121
122void QGCLoggingCategoryManager::setCategoryLevel(const QString& fullCategoryName, int qtMsgLevel)
123{
124 qCDebug(QGCLoggingCategoryRegisterLog) << "Set category level" << fullCategoryName << qtMsgLevel;
125
126 const bool isDefault = severityRank(qtMsgLevel) >= severityRank(kDefaultLevel);
127 {
128 QWriteLocker locker(&_filterLock);
129 if (isDefault) {
130 _categoryLevels.remove(fullCategoryName);
131 } else {
132 _categoryLevels.insert(fullCategoryName, qtMsgLevel);
133 }
134 }
135
136 QSettings settings;
137 settings.beginGroup(kFilterRulesSettingsGroup);
138 if (isDefault) {
139 settings.remove(fullCategoryName);
140 } else {
141 settings.setValue(fullCategoryName, qtMsgLevel);
142 }
143
144 QLoggingCategory::installFilter(_categoryFilter);
145}
146
147void QGCLoggingCategoryManager::setCategoryEnabled(const QString& fullCategoryName, bool enable)
148{
149 setCategoryLevel(fullCategoryName, enable ? QtDebugMsg : kDefaultLevel);
150}
151
152bool QGCLoggingCategoryManager::isCategoryEnabled(const QString& fullCategoryName) const
153{
154 QReadLocker locker(&_filterLock);
155 return severityRank(_resolvedLevel(fullCategoryName)) < severityRank(kDefaultLevel);
156}
157
158int QGCLoggingCategoryManager::categoryLevel(const QString& fullCategoryName) const
159{
160 QReadLocker locker(&_filterLock);
161 return _resolvedLevel(fullCategoryName);
162}
163
164void QGCLoggingCategoryManager::installFilter(const QString& commandLineLoggingOptions)
165{
166 if (!commandLineLoggingOptions.isEmpty()) {
167 const QStringList categoryList = commandLineLoggingOptions.split(',', Qt::SkipEmptyParts);
168
169 QWriteLocker locker(&_filterLock);
170 if (!categoryList.isEmpty() && categoryList.first() == QStringLiteral("full")) {
171 _commandLineFullLogging = true;
172 } else {
173 for (const QString& category : categoryList) {
174 _commandLineCategories.insert(category.trimmed());
175 }
176 }
177 }
178
179 {
180 QReadLocker locker(&_filterLock);
181 for (int i = 0; i < _flatModel->count(); ++i) {
182 auto* item = _flatModel->at(i);
183 item->setLogLevelFromManager(_resolvedLevel(item->fullCategory));
184 }
185 }
186
187 s_previousFilter = QLoggingCategory::installFilter(_categoryFilter);
188
189 qCDebug(QGCLoggingCategoryRegisterLog) << "Category filter installed";
190}
191
192int QGCLoggingCategoryManager::_resolvedLevel(const QString& fullCategoryName) const
193{
194 if (_commandLineFullLogging) {
195 return QtDebugMsg;
196 }
197
198 const QString normalized =
199 fullCategoryName.endsWith('.') ? fullCategoryName.left(fullCategoryName.size() - 1) : fullCategoryName;
200
201 for (const QString& cmdCat : std::as_const(_commandLineCategories)) {
202 if (normalized == cmdCat || normalized.startsWith(cmdCat + '.')) {
203 return QtDebugMsg;
204 }
205 }
206
207 // Check exact match first
208 auto it = _categoryLevels.constFind(normalized);
209 if (it != _categoryLevels.constEnd()) {
210 return it.value();
211 }
212
213 // Check prefix matches (parent categories ending with '.')
214 for (auto pit = _categoryLevels.constBegin(); pit != _categoryLevels.constEnd(); ++pit) {
215 if (pit.key().endsWith('.')) {
216 const QString prefix = pit.key().left(pit.key().size() - 1);
217 if (normalized == prefix || normalized.startsWith(prefix + '.')) {
218 return pit.value();
219 }
220 }
221 }
222
223 return kDefaultLevel;
224}
225
226void QGCLoggingCategoryManager::_categoryFilter(QLoggingCategory* category)
227{
228 if (s_previousFilter) {
229 s_previousFilter(category);
230 }
231
232 const QString categoryName = QString::fromLatin1(category->categoryName());
233
234 if (categoryName.startsWith(QLatin1String("qt."))) {
235 if (categoryName.startsWith(QLatin1String("qt.qml.connections"))) {
236 category->setEnabled(QtDebugMsg, false);
237 category->setEnabled(QtWarningMsg, false);
238 }
239 return;
240 }
241
242 if (categoryName == QLatin1String("default")) {
243 return;
244 }
245
246 auto* manager = instance();
247
248 QReadLocker locker(&manager->_filterLock);
249 const int rank = severityRank(manager->_resolvedLevel(categoryName));
250
251 category->setEnabled(QtDebugMsg, rank <= 0);
252 category->setEnabled(QtInfoMsg, rank <= 1);
253 category->setEnabled(QtWarningMsg, rank <= 2);
254 category->setEnabled(QtCriticalMsg, rank <= 3);
255}
256
257void QGCLoggingCategoryManager::disableAllCategories()
258{
259 qCDebug(QGCLoggingCategoryRegisterLog) << "Disabling all categories";
260
261 {
262 QWriteLocker locker(&_filterLock);
263 _categoryLevels.clear();
264 }
265
266 QSettings settings;
267 settings.beginGroup(kFilterRulesSettingsGroup);
268 settings.remove(QString());
269
270 for (int i = 0; i < _flatModel->count(); ++i) {
271 _flatModel->at(i)->setLogLevelFromManager(kDefaultLevel);
272 }
273
274 QLoggingCategory::installFilter(_categoryFilter);
275}
276
277void QGCLoggingCategoryManager::setFilterText(const QString& text)
278{
279 _filteredFlatModel.setFilterFixedString(text);
280}
281
282/*===========================================================================*/
283// QGCLoggingCategoryItem definitions live here — they call back into the Manager,
284// and moving them avoids pulling QGCLoggingCategoryManager.h into LoggingCategoryModel.h.
285
286QGCLoggingCategoryItem::QGCLoggingCategoryItem(const QString& shortCategory_, const QString& fullCategory_,
287 int logLevel_, QObject* parent)
288 : QObject(parent), shortCategory(shortCategory_), fullCategory(fullCategory_), _logLevel(logLevel_)
289{
290 connect(this, &QGCLoggingCategoryItem::logLevelChanged, this, [this]() {
291 if (!_updatingFromManager) {
292 QGCLoggingCategoryManager::instance()->setCategoryLevel(fullCategory, _logLevel);
293 }
294 });
295}
296
298{
299 setLogLevel(enabled ? QtDebugMsg : QtWarningMsg);
300}
301
303{
304 if (level != _logLevel) {
305 const bool wasEnabled = enabled();
306 _logLevel = level;
307 emit logLevelChanged();
308 if (wasEnabled != enabled()) {
309 emit enabledChanged();
310 }
311 if (!_updatingFromManager) {
312 QGCLoggingCategoryManager::instance()->setCategoryLevel(fullCategory, level);
313 }
314 }
315}
316
318{
319 if (level != _logLevel) {
320 const bool wasEnabled = enabled();
321 _updatingFromManager = true;
322 _logLevel = level;
323 emit logLevelChanged();
324 if (wasEnabled != enabled()) {
325 emit enabledChanged();
326 }
327 _updatingFromManager = false;
328 }
329}
QMutex & qgcLoggingEarlyMutex()
static QGCLoggingCategoryManager * s_managerInstance
QStringList *& qgcLoggingEarlyPending()
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void insertSorted(QGCLoggingCategoryItem *item)
QGCLoggingCategoryItem * at(int i) const
void insertCategory(const QStringList &pathSegments, const QString &fullCategory, QGCLoggingCategoryItem *item)
QGCLoggingCategoryItem(const QString &shortCategory_, const QString &fullCategory_, int logLevel_, QObject *parent=nullptr)