Skip to content

Commit bb71c5c

Browse files
authored
Merge pull request #571 from LogExperts/refactorings_logfile_reader
Refactor LogFileReader for better testing and fix PluginRegistry initialization
2 parents 99f1ffb + b7e045a commit bb71c5c

84 files changed

Lines changed: 7686 additions & 1940 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CLAUDE.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
LogExpert is a Windows log file viewer and analyzer built with C# and Windows Forms. It's a GUI replacement for the Unix `tail` command with extensive features including tail mode, filtering, bookmarks, highlighting, and a plugin-based architecture for parsing custom log formats.
8+
9+
**Key Technologies:**
10+
- .NET 10.0 (Windows target framework)
11+
- Windows Forms for UI
12+
- Nuke Build System with MSBuild
13+
- NUnit for testing
14+
- Plugin-based architecture
15+
16+
## Build Commands
17+
18+
### Using Nuke Build (Recommended)
19+
20+
```powershell
21+
# Build the solution
22+
./build.ps1
23+
24+
# Clean and build
25+
./build.ps1 --target Clean Compile
26+
27+
# Run tests
28+
./build.ps1 --target Test
29+
30+
# Full release build with packages
31+
./build.ps1 --target Clean Pack CreateSetup --configuration Release
32+
```
33+
34+
### Using .NET CLI Directly
35+
36+
```bash
37+
# From src/ directory
38+
dotnet restore
39+
dotnet build --no-restore
40+
dotnet test --no-build --verbosity normal
41+
42+
# Run specific test project
43+
dotnet test src/LogExpert.Tests/LogExpert.Tests.csproj
44+
```
45+
46+
### Important Build Notes
47+
48+
- **Windows-only**: This project requires Windows and .NET 10.0.100 SDK (specified in [global.json](global.json))
49+
- **Cannot build on Linux/macOS**: Uses Windows Desktop SDK and Windows Forms
50+
- Nuke build automatically downloads the correct .NET SDK version if needed
51+
- Output directory: `bin/(Debug|Release)/`
52+
53+
## Architecture
54+
55+
### High-Level Structure
56+
57+
The codebase follows a modular architecture with clear separation of concerns:
58+
59+
```
60+
LogExpert/
61+
├── LogExpert/ # Main application entry point and UI orchestration
62+
├── LogExpert.Core/ # Core business logic, log reading, filtering
63+
├── LogExpert.UI/ # Windows Forms UI components and dialogs
64+
├── LogExpert.Resources/ # Localization resources
65+
├── LogExpert.Configuration/# Configuration management
66+
├── ColumnizerLib/ # Plugin interface definitions
67+
├── PluginRegistry/ # Plugin discovery and security
68+
└── Columnizers/ # Built-in columnizer plugins (CSV, JSON, Regex, etc.)
69+
```
70+
71+
### Key Architectural Components
72+
73+
#### 1. Single Instance Mode with IPC
74+
- Application uses a Mutex to ensure single instance per session
75+
- Named pipes (`LogExpertInstanceMutex{sessionId}`) for inter-process communication
76+
- Secondary instances send file paths to primary instance via JSON over named pipes
77+
- See [Program.cs](src/LogExpert/Program.cs) for implementation
78+
79+
#### 2. Plugin System
80+
- **Columnizers** (`ILogLineColumnizer`): Parse log lines into columns
81+
- **File System Plugins** (`IFileSystemPlugin`): Support non-local file sources (e.g., SFTP)
82+
- **Context Menu Plugins** (`IContextMenuEntry`): Add custom menu items
83+
- **Keyword Actions** (`IKeywordAction`): React to keywords in logs
84+
- Plugin discovery happens at startup via `PluginRegistry`
85+
- Plugins are loaded from `plugins/` and `pluginsx86/` directories
86+
- Security: Plugin hashes are verified against generated hashes (Release builds only)
87+
- See [PLUGIN_DEVELOPMENT_GUIDE.md](src/docs/PLUGIN_DEVELOPMENT_GUIDE.md) for details
88+
89+
#### 3. Log File Reading
90+
- Abstract base class: `PositionAwareStreamReaderBase`
91+
- Implementations: `PositionAwareStreamReaderSystem`, `PositionAwareStreamReaderLegacy`
92+
- Uses buffered streams for efficient reading of large files
93+
- Supports encoding detection (UTF-8, UTF-16, UTF-32 with BOM)
94+
- Position tracking for tail mode and seeking
95+
- See [src/LogExpert.Core/Classes/Log/](src/LogExpert.Core/Classes/Log/) for implementations
96+
97+
#### 4. Configuration Management
98+
- Centralized via `ConfigManager.Instance`
99+
- Initialized with application startup path and screen information
100+
- Supports import/export of settings
101+
- Persists user preferences, columnizer history, highlight masks, etc.
102+
- Configuration stored in application startup directory (portable mode)
103+
104+
#### 5. Windows Forms UI Architecture
105+
- MDI interface with tab support via `AbstractLogTabWindow`
106+
- Main window created in [LogTabWindow.cs](src/LogExpert.UI/Dialogs/LogTabWindow/)
107+
- Custom controls: `BufferedDataGridView`, `LogTabControl`, `DateTimeDragControl`
108+
- High DPI considerations: Avoid `AutoScaleMode` and `AutoScaleDimensions` on individual controls
109+
- Dark mode support via `Application.SetColorMode()`
110+
111+
### Critical Files and Their Purposes
112+
113+
- [Program.cs](src/LogExpert/Program.cs) - Application entry point, IPC setup, single instance handling
114+
- [AbstractLogTabWindow.cs](src/LogExpert.UI/Extensions/LogWindow/AbstractLogTabWindow.cs) - Main window factory and orchestration
115+
- [ILogLineColumnizer.cs](src/ColumnizerLib/ILogLineColumnizer.cs) - Core plugin interface for columnizers
116+
- [ColumnizerPicker.cs](src/LogExpert.Core/Classes/Columnizer/ColumnizerPicker.cs) - Automatic columnizer detection
117+
- [PluginRegistry.cs](src/PluginRegistry/) - Plugin discovery and security verification
118+
- [ConfigManager.cs](src/LogExpert.Configuration/) - Configuration persistence and management
119+
- [LogBuffer.cs](src/LogExpert.Core/Classes/Log/LogBuffer.cs) - In-memory log line caching
120+
121+
## Development Workflow
122+
123+
### Adding a New Columnizer Plugin
124+
125+
1. Create new project in `src/` following naming pattern `*Columnizer`
126+
2. Add project reference to `ColumnizerLib`
127+
3. Implement `ILogLineColumnizer` interface
128+
4. Add project to `src/LogExpert.sln`
129+
5. Create corresponding test project in `Tests/` folder
130+
6. Plugin will be auto-discovered at runtime from output directory
131+
132+
### Modifying Core Log Reading Logic
133+
134+
- Core reading classes are in [src/LogExpert.Core/Classes/Log/](src/LogExpert.Core/Classes/Log/)
135+
- Inherit from `PositionAwareStreamReaderBase` for custom stream readers
136+
- Key methods to implement: `ReadLine()`, `Position` property, `Seek()`
137+
- Always maintain position tracking for tail mode support
138+
139+
### Working with Windows Forms UI
140+
141+
- UI components in `LogExpert.UI` project
142+
- Follow existing High DPI patterns (no AutoScale on controls)
143+
- Test with both light and dark mode (see `SetDarkMode()` in Program.cs)
144+
- Use localization resources from `LogExpert.Resources` project
145+
- Windows Forms designer files: `*.designer.cs`
146+
147+
### Testing
148+
149+
- Unit tests use NUnit framework with Moq for mocking
150+
- Test projects follow naming pattern `*.Tests`
151+
- Test data stored in `TestData/` directories within test projects
152+
- Run all tests: `./build.ps1 --target Test`
153+
- Run specific test: `dotnet test src/LogExpert.Tests/LogExpert.Tests.csproj`
154+
155+
## Important Patterns and Conventions
156+
157+
### Code Style
158+
- Nullable reference types enabled (`<Nullable>enable</Nullable>`)
159+
- Comprehensive `.editorconfig` with 4000+ rules
160+
- ImplicitUsings enabled
161+
- Assembly signing enabled (Key.snk)
162+
163+
### Configuration Files
164+
- **Directory.Build.props** - Common MSBuild properties for all projects
165+
- **Directory.Packages.props** - Centralized NuGet package version management
166+
- **global.json** - .NET SDK version pinning (10.0.100)
167+
- **.editorconfig** - Code style and analysis rules
168+
169+
### Project Organization
170+
- Solution folders: "Columnizers", "Tests", "docs", "setup"
171+
- Test projects nested under "Tests" solution folder
172+
- Columnizer projects nested under "Columnizers" solution folder
173+
- Documentation in `src/docs/` included in solution
174+
175+
### Git Workflow
176+
- Default branch: `Development` (use for PRs)
177+
- Commit format: Include "Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"
178+
- GitHub Actions run on push to Development branch
179+
- AppVeyor for CI builds and artifact creation
180+
181+
## Plugin Security System
182+
183+
**Release builds only:**
184+
- After compilation, `PluginHashGenerator.Tool` generates SHA256 hashes of all plugins
185+
- Hashes stored in [PluginHashGenerator.Generated.cs](src/PluginRegistry/PluginHashGenerator.Generated.cs)
186+
- At runtime, `PluginRegistry` verifies plugin hashes before loading
187+
- Users can trust new plugins via UI dialog
188+
- Hash updates automated via GitHub Actions on successful builds
189+
- See [PLUGIN_HASH_MANAGEMENT.md](src/docs/PLUGIN_HASH_MANAGEMENT.md)
190+
191+
## Common Gotchas
192+
193+
1. **Cross-platform builds fail**: This is Windows-only. Don't attempt Linux/macOS builds.
194+
2. **SDK version mismatch**: Must use .NET 10.0.100 SDK (specified in global.json)
195+
3. **Plugin not loading**: Check output directory - plugins must be in `plugins/` or `pluginsx86/`
196+
4. **High DPI issues**: Never use AutoScaleMode on individual controls, only on forms
197+
5. **IPC failures**: Named pipes require proper Windows permissions and session isolation
198+
6. **Encoding detection**: BOM-less files default to encoding from EncodingOptions
199+
7. **Plugin hashes**: Only verified in Release builds; Debug builds skip verification
200+
201+
## Key Dependencies
202+
203+
- **NLog**: Logging framework
204+
- **Newtonsoft.Json**: JSON serialization (legacy, but widely used)
205+
- **CsvHelper**: CSV parsing in CsvColumnizer
206+
- **SSH.NET**: SFTP support in SftpFileSystem plugins
207+
- **DockPanelSuite**: Docking panel UI controls
208+
- **Moq/NUnit**: Testing frameworks
209+
210+
## References
211+
212+
- Main README: [README.md](README.md)
213+
- Plugin Development: [PLUGIN_DEVELOPMENT_GUIDE.md](src/docs/PLUGIN_DEVELOPMENT_GUIDE.md)
214+
- Plugin Hash System: [PLUGIN_HASH_MANAGEMENT.md](src/docs/PLUGIN_HASH_MANAGEMENT.md)
215+
- Performance Benchmarks: [BENCHMARK_SUMMARY.md](src/docs/performance/BENCHMARK_SUMMARY.md)
216+
- GitHub Wiki: https://github.com/LogExperts/LogExpert/wiki
217+
- Discord: https://discord.gg/SjxkuckRe9
218+
219+
# Update Rules File
220+
To update this file, ensure that all sections are kept current with the latest architectural decisions, build processes, and development workflows. Follow these guidelines:
221+
- Review and update build commands if there are changes in the build system.
222+
- Reflect any architectural changes in the "Architecture" section.
223+
- Keep development workflow steps accurate for new contributors.
224+
- Regularly verify links to other documentation files.
225+
- Maintain clarity and conciseness for ease of understanding by new developers.
226+
- Use consistent formatting throughout the document.
227+
- Add new sections as needed for significant changes in the project structure or processes.
228+
- Ensure all technical terms are explained or linked to relevant documentation.
229+
- Periodically review for outdated information and remove or update as necessary.
230+
- If told to not do something, ensure this is also added to the "Dont Do that" section.

