Repository navigation
Improvements - #13
Merged
Merged
Improvements#13
Conversation
Inventory of the public surface per namespace, proposed 2.0 layout (registration extensions in Microsoft.Extensions.Logging, core types in Tjidde.Logging), migration path and user migration guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds assets/icon.svg (source) and a 128x128 assets/icon.png, and packs icon.png as the NuGet PackageIcon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test project only targeted net10.0, so the library builds for net7.0, net8.0 and net9.0 (each with its own Microsoft.Extensions.* major) were never tested. - Multi-target the test project net7.0;net8.0;net9.0;net10.0 with per-TFM Microsoft.Extensions.* versions matching the library. - Install the 7.0, 8.0, 9.0 and 10.0 runtimes in ci.yml and publish.yml so dotnet test runs every TFM. - ci.yml: write a .trx per TFM and upload them as the test-results artifact (also when tests fail). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- .github/dependabot.yml: weekly NuGet updates (grouped: Microsoft.Extensions.* and test packages) and weekly GitHub Actions updates. Semver-major updates of Microsoft.Extensions.* are ignored because each TFM deliberately uses its own major. - Test packages: xunit 2.6.6 -> 2.9.3 (last v2). net8.0+: Microsoft.NET.Test.Sdk 17.8.0 -> 18.10.1 and xunit.runner.visualstudio 2.5.6 -> 4.0.0. net7.0 stays on the last versions that support it: Microsoft.NET.Test.Sdk 17.13.0 and xunit.runner.visualstudio 3.0.2; Dependabot ignores the ranges that would drop net7.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PackageReleaseNotes used to contain the whole CHANGELOG.md. A target that runs before GenerateNuspec now extracts only the "## [<PackageVersion>]" section (up to the next "## [" heading) and appends a link to the full changelog. If the section is missing, only the link is used and the build does not fail. Works with -p:Version=... as passed by CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LogMetrics used the custom level (LogLevel)10, which also reached other providers; the Microsoft console formatters throw ArgumentOutOfRangeException for it, so LogMetrics crashed as soon as AddConsole() was registered. LogMetrics now logs at LogLevel.Information with MetricsEventId (Id 10000, Name "Metrics"). TjiddeLogger shows entries with that event name, and the old (LogLevel)10, as METRICS in magenta and never drops them through its own minimum level. MetricsLoggerExtensions.Metrics is marked [Obsolete]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TjiddeLogger.Log could throw from JSON serialization (cycles, unsupported types, throwing getters), from ToString() or enumeration of logged values, from a custom IExceptionFormatter, from the formatter delegate and from the OpenTelemetry export. - Serialize each JSON property value separately with IgnoreCycles and MaxDepth 32; on failure fall back to ToString() or "[unserializable: TypeName]". - Fall back to the message template when rendering the message throws, and make MessageTemplateRenderer.FormatValue safe. - Fall back to exception type and message when the exception formatter fails. - Skip scopes that throw and ignore OpenTelemetry export failures. - As a last resort, write a minimal line with level, category, message and a note that rendering failed (without unmasked values). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The provider now subscribes to IOptionsMonitor.OnChange and keeps options, masker, exception formatter and fallback ActivitySource in one immutable TjiddeLoggerConfiguration that is swapped atomically. Loggers read the current configuration once per call, so they pick up changes right away and never see a mixed set. The old ActivitySource is disposed on change, and the change registration on provider dispose. AddTjiddeLogger(IConfiguration/IConfigurationSection) registers an IOptionsChangeTokenSource so a configuration reload rebinds the options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Loggers already get their masker from the shared configuration object, so all categories use one SensitiveDataMasker and one set of regexes. Make that masker safe for concurrent use while MaskedKeysContext changes: the runtime key set is built from a copy, compared by content unless the accessor hands out immutable snapshots, and published with CompareExchange so a concurrent rebuild never overwrites a newer set. This also fixes custom accessors that return one mutable collection. IsEnabled now caches the resolved minimum level per logger together with the configuration it came from, so it is recomputed only after an options change. Tests cover concurrent logging while keys are added and removed, the shared masker across loggers and options changes, and the mutable accessor case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TjiddeLogger used DateTime.Now. Timestamps now come from System.TimeProvider: the one registered in DI, or TimeProvider.System. TjiddeLoggerProvider gets a constructor overload that takes a TimeProvider; the existing constructor keeps working. AddTjiddeLogger registers the provider through a factory, so the container never has to choose between the constructors. net7.0 references Microsoft.Bcl.TimeProvider. New option UseUtcTimestamp (default false) writes UTC instead of local time; JSON @timestamp is ISO-8601 with the offset. A throwing TimeProvider falls back to the system clock, so logging still never throws. Tests use FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing, 8.x for net7.0/net8.0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Tjidde:LogLevel
TjiddeLoggerOptions.MinimumLevel and CategoryMinimumLevels duplicate the
standard Microsoft.Extensions.Logging filters (Logging:LogLevel and, through
[ProviderAlias("Tjidde")], Logging:Tjidde:LogLevel). Mark them [Obsolete]
with a pointer to Logging:Tjidde:LogLevel; they will be removed in 2.0. They
keep working and keep binding from configuration; the library suppresses
CS0618 only where it still applies them.
New tests pin down how both filters combine: the framework filters first,
then Tjidde's own filter, so the stricter level wins (also for metrics,
which only the framework filter can drop). Existing tests of the legacy
options suppress CS0618 locally.
README and the wikis recommend Logging:Tjidde:LogLevel with an
appsettings.json example. AI-INTEGRATION.md no longer claims that options
changes need a restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaskedKeysStore is a non-static, thread-safe IMaskedKeysAccessor that hands out immutable snapshots, so the masker detects changes by reference. UseIsolatedMaskedKeys() registers one store per host as the accessor. MaskedKeysContext stays the static default and now wraps a MaskedKeysStore. Docs recommend accessors and DI for testable code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entries can now go to extra destinations next to the console. ILogSink receives an immutable, masked TjiddeLogEntry (timestamp, level, event id, category, class, method, customer, message, formatted exception, rendered line, output format, IsMetrics). Sinks are registered with AddTjiddeSink, AddTjiddeInMemorySink or plain DI, called synchronously, and isolated: a throwing sink never breaks logging, the console or other sinks, and entries logged from inside a sink are not dispatched again. TjiddeLoggerOptions.WriteToConsole (default true) turns the console off, also through an options reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Blazor, Avalonia and terminal samples drop their own in-memory logger providers and read the built-in InMemoryLogSink. Metrics entries are logged with LogMetrics and recognized by their event id instead of the obsolete Metrics level. The terminal sample turns the console off so console output no longer draws over its UI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main's squash commit fe98912 has the same tree as improvements@dd7dcb0; conflicts were resolved by taking the improvements version, so the merged tree is identical to improvements@325a711. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
benchmarks/Tjidde.Logging.Benchmarks (net10.0, not packable, in the solution) measures one log call through TjiddeLogger with the console off: a simple message, structured properties, a sensitive property, active scopes, a no-op sink and the IsEnabled == false path, each for Text and Json output. benchmarks/README.md explains how to run them and records the baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaskMessage ran two regular expressions per sensitive key over every message, property and scope value. Every match contains its key, so a key that does not occur in the text (OrdinalIgnoreCase) is skipped; on .NET 9+ one SearchValues<string> scan rules out all keys at once. The check is only used for ASCII text and ASCII keys, where it agrees with the expressions' IgnoreCase matching; anything else still runs them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Enable package validation against the published 1.0.1 package (PackageValidationBaselineVersion 1.0.1). dotnet pack reports no breaking changes; strict mode shows only additions, identical for net7.0, net8.0, net9.0 and net10.0. - Add Microsoft.CodeAnalysis.PublicApiAnalyzers 5.6.0 (PrivateAssets=all). PublicAPI.Shipped.txt holds exactly the 1.0.1 API (generated from the 1.0.1 sources, which strict package validation confirms match the nuget.org package); PublicAPI.Unshipped.txt holds everything added since. The build has no RS0016/RS0017 warnings. - Wikis (en/nl): after a release move Unshipped to Shipped and raise PackageValidationBaselineVersion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Create the masked-key list only when a property is masked, and size the property list from the state's count. - Read the scopes in one pass for the MethodName scope and the scope texts, with a static callback and a per-thread reader instead of two closures and LINQ. A scope whose enumeration throws while reading the method name still fails the entry, as before. - Write the text line straight into one reused StringBuilder per thread instead of building date, time, method and customer strings first. The output is unchanged. A new test logs from a scope value's ToString to check that a nested entry does not disturb the outer one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md # docs/AI-INTEGRATION.md # docs/wiki-en.md # docs/wiki-nl.md # src/Tjidde.Logging/Extensions/TjiddeLoggingBuilderExtensions.cs # src/Tjidde.Logging/Logging/TjiddeLogger.cs # src/Tjidde.Logging/Logging/TjiddeLoggerProvider.cs # src/Tjidde.Logging/Masking/SensitiveDataMasker.cs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Updates Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent and Avalonia.Fonts.Inter together. Avalonia 12 compiles bindings by default, so MainWindow declares x:DataType="vm:MainViewModel"; the obsolete TextBox.Watermark is replaced by PlaceholderText. The new Avalonia brings Tmds.DBus.Protocol 0.94.1, which resolves the NU1903 warning for 0.21.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BuildJsonLogLine built a dictionary for the entry and one for the properties and serialized them polymorphically. It now writes the same fields with a Utf8JsonWriter into a reused per-thread buffer, with the serializer's encoder and maximum depth. Repeated property names keep their first position and last value, as the dictionary did. String (without surrogates), int, long and bool values are written directly instead of through SerializeToElement; other values still are. If the writer throws, the previous serializer path runs, so the output and the failure behavior stay the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hmarks - samples and benchmarks (net10.0): 10.0.5 -> 10.0.12 - tests: net10.0 group 10.0.5 -> 10.0.12, net9.0 group 9.0.14 -> 9.0.20; net7.0 and net8.0 stay on the library's minimum versions The library keeps its deliberately low per-TFM minimum versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dependabot takes the highest Microsoft.Extensions.* version in a file as the current one and rewrites every occurrence, which collapsed the per-TFM ItemGroups in the library and tests. Microsoft.Extensions.* is now ignored for /src/* and /tests/* (updated by hand per TFM); /samples/* and /benchmarks/* get grouped microsoft-extensions and avalonia updates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.