2import QGroundControl.Controls
3import QGroundControl.FactControls
4import QGroundControl.Logging
10 QGCPalette { id: qgcPal; colorGroupEnabled: enabled }
12 readonly property var appSettings: QGroundControl.settingsManager.appSettings
13 property bool _exportFiltered: false
14 property int _selectedFormatIndex: 0
15 readonly property var exportFormats: [
16 { label: qsTr("Text (.txt)"), filters: [qsTr("Text files (*.txt)"), qsTr("All Files (*)")] },
17 { label: qsTr("JSON (.json)"), filters: [qsTr("JSON files (*.json)"), qsTr("All Files (*)")] },
18 { label: qsTr("CSV (.csv)"), filters: [qsTr("CSV files (*.csv)"), qsTr("All Files (*)")] },
19 { label: qsTr("JSONL (.jsonl)"), filters: [qsTr("JSON Lines files (*.jsonl)"), qsTr("All Files (*)")] }
21 readonly property var sink: LogManager.remoteSink
24 title: qsTr("Logging Settings")
27 spacing: ScreenTools.defaultFontPixelHeight / 2
28 width: maxContentAvailableWidth
30 // ── Disk Logging ─────────────────────────────────────────
32 Layout.fillWidth: true
33 heading: qsTr("Disk Logging")
36 Layout.fillWidth: true
37 checked: LogManager.diskLoggingEnabled
38 text: qsTr("Enable disk logging")
40 onClicked: LogManager.diskLoggingEnabled = checked
44 Layout.fillWidth: true
45 color: qgcPal.colorGrey
46 text: qsTr("Log files are saved to: %1").arg(appSettings.logSavePath)
47 visible: LogManager.diskLoggingEnabled
48 wrapMode: Text.WordWrap
52 Layout.fillWidth: true
53 spacing: ScreenTools.defaultFontPixelWidth
54 visible: LogManager.diskLoggingEnabled
57 text: qsTr("Flush on level:")
61 Layout.fillWidth: true
65 readonly property var _levelNames: [qsTr("Off"), qsTr("Warning"), qsTr("Critical")]
66 readonly property var _levelValues: [-1, LogEntry.Warning, LogEntry.Critical]
68 currentIndex: _levelValues.indexOf(LogManager.flushOnLevel)
72 onActivated: index => {
73 LogManager.flushOnLevel = _levelValues[index];
79 // ── Remote Logging ───────────────────────────────────────
81 Layout.fillWidth: true
82 heading: qsTr("Remote Logging")
85 Layout.fillWidth: true
86 fact: appSettings.remoteLoggingEnabled
87 text: qsTr("Enable remote logging")
91 Layout.fillWidth: true
92 spacing: ScreenTools.defaultFontPixelWidth
93 visible: appSettings.remoteLoggingEnabled.rawValue
100 Layout.fillWidth: true
101 fact: appSettings.remoteLoggingHost
109 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 8
110 fact: appSettings.remoteLoggingPort
115 Layout.fillWidth: true
116 spacing: ScreenTools.defaultFontPixelWidth
117 visible: appSettings.remoteLoggingEnabled.rawValue
120 text: qsTr("Protocol:")
124 Layout.fillWidth: true
128 currentIndex: appSettings.remoteLoggingProtocol.rawValue
129 model: [qsTr("UDP"), qsTr("TCP"), qsTr("Auto Fallback")]
132 onActivated: index => {
133 appSettings.remoteLoggingProtocol.rawValue = index;
139 Layout.fillWidth: true
140 color: qgcPal.colorGrey
141 text: qsTr("UDP is fire-and-forget, TCP is reliable, Auto Fallback starts with UDP and switches to TCP on failures.")
142 visible: appSettings.remoteLoggingEnabled.rawValue
143 wrapMode: Text.WordWrap
147 Layout.fillWidth: true
148 spacing: ScreenTools.defaultFontPixelWidth
149 visible: appSettings.remoteLoggingEnabled.rawValue
152 text: qsTr("Vehicle ID:")
156 Layout.fillWidth: true
157 fact: appSettings.remoteLoggingVehicleId
158 placeholderText: qsTr("Optional identifier")
163 Layout.fillWidth: true
164 spacing: ScreenTools.defaultFontPixelWidth
165 visible: sink && sink.enabled
169 const kb = sink.bytesSent / 1024;
170 return kb < 1024 ? qsTr("%1 KB sent").arg(Math.round(kb)) : qsTr("%1 MB sent").arg((kb / 1024).toFixed(1));
177 onClicked: sink.resetBytesSent()
182 Layout.fillWidth: true
183 color: qgcPal.warningText
184 text: sink ? qsTr("Error: %1").arg(sink.lastError) : ""
185 visible: sink && sink.lastError !== ""
186 wrapMode: Text.WordWrap
190 // ── TLS Encryption ───────────────────────────────────────
191 SettingsGroupLayout {
194 property string _tlsCaPath: ""
195 property string _tlsCertPath: ""
196 property string _tlsKeyPath: ""
198 Layout.fillWidth: true
199 heading: qsTr("TLS Encryption")
200 visible: appSettings.remoteLoggingEnabled.rawValue && appSettings.remoteLoggingProtocol.rawValue !== LogRemoteSink.UDP
203 Layout.fillWidth: true
204 fact: appSettings.remoteLoggingTlsEnabled
205 text: qsTr("Enable TLS")
209 Layout.fillWidth: true
210 fact: appSettings.remoteLoggingTlsVerifyPeer
211 text: qsTr("Verify server certificate")
212 visible: appSettings.remoteLoggingTlsEnabled.rawValue
216 Layout.fillWidth: true
217 spacing: ScreenTools.defaultFontPixelWidth
218 visible: appSettings.remoteLoggingTlsEnabled.rawValue
221 text: qsTr("CA Certificate")
225 Layout.fillWidth: true
229 color: qgcPal.colorGrey
230 text: tlsGroup._tlsCaPath || qsTr("Not loaded")
236 onClicked: caCertDialog.openForLoad()
242 nameFilters: [qsTr("PEM files (*.pem)"), qsTr("All Files (*)")]
243 title: qsTr("Select CA Certificate")
245 onAcceptedForLoad: file => {
246 if (sink.loadTlsCaCertificates(file))
247 tlsGroup._tlsCaPath = file;
253 Layout.fillWidth: true
254 spacing: ScreenTools.defaultFontPixelWidth
255 visible: appSettings.remoteLoggingTlsEnabled.rawValue
258 text: qsTr("Client Certificate")
262 Layout.fillWidth: true
266 text: tlsGroup._tlsCertPath ? qsTr("Cert \u2713") : qsTr("Cert")
268 onClicked: clientCertDialog.openForLoad()
272 text: tlsGroup._tlsKeyPath ? qsTr("Key \u2713") : qsTr("Key")
274 onClicked: clientKeyDialog.openForLoad()
278 enabled: tlsGroup._tlsCertPath !== "" && tlsGroup._tlsKeyPath !== ""
281 onClicked: sink.loadTlsClientCertificate(tlsGroup._tlsCertPath, tlsGroup._tlsKeyPath)
287 nameFilters: [qsTr("PEM files (*.pem)"), qsTr("All Files (*)")]
288 title: qsTr("Select Client Certificate")
290 onAcceptedForLoad: file => {
291 tlsGroup._tlsCertPath = file;
298 nameFilters: [qsTr("PEM files (*.pem)"), qsTr("All Files (*)")]
299 title: qsTr("Select Client Key")
301 onAcceptedForLoad: file => {
302 tlsGroup._tlsKeyPath = file;
308 Layout.fillWidth: true
309 color: qgcPal.warningText
310 text: sink ? qsTr("TLS Error: %1").arg(sink.lastTlsError) : ""
311 visible: sink && sink.lastTlsError !== ""
312 wrapMode: Text.WordWrap
316 // ── Compression ──────────────────────────────────────────
317 SettingsGroupLayout {
318 Layout.fillWidth: true
319 heading: qsTr("Compression")
320 visible: appSettings.remoteLoggingEnabled.rawValue
323 Layout.fillWidth: true
324 fact: appSettings.remoteLoggingCompressionEnabled
325 text: qsTr("Enable compression")
329 Layout.fillWidth: true
330 spacing: ScreenTools.defaultFontPixelWidth
331 visible: appSettings.remoteLoggingCompressionEnabled.rawValue
338 Layout.fillWidth: true
342 text: Math.round(compressionSlider.value)
346 id: compressionSlider
348 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 15
352 value: appSettings.remoteLoggingCompressionLevel.rawValue
354 onMoved: appSettings.remoteLoggingCompressionLevel.rawValue = value
359 Layout.fillWidth: true
360 color: qgcPal.colorGrey
361 text: qsTr("Compresses log data using zlib. Level 1 is fastest, level 9 provides best compression.")
362 visible: appSettings.remoteLoggingCompressionEnabled.rawValue
363 wrapMode: Text.WordWrap
367 // ── Console Buffer ───────────────────────────────────────
368 SettingsGroupLayout {
369 Layout.fillWidth: true
370 heading: qsTr("Console Buffer")
373 Layout.fillWidth: true
374 spacing: ScreenTools.defaultFontPixelWidth
377 text: qsTr("Max entries")
381 Layout.fillWidth: true
385 text: Math.round(bufferSlider.value).toLocaleString()
391 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 15
395 value: LogManager.model.maxEntries
397 onMoved: LogManager.model.maxEntries = value
402 Layout.fillWidth: true
403 color: qgcPal.colorGrey
404 text: qsTr("Number of log entries kept in memory for the console view. Higher values use more RAM.")
405 wrapMode: Text.WordWrap
409 // ── Log History ──────────────────────────────────────────
410 SettingsGroupLayout {
411 Layout.fillWidth: true
412 heading: qsTr("Log History")
413 visible: LogManager.logStore.isOpen
416 Layout.fillWidth: true
417 color: qgcPal.colorGrey
418 text: qsTr("Session: %1 (%2 entries)").arg(LogManager.logStore.sessionId).arg(LogManager.logStore.entryCount)
422 Layout.fillWidth: true
423 spacing: ScreenTools.defaultFontPixelWidth
426 text: qsTr("Browse session:")
432 Layout.fillWidth: true
433 model: LogManager.historyModel.availableSessions
436 Component.onCompleted: {
438 currentIndex = count - 1;
440 onActivated: LogManager.historyModel.sessionFilter = currentText
443 const prev = currentText;
444 const idx = find(prev);
445 currentIndex = idx >= 0 ? idx : count - 1;
452 Layout.fillWidth: true
453 color: qgcPal.colorGrey
454 text: qsTr("%1 entries in selected session").arg(LogManager.historyModel.totalResults)
455 visible: sessionCombo.currentText !== ""
459 Layout.fillWidth: true
460 spacing: ScreenTools.defaultFontPixelWidth
461 visible: sessionCombo.currentText !== ""
464 text: qsTr("Export Session")
466 onClicked: historyExportDialog.openForSave()
470 text: qsTr("Delete Session")
472 onClicked: deleteConfirmDialogFactory.open()
476 QGCPopupDialogFactory {
477 id: deleteConfirmDialogFactory
479 dialogComponent: Component {
480 QGCSimpleMessageDialog {
481 title: qsTr("Confirm Delete")
482 text: qsTr("Delete session \"%1\" and all its log entries?").arg(sessionCombo.currentText)
483 buttons: Dialog.Yes | Dialog.No
485 onAccepted: LogManager.logStore.deleteSession(sessionCombo.currentText)
491 id: historyExportDialog
493 folder: QGroundControl.settingsManager.appSettings.logSavePath
494 nameFilters: exportFormats[_selectedFormatIndex].filters
495 title: qsTr("Export session log")
497 onAcceptedForSave: file => {
498 LogManager.logStore.exportSession(sessionCombo.currentText, file, _selectedFormatIndex);
503 // ── GStreamer ─────────────────────────────────────────────
504 SettingsGroupLayout {
505 Layout.fillWidth: true
506 heading: qsTr("GStreamer")
507 visible: appSettings.gstDebugLevel.userVisible
510 Layout.fillWidth: true
511 spacing: ScreenTools.defaultFontPixelWidth
514 text: qsTr("Debug level:")
518 Layout.fillWidth: true
522 fact: appSettings.gstDebugLevel
528 // ── Export ────────────────────────────────────────────────
529 SettingsGroupLayout {
530 Layout.fillWidth: true
531 heading: qsTr("Export")
534 Layout.fillWidth: true
535 spacing: ScreenTools.defaultFontPixelWidth
538 text: qsTr("Format:")
542 Layout.fillWidth: true
546 id: exportFormatCombo
548 currentIndex: _selectedFormatIndex
549 model: exportFormats.map(f => f.label)
551 onActivated: _selectedFormatIndex = currentIndex
556 Layout.fillWidth: true
557 spacing: ScreenTools.defaultFontPixelWidth
560 text: qsTr("Save Log")
563 _exportFiltered = false;
564 saveFileDialog.openForSave();
569 text: qsTr("Save Filtered")
570 visible: LogManager.model.filterLevel > LogEntry.Debug || LogManager.model.filterCategory !== "" || LogManager.model.filterText !== ""
573 _exportFiltered = true;
574 saveFileDialog.openForSave();
580 // ── Actions ──────────────────────────────────────────────
581 SettingsGroupLayout {
582 Layout.fillWidth: true
583 heading: qsTr("Actions")
586 Layout.fillWidth: true
587 spacing: ScreenTools.defaultFontPixelWidth
590 text: qsTr("Clear Log")
592 onClicked: LogManager.model.clear()
596 enabled: LogManager.diskLoggingEnabled
597 text: qsTr("Flush to Disk")
599 onClicked: LogManager.flush()
608 folder: QGroundControl.settingsManager.appSettings.logSavePath
609 nameFilters: exportFormats[_selectedFormatIndex].filters
610 title: _exportFiltered ? qsTr("Save filtered log") : qsTr("Save app log")
612 onAcceptedForSave: file => {
614 LogManager.writeFilteredMessages(file, _selectedFormatIndex);
616 LogManager.writeMessages(file, _selectedFormatIndex);