Skip to content

Commit 9afed57

Browse files
authored
Issue #751: Add logs to bug report (#753)
1 parent efcf132 commit 9afed57

18 files changed

Lines changed: 849 additions & 7 deletions
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# Capturing wxLog Lines for Bug Reports
2+
3+
## Overview
4+
This document outlines how to capture the last N log lines from wxLog into bug reports using wxWidgets' logging system.
5+
6+
## How wxLog Works
7+
wxWidgets provides a flexible logging system with several key components:
8+
9+
1. **wxLog**: Base logging class - the foundation of the system
10+
2. **wxLogTextCtrl**: Logs to a wxTextCtrl
11+
3. **wxLogStderr**: Logs to stderr (default in non-GUI apps)
12+
4. **wxLogGui**: Shows messages in message boxes (default in GUI apps)
13+
5. **wxLogChain**: Chains multiple loggers together
14+
15+
The key insight: **You can install a custom wxLog instance that captures log messages while also chaining to the default behavior**.
16+
17+
## Architecture: Custom wxLog Target
18+
19+
### High-level Strategy
20+
21+
1. **Create a custom `CircularLogBuffer` class** that:
22+
- Inherits from `wxLog` (or implements a custom log target)
23+
- Maintains a circular buffer of the last N log messages
24+
- Stores: timestamp, log level, message
25+
- Can be queried to retrieve all captured messages
26+
27+
2. **Install it during app initialization** in `CalChartApp::OnInit()`:
28+
- Create the circular buffer with a configurable size (e.g., 100 lines)
29+
- Use `wxLog::SetActiveTarget()` or chain it with the existing logger
30+
- Keep a global reference for bug report access
31+
32+
3. **Query it when building bug reports** in `BugReportDialog`:
33+
- Add a method to `DiagnosticInfo` to include log lines
34+
- Format as a markdown code block in the bug report
35+
36+
## Implementation Approach
37+
38+
### 1. Core Logger Class (Platform-independent)
39+
40+
**File**: `core/CircularLogBuffer.hpp` / `core/CircularLogBuffer.cpp`
41+
42+
```cpp
43+
namespace CalChart {
44+
45+
struct LogMessage {
46+
std::string timestamp; // ISO 8601 format
47+
std::string level; // "Info", "Warning", "Error", etc.
48+
std::string message;
49+
};
50+
51+
class CircularLogBuffer {
52+
public:
53+
explicit CircularLogBuffer(size_t capacity = 100);
54+
55+
// Add a message to the buffer
56+
void AddMessage(std::string level, std::string message);
57+
58+
// Get all messages in chronological order
59+
[[nodiscard]] std::vector<LogMessage> GetMessages() const;
60+
61+
// Clear the buffer
62+
void Clear();
63+
64+
private:
65+
std::vector<LogMessage> buffer_;
66+
size_t capacity_;
67+
size_t current_index_;
68+
mutable std::mutex lock_;
69+
};
70+
71+
} // namespace CalChart
72+
```
73+
74+
**Why platform-independent?**
75+
- The log buffer logic is pure C++
76+
- Doesn't depend on wxWidgets or UI framework
77+
- Can be tested without GUI
78+
- Makes it reusable
79+
80+
### 2. wxWidgets Integration
81+
82+
**File**: `src/CalChartLogTarget.h` / `src/CalChartLogTarget.cpp`
83+
84+
This creates a bridge between wxWidgets' logging and our circular buffer:
85+
86+
```cpp
87+
class CalChartLogTarget : public wxLog {
88+
public:
89+
explicit CalChartLogTarget(CalChart::CircularLogBuffer& buffer);
90+
91+
protected:
92+
void DoLogText(const wxLogRecordInfo& info, const wxString& msg) override;
93+
94+
private:
95+
CalChart::CircularLogBuffer& buffer_;
96+
};
97+
```
98+
99+
### 3. Integration Points
100+
101+
**In CalChartApp** (`src/CalChartApp.h/cpp`):
102+
- Create a global `CircularLogBuffer` instance
103+
- Install `CalChartLogTarget` during `OnInit()`
104+
- Provide accessor method: `GetLogBuffer()`
105+
- Use `wxLog::SetActiveTarget()` or chain loggers
106+
107+
**In DiagnosticInfo** (`src/DiagnosticInfo.cpp`):
108+
- Query the log buffer when collecting diagnostic info
109+
- Add methods:
110+
- `CollectLogLines()` - gets recent log messages
111+
- Format them for the bug report
112+
113+
**In BugReportDialog** (`src/BugReportDialog.cpp`):
114+
- Include log lines in the diagnostic info section
115+
- Display them in a collapsible section or code block
116+
- Allow users to see what happened before the report
117+
118+
## Key Design Decisions
119+
120+
### Thread Safety
121+
- Use `std::mutex` in the circular buffer for thread-safe access
122+
- wxLog operations are usually on the main thread, but it's good practice
123+
124+
### Circular Buffer Implementation
125+
Option 1: Pre-allocated vector with index wrapping (simple, efficient)
126+
Option 2: Using `std::deque` with size checking (simpler code)
127+
128+
```cpp
129+
// Simple circular approach - pre-allocated
130+
void CircularLogBuffer::AddMessage(std::string level, std::string message) {
131+
std::lock_guard<std::mutex> lock(lock_);
132+
133+
if (current_index_ >= capacity_) {
134+
current_index_ = 0; // Wrap around
135+
}
136+
137+
if (buffer_.size() < capacity_) {
138+
buffer_.push_back({timestamp, level, message});
139+
} else {
140+
buffer_[current_index_] = {timestamp, level, message};
141+
}
142+
current_index_++;
143+
}
144+
```
145+
146+
### Log Level Mapping
147+
wxLog provides different log levels:
148+
- `wxLOG_FatalError` → "Fatal Error"
149+
- `wxLOG_Error` → "Error"
150+
- `wxLOG_Warning` → "Warning"
151+
- `wxLOG_Message` → "Info"
152+
- `wxLOG_Debug` → "Debug"
153+
- `wxLOG_Trace` → "Trace"
154+
155+
You can configure which levels to capture (e.g., skip debug/trace in Release builds).
156+
157+
## Integration with Existing Bug Report System
158+
159+
The existing structure already has:
160+
- `CalChart::DiagnosticInfo` - stores diagnostic data
161+
- `CalChart::DiagnosticInfo::toString()` - formats as markdown
162+
- `BugReportDialog` - collects and files reports
163+
- `BugReport` struct - holds all report data
164+
165+
### Minimal Changes:
166+
1. Add `log_lines` field to `CalChart::DiagnosticInfo`:
167+
```cpp
168+
struct DiagnosticInfo {
169+
// ... existing fields ...
170+
std::vector<CalChart::LogMessage> recent_logs;
171+
};
172+
```
173+
174+
2. Update `toString()` to include logs in markdown:
175+
```markdown
176+
## Recent Log Messages
177+
\`\`\`
178+
[timestamp] [level] message
179+
[timestamp] [level] message
180+
\`\`\`
181+
```
182+
183+
3. In `src/DiagnosticInfo.cpp`, populate logs when collecting info:
184+
```cpp
185+
auto wxCalChart::DiagnosticInfo::CollectDiagnosticInfo(CalChartDoc const* doc)
186+
{
187+
auto info = CalChart::DiagnosticInfo::Create();
188+
// ... existing code ...
189+
info.recent_logs = wxCalChart::GetGlobalApp().GetLogBuffer().GetMessages();
190+
return info;
191+
}
192+
```
193+
194+
## Configuration Options
195+
196+
Consider these as CMake or runtime options:
197+
- `CALCHART_LOG_BUFFER_SIZE` - number of lines to keep (default: 100)
198+
- `CALCHART_LOG_LEVELS_TO_CAPTURE` - which levels to capture (default: Warning, Error, FatalError)
199+
- `CALCHART_LOG_INCLUDE_IN_REPORTS` - whether to include logs (default: true)
200+
201+
## Privacy Considerations
202+
203+
**Important**: Log messages could contain sensitive information:
204+
- File paths (may reveal user directory structure)
205+
- User data embedded in parsed content
206+
- Performance metrics that could identify a user
207+
208+
### Recommendations:
209+
1. **User control**: Let users see what's being captured
210+
- Already done in `BugReportDialog` - shows diagnostic info before submit
211+
- Users can review and edit if needed
212+
213+
2. **Log levels**: Don't capture `wxLOG_Debug` / `wxLOG_Trace` in Release builds
214+
- These are typically more verbose
215+
216+
3. **Documentation**: Note in Help/Documentation that logs are captured
217+
- Similar to other diagnostic info (OS, version, etc.)
218+
219+
## Testing Strategy
220+
221+
### Unit Tests (`core/tests/CircularLogBufferTests.cpp`):
222+
- Test adding messages at capacity
223+
- Test wrapping behavior
224+
- Test formatting output
225+
- Test thread safety (manual verification)
226+
227+
### Integration Tests (`src/tests/DiagnosticInfoTests.cpp`):
228+
- Test log collection during app operation
229+
- Test formatting in bug report context
230+
- Test with real wxLog messages
231+
232+
### Manual Testing:
233+
1. Trigger some errors/warnings in CalChart
234+
2. Open bug report dialog
235+
3. Verify recent logs appear in diagnostic info
236+
4. Verify formatting looks correct in markdown
237+
238+
## Example Output
239+
240+
When formatted in a bug report:
241+
242+
```markdown
243+
## System Information
244+
...
245+
246+
## Recent Log Messages
247+
[2025-12-27 14:30:45] Warning: Could not load image from file.png
248+
[2025-12-27 14:30:46] Error: Parser encountered unexpected token
249+
[2025-12-27 14:30:47] Message: File saved successfully
250+
```
251+
252+
## Next Steps
253+
254+
1. ✅ Implement `CircularLogBuffer` in core - **COMPLETED**
255+
- Created `core/CircularLogBuffer.hpp` and `core/CircularLogBuffer.cpp`
256+
- Implements circular buffer with thread-safe operations
257+
- Supports timestamps, log levels, and formatting
258+
259+
2. ✅ Create `CalChartLogTarget` wxWidgets adapter - **COMPLETED**
260+
- Created `src/CalChartLogTarget.h` and `src/CalChartLogTarget.cpp`
261+
- Inherits from `wxLog` and bridges to `CircularLogBuffer`
262+
- Maps wxLog levels to string representations
263+
264+
3. ✅ Install in `CalChartApp::OnInit()` - **COMPLETED**
265+
- Modified `src/CalChartApp.h` to add log buffer, target, and chain members
266+
- Modified `src/CalChartApp.cpp` to initialize buffer and chain in `InitAppAsServer()`
267+
- Added `GetLogBuffer()` accessor method
268+
- Uses `wxLogChain` to properly chain loggers so messages flow through our capture AND the original system
269+
- Messages are captured but then forwarded to the original logger
270+
271+
4. ✅ Extend `DiagnosticInfo` to collect logs - **COMPLETED**
272+
- Added `recent_logs` field to `CalChart::DiagnosticInfo` struct
273+
- Updated `toString()` method to include log messages in markdown format
274+
- Logs are formatted as a code block with timestamp, level, and message
275+
276+
5. ✅ Collect logs in `wxCalChart::DiagnosticInfo` - **COMPLETED**
277+
- Modified `src/DiagnosticInfo.cpp::CollectDiagnosticInfo()`
278+
- Queries the global app's log buffer
279+
- Populates `info.recent_logs` with captured messages
280+
281+
6. ✅ Write unit tests - **COMPLETED**
282+
- Created `core/tests/CircularLogBufferTests.cpp`
283+
- Tests cover: adding/retrieving messages, circular wrapping, formatting, clearing, timestamps
284+
- Tests verify thread safety and chronological ordering
285+
286+
7. ✅ Update CMakeLists files - **COMPLETED**
287+
- Added `CircularLogBuffer.cpp/hpp` to `core/CMakeLists.txt`
288+
- Added `CalChartLogTarget.cpp/h` to `src/CMakeLists.txt`
289+
- Added `CircularLogBufferTests.cpp` to `core/tests/CMakeLists.txt`
290+
291+
8. ✅ Update documentation - **IN PROGRESS**
292+
- Marking completed steps in this document
293+
294+
## Testing the Implementation
295+
296+
To verify the implementation works:
297+
298+
1. Build the project:
299+
```bash
300+
cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug
301+
cmake --build build --config Debug
302+
```
303+
304+
2. Run unit tests:
305+
```bash
306+
ctest --test-dir build --output-on-failure
307+
```
308+
309+
3. Manual testing:
310+
- Launch CalChart
311+
- Trigger some errors/warnings (e.g., try to load a bad file)
312+
- Open the bug report dialog (Help → Report a Bug or Ctrl+Shift+B)
313+
- Verify that recent log messages appear in the diagnostic info
314+
- Check that the logs are formatted correctly with timestamps and levels
315+
316+
## Implementation Summary
317+
318+
The log capture system is now fully integrated and ready to use:
319+
320+
- **Auto-capture**: All wxLog messages are automatically captured without code changes
321+
- **Circular buffer**: The last 100 messages are retained (configurable)
322+
- **Thread-safe**: All operations use mutex protection
323+
- **Privacy-aware**: Users see logs before submitting, can review/edit
324+
- **Formatted output**: Logs appear as a markdown code block in bug reports
325+
- **Well-tested**: Unit tests verify core functionality
326+
327+
The system integrates seamlessly with the existing bug reporting infrastructure.
328+
329+
---
330+
331+
## References
332+
333+
- [wxLog Documentation](https://docs.wxwidgets.org/3.2/overview_log.html)
334+
- [wxLog::SetActiveTarget()](https://docs.wxwidgets.org/3.2/classwx_log.html#a59f3e77c25cec7dd98a5d9f937f00c65)
335+
- Existing code: `src/DiagnosticInfo.h/cpp`, `core/CalChartDiagnosticInfo.hpp/cpp`

LATEST_RELEASE_NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ Other changes:
2525
* [#732](../../issues/732) remove wxUI::Custom
2626
* [#735](../../issues/735) Update the CI to have the release or debug in the name
2727
* [#737](../../issues/737) Print the version of wxWidgets used
28+
* [#751](../../issues/751) Add logs to bug report
2829

core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ add_library(
4747
CalChartCoroutine.h
4848
CalChartGitHubIssueSubmitter.cpp
4949
CalChartGitHubIssueSubmitter.hpp
50+
CircularLogBuffer.cpp
51+
CircularLogBuffer.hpp
5052
CalChartDrawCommand.cpp
5153
CalChartDrawCommand.h
5254
CalChartDrawPrimatives.h

core/CalChartDiagnosticInfo.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "CalChartDiagnosticInfo.hpp"
2424
#include "CalChartShow.h"
2525
#include "CalChartShowMode.h"
26+
#include "CircularLogBuffer.hpp"
2627
#include "ccvers.h"
2728
#include <format>
2829

@@ -83,6 +84,12 @@ auto DiagnosticInfo::toString() const -> std::string
8384
}
8485
result += "\n";
8586

87+
// Include recent log messages if available
88+
if (!recent_logs.empty()) {
89+
result += "## Recent Log Messages\n";
90+
result += CalChart::FormatLogMessages(recent_logs) + "\n\n";
91+
}
92+
8693
if (!additional_info.empty()) {
8794
result += "## Additional Information\n";
8895
for (auto const& [key, value] : additional_info) {

core/CalChartDiagnosticInfo.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
along with this program. If not, see <http://www.gnu.org/licenses/>.
2222
*/
2323

24+
#include "CircularLogBuffer.hpp"
25+
#include <map>
2426
#include <nlohmann/json.hpp>
2527
#include <optional>
2628
#include <string>
29+
#include <vector>
2730

2831
namespace CalChart {
2932

@@ -49,6 +52,9 @@ struct DiagnosticInfo {
4952
std::string file_format_version;
5053
} show_info;
5154

55+
// Recent log messages
56+
std::vector<LogMessage> recent_logs;
57+
5258
// Generic key-value pairs for additional info
5359
std::map<std::string, std::string> additional_info;
5460

0 commit comments

Comments
 (0)