.gitignore

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ x86/
3333
bld/
3434
[Bb]in/
3535
[Oo]bj/
36-
[Ll]og/
37-
[Ll]ogs/
36+
/[Ll]og/
37+
/[Ll]ogs/
3838

3939
# Visual Studio 2015/2017 cache/options directory
4040
.vs/
@@ -618,3 +618,102 @@ temp.txt
618618
/Data
619619
/.tmp
620620
/.nuke/temp/*.log
621+
622+
# roots docs folder should be empty
623+
/docs/*
624+
625+
# test project logs should be ignored, this is for rolling logfile tests
626+
/src/tools/LogRotator/logs/*
627+
628+
######## START JET BRAINS git ignore https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
629+
/src/.idea/*
630+
631+
# Covers JetBrains IDEs: IntelliJ, GoLand, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
632+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
633+
634+
# User-specific stuff
635+
.idea/**/workspace.xml
636+
.idea/**/tasks.xml
637+
.idea/**/usage.statistics.xml
638+
.idea/**/dictionaries
639+
.idea/**/shelf
640+
641+
# AWS User-specific
642+
.idea/**/aws.xml
643+
644+
# Generated files
645+
.idea/**/contentModel.xml
646+
647+
# Sensitive or high-churn files
648+
.idea/**/dataSources/
649+
.idea/**/dataSources.ids
650+
.idea/**/dataSources.local.xml
651+
.idea/**/sqlDataSources.xml
652+
.idea/**/dynamic.xml
653+
.idea/**/uiDesigner.xml
654+
.idea/**/dbnavigator.xml
655+
656+
# Gradle
657+
.idea/**/gradle.xml
658+
.idea/**/libraries
659+
660+
# Gradle and Maven with auto-import
661+
# When using Gradle or Maven with auto-import, you should exclude module files,
662+
# since they will be recreated, and may cause churn. Uncomment if using
663+
# auto-import.
664+
# .idea/artifacts
665+
# .idea/compiler.xml
666+
# .idea/jarRepositories.xml
667+
# .idea/modules.xml
668+
# .idea/*.iml
669+
# .idea/modules
670+
# *.iml
671+
# *.ipr
672+
673+
# CMake
674+
cmake-build-*/
675+
676+
# Mongo Explorer plugin
677+
.idea/**/mongoSettings.xml
678+
679+
# File-based project format
680+
*.iws
681+
682+
# IntelliJ
683+
out/
684+
685+
# mpeltonen/sbt-idea plugin
686+
.idea_modules/
687+
688+
# JIRA plugin
689+
atlassian-ide-plugin.xml
690+
691+
# Cursive Clojure plugin
692+
.idea/replstate.xml
693+
694+
# SonarLint plugin
695+
.idea/sonarlint/
696+
# see https://community.sonarsource.com/t/is-the-file-idea-idea-idea-sonarlint-xml-intended-to-be-under-source-control/121119
697+
.idea/sonarlint.xml
698+
699+
# Crashlytics plugin (for Android Studio and IntelliJ)
700+
com_crashlytics_export_strings.xml
701+
crashlytics.properties
702+
crashlytics-build.properties
703+
fabric.properties
704+
705+
# Editor-based HTTP Client
706+
.idea/httpRequests
707+
http-client.private.env.json
708+
709+
# Android studio 3.1+ serialized cache file
710+
.idea/caches/build_file_checksums.ser
711+
712+
# Apifox Helper cache
713+
.idea/.cache/.Apifox_Helper
714+
.idea/ApifoxUploaderProjectSetting.xml
715+
716+
# Github Copilot persisted session migrations, see: https://github.com/microsoft/copilot-intellij-feedback/issues/712#issuecomment-3322062215
717+
.idea/**/copilot.data.migration.*.xml
718+
719+
######## END JET BRAINS git ignore https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore

