@@ -26,33 +26,33 @@ almost always be doing.
2626 (238 of 244 files in ` src/log4net ` ). Copy it verbatim into new files.
2727- File-scoped namespaces (` namespace log4net.Appender; ` ). Note ` .editorconfig ` still says
2828 ` csharp_style_namespace_declarations = block_scoped:silent ` , but 242 of 244 files are
29- file-scoped — follow the code, not that setting.
29+ file-scoped: follow the code, not that setting.
3030- ` using ` directives outside the namespace, in one contiguous block.
3131
3232### Language usage
33- - ** Explicit types, not ` var ` ** — all three ` csharp_style_var_* ` options are ` false ` .
33+ - ** Explicit types, not ` var ` ** : all three ` csharp_style_var_* ` options are ` false ` .
3434 Write ` StringWriter writer = new(...) ` .
3535- Target-typed ` new() ` and collection expressions (` private static readonly char[] _x = [',', ';']; ` ).
36- Omit the type wherever the target is known — including ` return new(…); ` and ` => new(…); ` , where
36+ Omit the type wherever the target is known, including ` return new(…); ` and ` => new(…); ` , where
3737 the enclosing member's return type supplies it. It cannot be omitted when the target type is an
3838 interface or abstract class, as in ` Func<ISmtpTransport> f = () => new MailKitSmtpTransport(); ` .
39- - Expression-bodied members whenever the body fits on one line — this includes constructors
39+ - Expression-bodied members whenever the body fits on one line, including constructors
4040 (` resharper_constructor_or_destructor_body = expression_body ` ).
4141- Braces on ` if ` /` else ` bodies even for a single statement.
4242- ` LangVersion ` is ` latest ` , and current C# features are welcome and in use: primary
4343 constructors (` csharp_style_prefer_primary_constructors = true ` ), the ` field ` keyword in
4444 property accessors, list patterns, ` switch ` expressions.
4545- ** Wrap long string literals with a multi-line raw string (` """ ` ), never with ` + `
46- concatenation.** This includes attribute arguments — see the ` [Obsolete(...)] ` message on
46+ concatenation.** This includes attribute arguments; see the ` [Obsolete(...)] ` message on
4747 ` log4net.Appender.SmtpAppender ` . Raw strings have no line-continuation, so each source line
4848 break really is a ` \n ` in the value, but that is fine here: compiler diagnostics render those
4949 newlines as spaces, so a wrapped message still reads as one sentence. Raw strings are constant
50- expressions, so they are legal in attributes, and the feature is purely syntactic — it works on
50+ expressions, so they are legal in attributes, and the feature is purely syntactic, so it works on
5151 ` net462 ` /` netstandard2.0 ` too.
5252- Private fields are ` _camelCase ` . Private fields and helper methods are commonly placed
5353 * after* the public surface of the type rather than at the top.
5454
55- ### Nullability — the big constraint
55+ ### Nullability, the big constraint
5656- ` Nullable ` is enabled solution-wide with ` WarningsAsErrors=nullable ` : ** any nullability
5757 warning is a build error** , so it cannot be deferred.
5858- ` log4net ` targets ` net462;netstandard2.0 ` . ** Neither reference assembly is nullable-annotated** ,
@@ -68,7 +68,7 @@ almost always be doing.
6868- Use the internal ` log4net.Util.Log4NetAssert ` extensions rather than hand-rolled checks:
6969 ` EnsureNotNull() ` , ` EnsureNotNullOrEmpty() ` , ` EnsureIs<T>() ` . They carry
7070 ` [CallerArgumentExpression(nameof(value))] ` , so no argument name is passed at the call site.
71- This includes constructor and property assignments — write ` _x = x.EnsureNotNull(); ` ,
71+ This includes constructor and property assignments: write ` _x = x.EnsureNotNull(); ` ,
7272 not ` _x = x ?? throw new ArgumentNullException(nameof(x)); ` .
7373- Appenders never let exceptions escape to the caller. The house pattern is
7474 ` catch (Exception e) when (!e.IsFatal()) { ErrorHandler.Error("...", e); } ` .
@@ -83,20 +83,90 @@ almost always be doing.
8383 requires linking ` NotNullAttribute ` , ` ValidatedNotNullAttribute ` and
8484 ` CallerArgumentExpressionAttribute ` , or you get ` CS0122 ` .
8585- Analyzers (` Microsoft.CodeAnalysis.NetAnalyzers ` , ` AnalysisLevel 8 ` , ` src/log4net.globalconfig ` )
86- run on every build. ** The solution builds with 0 warnings — keep it that way.**
86+ run on every build. ** The solution builds with 0 warnings, keep it that way.**
87+
88+ ### Documentation comments
89+ - ** Every public and protected member gets an XML doc comment** , in test code as well as production
90+ code: test methods, nested helper classes and hand-written fakes included.
91+ - Use ` /// <inheritdoc/> ` when the member implements an interface or overrides a base member, and a
92+ real ` <summary> ` for everything else. ` Log4NetTransaction ` in the AdoNet test doubles is the
93+ pattern to copy.
94+ - When checking whether a member is documented, remember that ` [Test] ` , ` #pragma ` and
95+ ` // ReSharper disable ` lines legitimately sit between the doc comment and the declaration.
96+
97+ ### Writing, in code and everywhere else
98+ - ** Never use an em dash (` — ` ) or en dash (` – ` ).** Use a plain hyphen, or restructure with a colon,
99+ comma or parentheses. This covers comments, XML docs, commit messages, AsciiDoc and chat.
100+ - In AsciiDoc, ` -- ` is also forbidden: Asciidoctor renders a spaced double hyphen as an em dash,
101+ so it breaks the rule even though the source looks like plain hyphens. Grep touched files for
102+ ` [—–] ` and ` -- ` before presenting a change.
103+ - No underscores in identifiers, including test method names. ` AllContainsEveryFlag ` , not
104+ ` All_ShouldContainAllFlags ` . (Private fields are ` _camelCase ` , which is the one exception.)
87105
88106### Tests
89107- NUnit 4, not MSTest, and always the constraint model: ` Assert.That(actual, Is.EqualTo(expected)) `
90108 (810 uses of ` Assert.That ` , zero of ` Assert.AreEqual ` ). ` [TestFixture] ` , ` [Test] ` , ` [TestCase] ` ,
91109 with ` [SetUp] ` /` [TearDown] ` for per-test state.
92- - ` NUnit.Analyzers ` warnings are errors too — e.g. NUnit1032 requires an ` IDisposable ` fixture
110+ - Use an expression body for a single-statement test: ` public void X() => Assert.That(...); ` .
111+ - ** ` log4net ` has no ` InternalsVisibleTo ` ** , so private and internal members are exercised through
112+ reflection, not by widening their accessibility. See ` SystemInfoTest ` , ` LevelMappingTest ` and
113+ ` UserNameFixingTest ` for the ` BindingFlags.Static | BindingFlags.NonPublic ` pattern.
114+ ` log4net.Ext.Mail ` does grant ` InternalsVisibleTo ` to its own test project.
115+ - Mark a test ` [NonParallelizable] ` when it mutates static state (` LogLog.InternalDebugging ` , a
116+ static field on a test double, a process-wide native registration).
117+ - Wrap expected internal logging in ` LogLog.ExecuteWithoutEmittingInternalMessages(...) ` and capture
118+ it with ` LogLog.LogReceivedAdapter ` rather than letting it reach the console. Appender errors are
119+ emitted by default, so a test that provokes one will otherwise add noise to the suite output.
120+ - Guard platform-specific tests with ` [Platform("Win")] ` / ` [Platform("Linux")] ` . A test that only
121+ runs on Windows leaves the behaviour unverified in local Linux runs, so prefer a cross-platform
122+ home for the assertion when one exists.
123+ - ` NUnit.Analyzers ` warnings are errors too: for example NUnit1032 requires an ` IDisposable ` fixture
93124 field to be disposed in a ` [TearDown] ` method.
94125- For code that talks to the outside world, introduce a narrow interface and hand-write a fake;
95126 there is no mocking library in any test project. See ` ISmtpTransport ` / ` FakeSmtpTransport ` .
96127- Verify with ` dotnet build src/log4net.sln ` and
97128 ` dotnet test src/<project>.Tests/<project>.Tests.csproj ` .
98129- ** When inspecting build output, redirect it to a file and read the whole thing; do not pipe
99130 MSBuild through line-oriented tools.** ` grep ` /` Select-String ` cannot match across newlines, and
100- MSBuild's console logger formats differently when piped than when redirected — a multi-line
131+ MSBuild's console logger formats differently when piped than when redirected, so a multi-line
101132 diagnostic message then looks truncated when it is not. Before reporting that the toolchain
102133 mangles something, re-check with ` dotnet build … > out.txt 2>&1 ` and inspect ` out.txt ` .
134+
135+ ## Changelog
136+
137+ Every user-visible change gets an entry in ` src/changelog/<unreleased version>/ ` , named
138+ ` <issue>-<kebab-case-slug>.xml ` . The format is the log4j changelog schema:
139+
140+ - ` type ` is one of ` added ` , ` changed ` , ` fixed ` , ` removed ` , ` updated ` .
141+ - ** Every ` <issue> ` element requires both ` id ` and ` link ` ** ; the export fails with
142+ ` missing attribute: link ` otherwise, which is only caught by the Maven site build.
143+ - Put anything that has no issue number, such as an external finding identifier, in the description
144+ text rather than inventing an ` <issue> ` for it.
145+ - ` src/changelog/3.3.2/298-fix-interprocesslock-mutex-leak.xml ` shows the shape for a change that
146+ came out of an external audit.
147+
148+ ## Documentation site
149+
150+ The manual lives in ` src/site/antora/modules/ROOT/pages/ ` . A new appender page needs three edits,
151+ not one: the page itself, an ` xref ` line in ` nav.adoc ` (kept alphabetical), and the appender table
152+ in ` manual/configuration/appenders.adoc ` .
153+
154+ ## Security findings
155+
156+ ** [ AGENTS.md] ( AGENTS.md ) decides whether something is in scope and whether it is a vulnerability.**
157+ Read it before triaging a report, and describe a finding in commit messages and changelog entries
158+ the way it comes out of that assessment: a correctness bug, a reliability defect or hardening is
159+ none the worse for being called one.
160+
161+ What that leaves for this file is where the answers live in the code:
162+
163+ - When a report is likely to recur on a path the threat model already settles, leave a short comment
164+ at the site with a link to the model rather than changing the code. ` XmlConfigurator ` and
165+ ` XmlHierarchyConfigurator ` carry these for the configuration-is-trusted paths, and
166+ ` SystemStringFormat ` for the format string.
167+ - ` LocalSyslogAppender.EscapeNulCharacters ` and ` RemoteSyslogAppender.ValidateIdentity ` are the two
168+ sides of the content and structural-identifier rule: content is escaped and never rejected, a
169+ malformed identifier is reported rather than quietly repaired.
170+ - Deliberate secure-default choices belong in the changelog with their opt-out named, so that an
171+ upgrade surprise is searchable. See the entries for ` SendTimeoutMillis ` , ` MatchTimeoutMillis ` and
172+ ` LockTimeoutMillis ` .
0 commit comments