diff --git a/.gitignore b/.gitignore
index 2503474..3f2e2ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@
*.userosscache
*.sln.docstates
*.env
+n# Claude Code
+.claude/
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
diff --git a/Solution.slnx b/In-Memory Logger.slnx
similarity index 52%
rename from Solution.slnx
rename to In-Memory Logger.slnx
index 35f7f20..eaef030 100644
--- a/Solution.slnx
+++ b/In-Memory Logger.slnx
@@ -1,24 +1,31 @@
+
+
+
+
-
+
+
-
+
+
+
@@ -26,6 +33,10 @@
-
-
+
+
+
+
+
+
diff --git a/README.md b/README.md
index 682248c..a1da96b 100644
--- a/README.md
+++ b/README.md
@@ -1,181 +1,43 @@
# Wolfgang.Extensions.Logging.InMemoryLogger
-Implementations of ILogger and ILogger that write log events to an in-memory list. Useful for testing
+An implementation of `ILogger` from `Microsoft.Extensions.Logging` that writes log entries to an in-memory collection. Designed for use in unit and integration tests where you need to assert against logged messages.
-[](LICENSE)
-[](https://dotnet.microsoft.com/)
-[](https://github.com/Chris-Wolfgang/In-memory-Logger)
+## Features
----
+- Thread-safe in-memory log entry storage
+- Configurable minimum log level
+- Configurable initial capacity
+- Supports all .NET log levels (Trace through Critical)
+- Returns log entries as `IReadOnlyList>` for easy assertion
-## 📦 Installation
+## Installation
-```bash
+```shell
dotnet add package Wolfgang.Extensions.Logging.InMemoryLogger
```
-**NuGet Package:** Coming soon to NuGet.org
+## Usage
----
+```csharp
+var logger = new InMemoryLogger(LogLevel.Information);
-## 📄 License
+// Use the logger in your code under test
+var service = new MyService(logger);
+service.DoWork();
-This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
-
----
-
-## 📚 Documentation
-
-- **GitHub Repository:** [https://github.com/Chris-Wolfgang/In-memory-Logger](https://github.com/Chris-Wolfgang/In-memory-Logger)
-- **API Documentation:** https://Chris-Wolfgang.github.io/In-memory-Logger/
-- **Formatting Guide:** [README-FORMATTING.md](README-FORMATTING.md)
-- **Contributing Guide:** [CONTRIBUTING.md](CONTRIBUTING.md)
-
----
-
-## 🚀 Quick Start
-
-{{QUICK_START_EXAMPLE}}
-
----
-
-## ✨ Features
-
-{{FEATURES_TABLE}}
-
-**Examples:**
-{{FEATURE_EXAMPLES}}
-
----
-
-## 🎯 Target Frameworks
-
-| Framework | Versions |
-|-----------|----------|
-| .NET Framework | .NET 4.6.2, .NET 4.7.0, .NET 4.7.1, .NET 4.7.2, .NET 4.8, .NET 4.8.1 |
-| .NET Core | .NET Core 3.1 |
-| .NET | .NET 5.0, .NET 6.0, .NET 7.0, .NET 8.0, .NET 9.0, .NET 10.0 |
-
----
-
-## 🔍 Code Quality & Static Analysis
-
-This project enforces **strict code quality standards** through **7 specialized analyzers** and custom async-first rules:
-
-### Analyzers in Use
-
-1. **Microsoft.CodeAnalysis.NetAnalyzers** - Built-in .NET analyzers for correctness and performance
-2. **Roslynator.Analyzers** - Advanced refactoring and code quality rules
-3. **AsyncFixer** - Async/await best practices and anti-pattern detection
-4. **Microsoft.VisualStudio.Threading.Analyzers** - Thread safety and async patterns
-5. **Microsoft.CodeAnalysis.BannedApiAnalyzers** - Prevents usage of banned synchronous APIs
-6. **Meziantou.Analyzer** - Comprehensive code quality rules
-7. **SonarAnalyzer.CSharp** - Industry-standard code analysis
-
-### Async-First Enforcement
-
-This library uses **`BannedSymbols.txt`** to prohibit synchronous APIs and enforce async-first patterns:
-
-**Blocked APIs Include:**
-- ❌ `Task.Wait()`, `Task.Result` - Use `await` instead
-- ❌ `Thread.Sleep()` - Use `await Task.Delay()` instead
-- ❌ Synchronous file I/O (`File.ReadAllText`) - Use async versions
-- ❌ Synchronous stream operations - Use `ReadAsync()`, `WriteAsync()`
-- ❌ `Parallel.For/ForEach` - Use `Task.WhenAll()` or `Parallel.ForEachAsync()`
-- ❌ Obsolete APIs (`WebClient`, `BinaryFormatter`)
-
-**Why?** To ensure all code is **truly async** and **non-blocking** for optimal performance in async contexts.
-
----
-
-## 🛠️ Building from Source
-
-### Prerequisites
-- [.NET 8.0 SDK](https://dotnet.microsoft.com/download) or later
-- Optional: [PowerShell Core](https://github.com/PowerShell/PowerShell) for formatting scripts
-
-### Build Steps
-
-```bash
-# Clone the repository
-git clone https://github.com/Chris-Wolfgang/In-memory-Logger.git
-cd In-memory-Logger
-
-# Restore dependencies
-dotnet restore
-
-# Build the solution
-dotnet build --configuration Release
-
-# Run tests
-dotnet test --configuration Release
-
-# Run code formatting (PowerShell Core)
-pwsh ./format.ps1
-```
-
-### Code Formatting
-
-This project uses `.editorconfig` and `dotnet format`:
-
-```bash
-# Format code
-dotnet format
-
-# Verify formatting (as CI does)
-dotnet format --verify-no-changes
-```
-
-See [README-FORMATTING.md](README-FORMATTING.md) for detailed formatting guidelines.
-
-### Building Documentation
-
-This project uses [DocFX](https://dotnet.github.io/docfx/) to generate API documentation:
-
-```bash
-# Install DocFX (one-time setup)
-dotnet tool install -g docfx
-
-# Generate API metadata and build documentation
-cd docfx_project
-docfx metadata # Extract API metadata from source code
-docfx build # Build HTML documentation
-
-# Documentation is generated in the docs/ folder at the repository root
+// Assert against captured log entries
+Assert.Single(logger.LogEntries);
+Assert.Equal(LogLevel.Information, logger.LogEntries[0].LogLevel);
```
-The documentation is automatically built and deployed to GitHub Pages when changes are pushed to the `main` branch.
-
-**Local Preview:**
-```bash
-# Serve documentation locally (with live reload)
-cd docfx_project
-docfx build --serve
-
-# Open http://localhost:8080 in your browser
-```
-
-**Documentation Structure:**
-- `docfx_project/` - DocFX configuration and source files
-- `docs/` - Generated HTML documentation (published to GitHub Pages)
-- `docfx_project/index.md` - Main landing page content
-- `docfx_project/docs/` - Additional documentation articles
-- `docfx_project/api/` - Auto-generated API reference YAML files
-
----
-
-## 🤝 Contributing
-
-Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
-- Code quality standards
-- Build and test instructions
-- Pull request guidelines
-- Analyzer configuration details
-
----
-
+## Target Frameworks
-## 🙏 Acknowledgments
+- .NET Framework 4.6.2
+- .NET Standard 2.0
+- .NET Standard 2.1
+- .NET 8.0
+- .NET 10.0
-{{ACKNOWLEDGMENTS}}
+## License
+[MIT](LICENSE)
diff --git a/src/Wolfgang.Extensions.Logging.InMemoryLogger/InMemoryLogger.cs b/src/Wolfgang.Extensions.Logging.InMemoryLogger/InMemoryLogger.cs
new file mode 100644
index 0000000..ace0d22
--- /dev/null
+++ b/src/Wolfgang.Extensions.Logging.InMemoryLogger/InMemoryLogger.cs
@@ -0,0 +1,254 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace Wolfgang.Extensions.Logging.InMemoryLogger;
+
+///
+/// An implementation of that logs messages to an in-memory collection.
+///
+[SuppressMessage("Style", "MA0049:Type name should not match containing namespace",
+ Justification = "The package, namespace, and type intentionally share a name; the namespace is named after the project's primary type.")]
+public class InMemoryLogger : ILogger
+{
+ private readonly List> _logEntries;
+#if NET9_0_OR_GREATER
+ private readonly System.Threading.Lock _lock = new();
+#else
+ private readonly object _lock = new object();
+#endif
+ private readonly AsyncLocal _scopeStack = new AsyncLocal();
+
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The category name for messages produced by the logger.
+ /// The minimum log level to log.
+ /// The initial capacity of the log entries collection.
+ /// Thrown when is .
+ /// Thrown when capacity is less than 1.
+ public InMemoryLogger
+ (
+ string category,
+ LogLevel minLogLevel = LogLevel.Trace,
+ int capacity = 16
+ )
+ {
+ if (category == null)
+ {
+ throw new ArgumentNullException(nameof(category));
+ }
+
+ if (capacity < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), "Value cannot be less than 1");
+ }
+
+ Category = category;
+ MinimumLogLevel = minLogLevel;
+ _logEntries = new List>(capacity);
+ }
+
+
+
+ ///
+ /// Logs the specified data.
+ ///
+ /// The log level.
+ /// The event ID associated with the log entry.
+ /// The state associated with the log entry.
+ /// The exception associated with the log entry, if any.
+ /// A function to create a string message from the state and exception.
+ /// The type of the state object.
+ public void Log
+ (
+ LogLevel logLevel,
+ EventId eventId,
+ TState state,
+ Exception? exception,
+ Func formatter
+ )
+ {
+ if (!IsEnabled(logLevel))
+ {
+ return;
+ }
+
+ var entry = new LogEntry