.vscode/settings.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"chat.tools.terminal.autoApprove": {
3+
"dotnet build": true,
4+
"dotnet test": true,
5+
"dotnet clean": true,
6+
"Out-Null": true,
7+
"ForEach-Object": true,
8+
"Set-Content": true,
9+
"Start-Process": true,
10+
"Test-Path": true,
11+
"type": true,
12+
"New-Item": true,
13+
"/^git stash; dotnet test src/LogExpert\\.Tests/LogExpert\\.Tests\\.csproj --filter \"TestShiftBuffers1\" --no-restore 2>&1 \\| Select-Object -Last 30$/": {
14+
"approve": true,
15+
"matchCommandLine": true
16+
},
17+
"git rev-parse": true
18+
}
19+
}

src/AutoColumnizer/AutoColumnizer.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,9 @@ public void PushValue (ILogLineMemoryColumnizerCallback callback, int column, st
6262
{
6363
}
6464

65+
public void PushValue (ILogLineMemoryColumnizerCallback callback, int column, string value, ReadOnlyMemory<char> oldValue)
66+
{
67+
}
68+
6569
#endregion ILogLineColumnizer implementation
6670
}

src/ColumnizerLib/ILogLineMemoryColumnizer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,17 @@ public interface ILogLineMemoryColumnizer
9494
/// <param name="oldValue">The previous value that was associated with the specified column before the update.</param>
9595
void PushValue (ILogLineMemoryColumnizerCallback callback, int column, string value, string oldValue);
9696

97+
// <summary>
98+
/// <param name="callback">
99+
/// The callback interface that receives the value update notification. Cannot be null.
100+
/// </param>
101+
/// <param name="column">The zero-based index of the column for which the value is being updated.</param>
102+
/// <param name="value">The new value to be associated with the specified column.</param>
103+
/// <param name="oldValue">
104+
/// The previous value <see cref="ReadOnlyMemory{T}"/> associated with the specified
105+
/// column before the update.
106+
/// </param>
107+
void PushValue (ILogLineMemoryColumnizerCallback callback, int column, string value, ReadOnlyMemory<char> oldValue);
108+
97109
#endregion
98110
}

0 commit comments

Comments
 (0)