From 136b5cd19286055b20214ab5e5c809d171e0a2be Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Tue, 19 Aug 2025 21:42:43 +0200
Subject: [PATCH 01/26] start verify-release.sh with bash
---
src/changelog/3.2.0/.release.xml | 2 +-
src/site/antora/modules/ROOT/pages/release-review.adoc | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/changelog/3.2.0/.release.xml b/src/changelog/3.2.0/.release.xml
index b1a9979b..a41b9803 100644
--- a/src/changelog/3.2.0/.release.xml
+++ b/src/changelog/3.2.0/.release.xml
@@ -2,5 +2,5 @@
diff --git a/src/site/antora/modules/ROOT/pages/release-review.adoc b/src/site/antora/modules/ROOT/pages/release-review.adoc
index dfc03bf4..17cd5812 100644
--- a/src/site/antora/modules/ROOT/pages/release-review.adoc
+++ b/src/site/antora/modules/ROOT/pages/release-review.adoc
@@ -98,7 +98,8 @@ wget --cut-dirs=6 \
+
[source,bash]
----
-& ./verify-release.sh
+bash ./verify-release.sh
+cd src
----
+
[%collapsible]
From c3f92ba0cbca802573dd74843ca2528d9f6ef8f1 Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Tue, 19 Aug 2025 22:48:17 +0200
Subject: [PATCH 02/26] change dockerfile from mono:latest to ubuntu:20.04 and
install mono manually
---
Dockerfile | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/Dockerfile b/Dockerfile
index b018427e..f39c387b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,7 +16,21 @@
# MAINTAINER Jan Friedrich
-FROM mono:latest
+FROM ubuntu:20.04
+ENV DEBIAN_FRONTEND=noninteractive \
+ TZ=Etc/UTC
+
+# Install Mono SDK (compiler, msbuild, runtime, etc.)
+RUN apt-get update && \
+ apt-get install -y gnupg ca-certificates && \
+ apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
+ echo "deb https://download.mono-project.com/repo/ubuntu stable-focal main" | tee /etc/apt/sources.list.d/mono-official-stable.list && \
+ apt-get update && \
+ apt-get install -y mono-complete && \
+ rm -rf /var/lib/apt/lists/*
+
+# Check Mono version
+RUN mono --version
RUN apt-get update \
&& apt-get upgrade -y \
From 0225493d8ac2243a8b0828be4b82e54d199b979a Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Tue, 19 Aug 2025 23:01:29 +0200
Subject: [PATCH 03/26] added release notes for c3f92ba
---
src/changelog/3.2.0/c3f92ba-change-dockerfile.xml | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 src/changelog/3.2.0/c3f92ba-change-dockerfile.xml
diff --git a/src/changelog/3.2.0/c3f92ba-change-dockerfile.xml b/src/changelog/3.2.0/c3f92ba-change-dockerfile.xml
new file mode 100644
index 00000000..483f783d
--- /dev/null
+++ b/src/changelog/3.2.0/c3f92ba-change-dockerfile.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ change dockerfile from mono:latest to ubuntu:20.04 and install mono manually (by @FreeAndNil)
+
+
\ No newline at end of file
From 4559fde4bcad8980c7ef5930297a9344d589504f Mon Sep 17 00:00:00 2001
From: Gary Gregory
Date: Wed, 20 Aug 2025 14:22:53 -0400
Subject: [PATCH 04/26] Update release-review.adoc
- Mention PowerShell
- Add missing $releaseVersion = ...
- Add missing `$`
---
src/site/antora/modules/ROOT/pages/release-review.adoc | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/site/antora/modules/ROOT/pages/release-review.adoc b/src/site/antora/modules/ROOT/pages/release-review.adoc
index 17cd5812..b9e4c942 100644
--- a/src/site/antora/modules/ROOT/pages/release-review.adoc
+++ b/src/site/antora/modules/ROOT/pages/release-review.adoc
@@ -20,12 +20,13 @@
Releases of log4net can be verified with following steps:
[#windows]
-== Windows
+== Windows (PowerShell)
. Prerequisites (winget - in case of problems see next section choco)
+
[source,powershell]
----
+$releaseVersion = ...VerionToValidate...
winget install -e --id GnuPG.Gpg4win
winget install -e --id Slik.Subversion # or any other subversion client
winget install -e --id Mono.Mono
@@ -48,8 +49,8 @@ choco install dotnet-8.0-sdk
+
[source,powershell]
----
-svn co https://dist.apache.org/repos/dist/dev/logging/log4net/{releaseVersion} log4net-{releaseVersion}
-pushd log4net-{releaseVersion}
+svn co https://dist.apache.org/repos/dist/dev/logging/log4net/${releaseVersion} log4net-${releaseVersion}
+pushd log4net-${releaseVersion}
----
. Verify and extract
@@ -147,4 +148,4 @@ docker run -it log4net-builder
# - build src/log4net.sln
# inside the container run
dotnet test /logging-log4net/src/log4net.sln
-----
\ No newline at end of file
+----
From 71631474ed652e3be908a3217f6b272cfb1cded8 Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Wed, 20 Aug 2025 22:48:58 +0200
Subject: [PATCH 05/26] added check for unzip moved switch to src from doc to
script added Set-ExecutionPolicy to prerequisites added Unblock-File added
unzip and subversion to Linux prerequisites
---
scripts/verify-release.sh | 7 ++++++
.../modules/ROOT/pages/release-review.adoc | 22 ++++++++++++++-----
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/scripts/verify-release.sh b/scripts/verify-release.sh
index da6720a1..d1b3b9b8 100644
--- a/scripts/verify-release.sh
+++ b/scripts/verify-release.sh
@@ -1,6 +1,11 @@
#!/bin/bash
set -e
+if ! which unzip >/dev/null 2>&1; then
+ echo "The 'unzip' utility is required, but was not found in your path" >&2
+ exit 1
+fi
+
TARGET_DIR="$1";
if test -z "$TARGET_DIR"; then
TARGET_DIR="$(pwd)"
@@ -17,3 +22,5 @@ done
mkdir -p src
cd src
unzip -q -o ../*source*.zip
+
+cd src
\ No newline at end of file
diff --git a/src/site/antora/modules/ROOT/pages/release-review.adoc b/src/site/antora/modules/ROOT/pages/release-review.adoc
index b9e4c942..a7f8efd1 100644
--- a/src/site/antora/modules/ROOT/pages/release-review.adoc
+++ b/src/site/antora/modules/ROOT/pages/release-review.adoc
@@ -26,7 +26,8 @@ Releases of log4net can be verified with following steps:
+
[source,powershell]
----
-$releaseVersion = ...VerionToValidate...
+# as administrator
+Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
winget install -e --id GnuPG.Gpg4win
winget install -e --id Slik.Subversion # or any other subversion client
winget install -e --id Mono.Mono
@@ -49,6 +50,7 @@ choco install dotnet-8.0-sdk
+
[source,powershell]
----
+$releaseVersion = ...VersionToValidate...
svn co https://dist.apache.org/repos/dist/dev/logging/log4net/${releaseVersion} log4net-${releaseVersion}
pushd log4net-${releaseVersion}
----
@@ -57,6 +59,7 @@ pushd log4net-${releaseVersion}
+
[source,powershell]
----
+Unblock-File ./verify-release.ps1
& ./verify-release.ps1
----
@@ -72,6 +75,14 @@ dotnet test ./src/log4net.sln
[#linux]
== Linux
+. Prerequisites
++
+[source,bash]
+----
+sudo apt install unzip -y
+sudo apt install subversion -y
+----
+
. Check out the release distribution:
+
[source,bash]
@@ -100,7 +111,6 @@ wget --cut-dirs=6 \
[source,bash]
----
bash ./verify-release.sh
-cd src
----
+
[%collapsible]
@@ -131,8 +141,10 @@ for sigFile in *.asc; do gpg --verify $sigFile ${sigFile%.asc}; done
+
[source,bash]
----
-umask 0022
-unzip -q *source*.zip -d src
+mkdir -p src
+cd src
+unzip -q -o ../*source*.zip
+
cd src
----
====
@@ -148,4 +160,4 @@ docker run -it log4net-builder
# - build src/log4net.sln
# inside the container run
dotnet test /logging-log4net/src/log4net.sln
-----
+----
\ No newline at end of file
From aab079170989ce80958d070d1bee1a4a0f4755bb Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Thu, 21 Aug 2025 20:43:20 +0200
Subject: [PATCH 06/26] switch to macos-14 - mono no longer works on macos-15
---
.github/workflows/build.yaml | 6 +++---
src/log4net.Tests/log4net.Tests.csproj | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 6c0a06d1..86205a46 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -33,7 +33,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ macos-latest, ubuntu-22.04, windows-latest ]
+ os: [ macos-14, ubuntu-22.04, windows-latest ]
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
@@ -53,7 +53,7 @@ jobs:
- name: Build
run: |
dotnet build ./src/log4net.sln
-
+
- name: Test
run: |
- dotnet test ./src/log4net.sln
+ dotnet test ./src/log4net.sln
\ No newline at end of file
diff --git a/src/log4net.Tests/log4net.Tests.csproj b/src/log4net.Tests/log4net.Tests.csproj
index b144e43a..4a1895ed 100644
--- a/src/log4net.Tests/log4net.Tests.csproj
+++ b/src/log4net.Tests/log4net.Tests.csproj
@@ -55,4 +55,4 @@
-
+
\ No newline at end of file
From ed615c93b405cbdf7d92141aa6e2e2e358cb3da8 Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Fri, 22 Aug 2025 22:09:41 +0200
Subject: [PATCH 07/26] adjust release date for 3.2.0
---
src/changelog/3.2.0/.release.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/changelog/3.2.0/.release.xml b/src/changelog/3.2.0/.release.xml
index a41b9803..b854b1ab 100644
--- a/src/changelog/3.2.0/.release.xml
+++ b/src/changelog/3.2.0/.release.xml
@@ -2,5 +2,5 @@
From b16ef87c3954f3f2b9d49d6feb28efac206b4fe4 Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Fri, 22 Aug 2025 23:02:46 +0200
Subject: [PATCH 08/26] updated version to 3.2.1
---
doc/MailTemplate.Announce.txt | 8 ++--
doc/MailTemplate.Result.txt | 4 +-
doc/MailTemplate.txt | 6 +--
package.json | 2 +-
pom.xml | 4 +-
scripts/build-preview.ps1 | 4 +-
scripts/build-release.ps1 | 2 +-
src/changelog/3.2.1/.release-notes.adoc.ftl | 41 +++++++++++++++++++++
src/changelog/3.2.1/.release.xml | 7 ++++
src/log4net/log4net.csproj | 2 +-
10 files changed, 64 insertions(+), 16 deletions(-)
create mode 100644 src/changelog/3.2.1/.release-notes.adoc.ftl
create mode 100644 src/changelog/3.2.1/.release.xml
diff --git a/doc/MailTemplate.Announce.txt b/doc/MailTemplate.Announce.txt
index 1855ceb1..0de9c5c5 100644
--- a/doc/MailTemplate.Announce.txt
+++ b/doc/MailTemplate.Announce.txt
@@ -1,10 +1,10 @@
To: announce@apache.org, dev@logging.apache.org
-Subject: [ANNOUNCE] Apache log4net 3.2.0 released
+Subject: [ANNOUNCE] Apache log4net 3.2.1 released
Hi,
-the Apache log4net team is pleased to announce the 3.2.0 release.
+the Apache log4net team is pleased to announce the 3.2.1 release.
For further information (support, download, etc.) see
- https://logging.apache.org/log4net/release-notes.html
-- https://github.com/apache/logging-log4net/releases/tag/rel%2F3.2.0
-- https://www.nuget.org/packages/log4net/3.2.0
+- https://github.com/apache/logging-log4net/releases/tag/rel%2F3.2.1
+- https://www.nuget.org/packages/log4net/3.2.1
diff --git a/doc/MailTemplate.Result.txt b/doc/MailTemplate.Result.txt
index e8818c1e..38e094ed 100644
--- a/doc/MailTemplate.Result.txt
+++ b/doc/MailTemplate.Result.txt
@@ -1,5 +1,5 @@
To: dev@logging.apache.org
-Subject: [RESULT][VOTE] Release Apache Log4net 3.2.0
+Subject: [RESULT][VOTE] Release Apache Log4net 3.2.1
and here is my +1.
@@ -9,6 +9,6 @@ I will continue the release process.
Jan
---------------------------------------------------------------------------------------------------
-This is a vote to release the Apache Log4net 3.2.0.
+This is a vote to release the Apache Log4net 3.2.1.
...
diff --git a/doc/MailTemplate.txt b/doc/MailTemplate.txt
index eaf09174..fa50a629 100644
--- a/doc/MailTemplate.txt
+++ b/doc/MailTemplate.txt
@@ -1,12 +1,12 @@
To: dev@logging.apache.org
-Subject: [VOTE] Release Apache Log4net 3.2.0
+Subject: [VOTE] Release Apache Log4net 3.2.1
-This is a vote to release the Apache Log4net 3.2.0.
+This is a vote to release the Apache Log4net 3.2.1.
Website: https://logging.staged.apache.org/log4net/release-notes.html
GitHub: https://github.com/apache/logging-log4net
Commit:
-Distribution: https://dist.apache.org/repos/dist/dev/logging/log4net/3.2.0
+Distribution: https://dist.apache.org/repos/dist/dev/logging/log4net/3.2.1
Signing key: 0x7D24496A230E29D6349A99EF583E491578F02D5D
Review kit: https://logging.staged.apache.org/log4net/release-review.html
diff --git a/package.json b/package.json
index e3a67355..b1ad339c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "log4net",
- "version": "3.2.0",
+ "version": "3.2.1",
"description": "Log4Net is a logging framework for .NET",
"scripts": {
"test": "run-s clean-build test-dotnet run-dotnet-core-tests",
diff --git a/pom.xml b/pom.xml
index 35f1571c..ebc397f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,4 +1,4 @@
-
+
-
-
+
+
\ No newline at end of file
diff --git a/src/site/antora/modules/ROOT/pages/manual/configuration/appenders/remotesyslogappender.adoc b/src/site/antora/modules/ROOT/pages/manual/configuration/appenders/remotesyslogappender.adoc
index 0a301b04..8a4557a8 100644
--- a/src/site/antora/modules/ROOT/pages/manual/configuration/appenders/remotesyslogappender.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/configuration/appenders/remotesyslogappender.adoc
@@ -71,12 +71,12 @@ The new default is `Escape`.
The available modes are:
`Escape` (default since 3.3.0)::
- Newlines are replaced with the escaped representations `\r` → "\\r" and `\n` → "\\n`.
+ Newlines are replaced with the escaped representations `\r` → "\\r" and `\n` → "\\n".
The message is sent as a single syslog entry.
`Split` (default before 3.3.0)::
The message is split at each newline into multiple syslog entries.
- Combined sequences (\r\n or \n\r) count as a single break.
+ Combined sequences (\r\n) count as a single break.
This matches the behaviour in versions before 3.3.0.
`Keep`::
From 2fec026a69494de96cdd8be5d44f022086483bfe Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Mon, 8 Dec 2025 23:05:15 +0100
Subject: [PATCH 22/26] set version in supported-versions.adoc via
update-version.ps1
---
antora-playbook.yaml | 2 +-
scripts/update-version.ps1 | 1 +
src/changelog/3.3.0/.release.xml | 2 +-
src/site/antora/modules/ROOT/partials/supported-versions.adoc | 4 ++--
4 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/antora-playbook.yaml b/antora-playbook.yaml
index edd2e794..c8951d3f 100644
--- a/antora-playbook.yaml
+++ b/antora-playbook.yaml
@@ -134,7 +134,7 @@ ui:
Please read our privacy policy.
- Apache, log4net, and the Apache feather logo are trademarks or registered trademarks of The Apache Software Foundation.
+ Apache, log4net, and the Apache logo are trademarks or registered trademarks of The Apache Software Foundation.
Other names may be trademarks of their respective owners.
diff --git a/scripts/update-version.ps1 b/scripts/update-version.ps1
index cce71f2d..9c41e969 100644
--- a/scripts/update-version.ps1
+++ b/scripts/update-version.ps1
@@ -53,6 +53,7 @@ Update-TextVersion $PSScriptRoot/../doc/MailTemplate.Announce.txt $OldVersion $N
Update-TextVersion $PSScriptRoot/build-preview.ps1 $OldVersion $NewVersion
Update-TextVersion $PSScriptRoot/build-release.ps1 $OldVersion $NewVersion
Update-XmlVersion $PSScriptRoot/../src/log4net/log4net.csproj $NewVersion '/Project/PropertyGroup/Version'
+Update-TextVersion $PSScriptRoot/../src/site/antora/modules/ROOT/partials/supported-versions.adoc $OldVersion $NewVersion
$ReleaseNoteXml = '
+
Date: Tue, 9 Dec 2025 09:00:02 +0100
Subject: [PATCH 23/26] fixed flaky test
Log4Net_WritesLogFile_WithDateAndSizeRoll_Config_Works (#277)
- failed when the minute changed unexpectedly
- now with completely deterministic dates
---
src/Directory.Build.props | 1 +
.../Integration/Log4NetIntegrationTests.cs | 62 ++++++++++++++++---
src/log4net.Tests/Integration/MockDateTime.cs | 9 +--
.../log4net.maxsizeroll_date.config | 25 --------
src/log4net.Tests/log4net.Tests.csproj | 17 +----
5 files changed, 56 insertions(+), 58 deletions(-)
delete mode 100644 src/log4net.Tests/Integration/log4net.maxsizeroll_date.config
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 7f3ae8bb..370c0eb0 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -11,6 +11,7 @@
true
<_SkipUpgradeNetAnalyzersNuGetWarning>true
true
+ en;en-US
4.5.0
diff --git a/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs b/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
index 9725c8eb..313c4eb5 100644
--- a/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
+++ b/src/log4net.Tests/Integration/Log4NetIntegrationTests.cs
@@ -23,8 +23,12 @@
using System.Linq;
using log4net.Appender;
using log4net.Config;
+using log4net.Core;
+using log4net.Layout;
using log4net.Repository;
+using log4net.Repository.Hierarchy;
using NUnit.Framework;
+using LoggerHierarchy = log4net.Repository.Hierarchy.Hierarchy;
namespace log4net.Tests.Integration
{
@@ -172,17 +176,24 @@ public void Log4Net_WritesLogFile_WithMaxSizeRoll_Config_Works()
public void Log4Net_WritesLogFile_WithDateAndSizeRoll_Config_Works()
{
DirectoryInfo logDir = CreateLogDirectory("integrationTestLogDir_maxsizerolldate");
- (ILog log, ILoggerRepository repo) = ArrangeLogger("log4net.maxsizeroll_date.config");
- MockDateTime mockDateTime = new();
- repo.GetAppenders().OfType().First().DateTimeStrategy = mockDateTime;
- // Write enough lines to trigger rolling by size and date
- for (int i = 1; i < 10000; ++i)
+ DateTime startDate = new(2025, 12, 08, 15, 55, 50);
+ MockDateTime mockDateTime = new(startDate); // start at the end of a minute
+ (ILog log, ILoggerRepository repo) = ArrangeCompositeLogger(mockDateTime);
+ // distribute 10.000 log entries over 60 seconds
+ TimeSpan stepIncrement = new(TimeSpan.FromSeconds(60).Ticks / 10000);
+ // 1000 entries (each 100 bytes) -> ~100KB total - 10 rolls expected - 4 will survive
+ for (int i = 1; i < 1000; ++i)
{
- log.Debug($"DateRoll entry {i}");
- if (i % 5000 == 0)
- {
- mockDateTime.Offset = TimeSpan.FromMinutes(1); // allow time for date to change if needed
- }
+ log.Debug($"DateRoll entry {i:D5}");
+ mockDateTime.Now += stepIncrement;
+ }
+ // switch to next minute to force date roll
+ mockDateTime.Now = startDate.AddSeconds(10);
+ // 1000 entries (each 100 bytes) -> ~100KB total - 10 rolls expected - 4 will survive
+ for (int i = 1; i < 1000; ++i)
+ {
+ log.Debug($"DateRoll entry {i:D5}");
+ mockDateTime.Now += stepIncrement;
}
repo.Shutdown();
// Assert: rolled files exist (date+size pattern)
@@ -270,5 +281,36 @@ private static (ILog log, ILoggerRepository repo) ArrangeLogger(string configFil
ILog log = LogManager.GetLogger(repo.Name, "IntegrationTestLogger");
return (log, repo);
}
+
+ private static (ILog log, ILoggerRepository repo) ArrangeCompositeLogger(RollingFileAppender.IDateTime dateTime)
+ {
+ LoggerHierarchy repo = (LoggerHierarchy)LogManager.CreateRepository(Guid.NewGuid().ToString());
+ PatternLayout layout = new() { ConversionPattern = "%d{yyyy/MM/dd HH:mm:ss.fff} %m-%M%n" };
+ layout.ActivateOptions();
+ RollingFileAppender rollingAppender = new()
+ {
+ Name = "LogFileAppender",
+ File = "integrationTestLogDir_maxsizerolldate/.log",
+ AppendToFile = true,
+ RollingStyle = RollingFileAppender.RollingMode.Composite,
+ DatePattern = "HH-mm",
+ DateTimeStrategy = dateTime,
+ MaximumFileSize = "10KB",
+ MaxSizeRollBackups = 3,
+ StaticLogFileName = false,
+ CountDirection = 1,
+ PreserveLogFileNameExtension = true,
+ LockingModel = new FileAppender.NoLock(),
+ Layout = layout
+ };
+ rollingAppender.ActivateOptions();
+ repo.Configured = true;
+ Logger logger = (Logger)repo.GetLogger("IntegrationTestLogger");
+ logger.Level = Level.Debug;
+ logger.AddAppender(rollingAppender);
+ logger.Additivity = false;
+ return (log: new LogImpl(logger), repo);
+ }
+
}
}
\ No newline at end of file
diff --git a/src/log4net.Tests/Integration/MockDateTime.cs b/src/log4net.Tests/Integration/MockDateTime.cs
index efa67d7a..f0bd6317 100644
--- a/src/log4net.Tests/Integration/MockDateTime.cs
+++ b/src/log4net.Tests/Integration/MockDateTime.cs
@@ -25,13 +25,8 @@ namespace log4net.Tests.Integration;
///
/// Mock implementation of
///
-internal sealed class MockDateTime : RollingFileAppender.IDateTime
+internal sealed class MockDateTime(DateTime now) : RollingFileAppender.IDateTime
{
- ///
- /// Offset to apply to the current time.
- ///
- internal TimeSpan Offset { get; set; }
-
///
- public DateTime Now => DateTime.Now + Offset;
+ public DateTime Now { get; set; } = now;
}
\ No newline at end of file
diff --git a/src/log4net.Tests/Integration/log4net.maxsizeroll_date.config b/src/log4net.Tests/Integration/log4net.maxsizeroll_date.config
deleted file mode 100644
index 2ea6bf4c..00000000
--- a/src/log4net.Tests/Integration/log4net.maxsizeroll_date.config
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/log4net.Tests/log4net.Tests.csproj b/src/log4net.Tests/log4net.Tests.csproj
index 4a1895ed..368fd27e 100644
--- a/src/log4net.Tests/log4net.Tests.csproj
+++ b/src/log4net.Tests/log4net.Tests.csproj
@@ -35,22 +35,7 @@
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
- Always
-
-
+
Always
From 5640c187a1671599725cbd9ad68936d96df1bcca Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Tue, 9 Dec 2025 10:28:25 +0100
Subject: [PATCH 24/26] stabilize TelnetTest
---
src/log4net.Tests/Appender/TelnetAppenderTest.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/log4net.Tests/Appender/TelnetAppenderTest.cs b/src/log4net.Tests/Appender/TelnetAppenderTest.cs
index ea153e76..71b3b093 100644
--- a/src/log4net.Tests/Appender/TelnetAppenderTest.cs
+++ b/src/log4net.Tests/Appender/TelnetAppenderTest.cs
@@ -93,7 +93,7 @@ void WaitForReceived(int count)
{
retries++;
TestContext.Out.WriteLine($"receiver: waiting for message {count} of client - retry {retries} failed");
- if (retries > 100)
+ if (retries > 500)
{
Assert.Fail("Timeout waiting for received messages");
}
From 10647645458a0ccce9ebbc5b0fa9b6b42b55332d Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Mon, 15 Dec 2025 15:43:46 +0100
Subject: [PATCH 25/26] completed OutputDebugStringAppenderTest
---
.../Appender/OutputDebugAppenderTest.cs | 26 +++++++++----------
.../Appender/OutputDebugStringAppender.cs | 25 +++++++++++++-----
2 files changed, 32 insertions(+), 19 deletions(-)
diff --git a/src/log4net.Tests/Appender/OutputDebugAppenderTest.cs b/src/log4net.Tests/Appender/OutputDebugAppenderTest.cs
index 9659244c..882b1d53 100644
--- a/src/log4net.Tests/Appender/OutputDebugAppenderTest.cs
+++ b/src/log4net.Tests/Appender/OutputDebugAppenderTest.cs
@@ -19,8 +19,6 @@
using System;
-using System.Diagnostics;
-
using log4net.Appender;
using log4net.Config;
using log4net.Core;
@@ -33,13 +31,12 @@ namespace log4net.Tests.Appender;
///
/// Used for internal unit testing the class.
///
-///
-/// Used for internal unit testing the class.
-///
[TestFixture]
[Platform(Include = "Win")]
public sealed class OutputDebugStringAppenderTest
{
+ private const string DebugMessage = "Message - Сообщение - הודעה";
+
///
/// Verifies that the OutputDebugString api is called by the appender without issues
///
@@ -47,8 +44,8 @@ public sealed class OutputDebugStringAppenderTest
public void AppendShouldNotCauseAnyErrors()
{
ILoggerRepository rep = LogManager.CreateRepository(Guid.NewGuid().ToString());
-
- OutputDebugStringAppender outputDebugStringAppender = new()
+ string? lastDebugString = null;
+ OutputAppender outputDebugStringAppender = new(value => lastDebugString = value)
{
Layout = new SimpleLayout(),
ErrorHandler = new FailOnError()
@@ -58,15 +55,18 @@ public void AppendShouldNotCauseAnyErrors()
BasicConfigurator.Configure(rep, outputDebugStringAppender);
ILog log = LogManager.GetLogger(rep.Name, GetType());
- log.Debug("Message - Сообщение - הודעה");
-
- // need a way to check that the api is actually called and the string is properly marshalled.
+ log.Debug(DebugMessage);
+ Assert.That(lastDebugString, Is.Not.Null.And.Contains(DebugMessage));
}
}
-class FailOnError : IErrorHandler
+file sealed class OutputAppender(Action outputDebugString)
+ : OutputDebugStringAppender(outputDebugString)
+{ }
+
+file sealed class FailOnError : IErrorHandler
{
public void Error(string message, Exception? e, ErrorCode errorCode) => Assert.Fail($"Unexpected error: {message} exception: {e}, errorCode: {errorCode}");
public void Error(string message, Exception e) => Assert.Fail($"Unexpected error: {message} exception: {e}");
- public void Error(string message) => Assert.Fail($"Unexpected error: {message}");
-}
+ public void Error(string message) => Assert.Fail($"Unexpected error: {message}");
+}
\ No newline at end of file
diff --git a/src/log4net/Appender/OutputDebugStringAppender.cs b/src/log4net/Appender/OutputDebugStringAppender.cs
index 83e83da7..44442e9d 100644
--- a/src/log4net/Appender/OutputDebugStringAppender.cs
+++ b/src/log4net/Appender/OutputDebugStringAppender.cs
@@ -17,20 +17,33 @@
//
#endregion
-using System.Runtime.InteropServices;
-
+using System;
using log4net.Core;
using log4net.Util;
namespace log4net.Appender;
///
-/// Appends log events to the OutputDebugString system.
+/// Appends log events to the OutputDebugString system
///
/// Nicko Cadell
/// Gert Driesen
public class OutputDebugStringAppender : AppenderSkeleton
{
+ private readonly Action _outputDebugString;
+
+ ///
+ public OutputDebugStringAppender()
+ : this(null)
+ { }
+
+ ///
+ /// Constructor for unit testing
+ ///
+ /// replacement for
+ protected OutputDebugStringAppender(Action? outputDebugString)
+ => _outputDebugString = outputDebugString ?? NativeMethods.OutputDebugString;
+
///
/// Writes the logging event to the output debug string API
///
@@ -40,13 +53,13 @@ public class OutputDebugStringAppender : AppenderSkeleton
protected override void Append(LoggingEvent loggingEvent)
{
#if NETSTANDARD2_0_OR_GREATER
- if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
- throw new System.PlatformNotSupportedException("OutputDebugString is only available on Windows");
+ throw new PlatformNotSupportedException("OutputDebugString is only available on Windows");
}
#endif
- NativeMethods.OutputDebugString(RenderLoggingEvent(loggingEvent));
+ _outputDebugString(RenderLoggingEvent(loggingEvent));
}
///
From 42a77cb05abfd5b1ad7224f0861a932eac95c7c8 Mon Sep 17 00:00:00 2001
From: Jan Friedrich
Date: Tue, 17 Feb 2026 08:59:08 +0100
Subject: [PATCH 26/26] #280 harden the handling of invalid characters for the
XmlLayout classes
---
.../3.3.0/280-harden-invalid-characters.xml | 10 ++++
.../Layout/XmlLayoutSchemaLog4jTest.cs | 53 ++++++++++++++++++-
src/log4net.Tests/Layout/XmlLayoutTest.cs | 50 +++++++++++++++++
src/log4net.Tests/Util/TransformTest.cs | 2 +-
.../Layout/Internal/XmlWriterExtensions.cs | 15 +++++-
src/log4net/Layout/XmlLayout.cs | 45 ++++++++--------
src/log4net/Layout/XmlLayoutSchemaLog4j.cs | 38 +++++++------
src/log4net/Util/Transform.cs | 5 +-
8 files changed, 165 insertions(+), 53 deletions(-)
create mode 100644 src/changelog/3.3.0/280-harden-invalid-characters.xml
diff --git a/src/changelog/3.3.0/280-harden-invalid-characters.xml b/src/changelog/3.3.0/280-harden-invalid-characters.xml
new file mode 100644
index 00000000..512ab807
--- /dev/null
+++ b/src/changelog/3.3.0/280-harden-invalid-characters.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ harden the handling of invalid characters for the XmlLayout classes (by @FreeAndNil in https://github.com/apache/logging-log4net/pull/280[#280])
+
+
\ No newline at end of file
diff --git a/src/log4net.Tests/Layout/XmlLayoutSchemaLog4jTest.cs b/src/log4net.Tests/Layout/XmlLayoutSchemaLog4jTest.cs
index d7d14de0..06587783 100644
--- a/src/log4net.Tests/Layout/XmlLayoutSchemaLog4jTest.cs
+++ b/src/log4net.Tests/Layout/XmlLayoutSchemaLog4jTest.cs
@@ -18,12 +18,11 @@
#endregion
using System;
-
using log4net.Config;
+using log4net.Core;
using log4net.Layout;
using log4net.Repository;
using log4net.Tests.Appender;
-
using NUnit.Framework;
namespace log4net.Tests.Layout
@@ -63,5 +62,55 @@ void ThrowAndLog(int foo)
}
}
}
+
+ ///
+ /// Tests the serialization of invalid characters in the Properties dictionary
+ ///
+ [Test]
+ public void InvalidCharacterTest()
+ {
+ StringAppender stringAppender = new() { Layout = new XmlLayoutSchemaLog4J() };
+
+ ILoggerRepository repository = LogManager.CreateRepository(Guid.NewGuid().ToString());
+ BasicConfigurator.Configure(repository, stringAppender);
+ ILog log = LogManager.GetLogger(repository.Name, "TestLogger");
+
+ Log();
+
+ string logEventXml = stringAppender.GetString();
+ Assert.That(logEventXml, Does.Contain("us?er"));
+ Assert.That(logEventXml, Does.Contain("A?B"));
+ Assert.That(logEventXml, Does.Contain("Log?ger"));
+ Assert.That(logEventXml, Does.Contain("Thread?Name"));
+ Assert.That(logEventXml, Does.Contain("Do?main"));
+ Assert.That(logEventXml, Does.Contain("Ident?ity"));
+ Assert.That(logEventXml, Does.Contain("User?Name"));
+ Assert.That(logEventXml, Does.Contain("Mess?age"));
+ Assert.That(logEventXml, Does.Contain("oh?my"));
+
+ void Log()
+ {
+ // Build a LoggingEvent with an XML invalid character in a property value
+ LoggingEventData data = new()
+ {
+ LoggerName = "Log\u0001ger",
+ Level = Level.Info,
+ TimeStampUtc = DateTime.UtcNow,
+ ThreadName = "Thread\u0001Name",
+ Domain = "Do\u0001main",
+ Identity = "Ident\u0001ity",
+ UserName = "User\u0001Name",
+ Message = "Mess\u0001age",
+ ExceptionString = "oh\u0001my",
+ Properties = new()
+ };
+
+ // Value contains U+0001 which is illegal in XML 1.0
+ data.Properties["us\u0001er"] = "A\u0001B";
+
+ // Log the event
+ log.Logger.Log(new(null, repository, data));
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/log4net.Tests/Layout/XmlLayoutTest.cs b/src/log4net.Tests/Layout/XmlLayoutTest.cs
index 2f323a3b..3c0dc79d 100644
--- a/src/log4net.Tests/Layout/XmlLayoutTest.cs
+++ b/src/log4net.Tests/Layout/XmlLayoutTest.cs
@@ -371,4 +371,54 @@ void Bar(int foo)
Assert.That(sub, Does.Not.Contain(">"));
}
}
+
+ ///
+ /// Tests the serialization of invalid characters in the Properties dictionary
+ ///
+ [Test]
+ public void InvalidCharacterTest()
+ {
+ StringAppender stringAppender = new() { Layout = new XmlLayout() };
+
+ ILoggerRepository repository = LogManager.CreateRepository(Guid.NewGuid().ToString());
+ BasicConfigurator.Configure(repository, stringAppender);
+ ILog log = LogManager.GetLogger(repository.Name, "TestLogger");
+
+ Log();
+
+ string logEventXml = stringAppender.GetString();
+ Assert.That(logEventXml, Does.Contain("us?er"));
+ Assert.That(logEventXml, Does.Contain("A?B"));
+ Assert.That(logEventXml, Does.Contain("Log?ger"));
+ Assert.That(logEventXml, Does.Contain("Thread?Name"));
+ Assert.That(logEventXml, Does.Contain("Do?main"));
+ Assert.That(logEventXml, Does.Contain("Ident?ity"));
+ Assert.That(logEventXml, Does.Contain("User?Name"));
+ Assert.That(logEventXml, Does.Contain("Mess?age"));
+ Assert.That(logEventXml, Does.Contain("oh?my"));
+
+ void Log()
+ {
+ // Build a LoggingEvent with an XML invalid character in a property value
+ LoggingEventData data = new()
+ {
+ LoggerName = "Log\u0001ger",
+ Level = Level.Info,
+ TimeStampUtc = DateTime.UtcNow,
+ ThreadName = "Thread\u0001Name",
+ Domain = "Do\u0001main",
+ Identity = "Ident\u0001ity",
+ UserName = "User\u0001Name",
+ Message = "Mess\u0001age",
+ ExceptionString = "oh\u0001my",
+ Properties = new()
+ };
+
+ // Value contains U+0001 which is illegal in XML 1.0
+ data.Properties["us\u0001er"] = "A\u0001B";
+
+ // Log the event
+ log.Logger.Log(new(null, repository, data));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/log4net.Tests/Util/TransformTest.cs b/src/log4net.Tests/Util/TransformTest.cs
index 78b106cd..b0e932ac 100644
--- a/src/log4net.Tests/Util/TransformTest.cs
+++ b/src/log4net.Tests/Util/TransformTest.cs
@@ -40,4 +40,4 @@ public void MaskXmlInvalidCharactersMasks0Char()
const string c = "\0";
Assert.That(Transform.MaskXmlInvalidCharacters(c, "?"), Is.EqualTo("?"));
}
-}
+}
\ No newline at end of file
diff --git a/src/log4net/Layout/Internal/XmlWriterExtensions.cs b/src/log4net/Layout/Internal/XmlWriterExtensions.cs
index e08b759d..91c46070 100644
--- a/src/log4net/Layout/Internal/XmlWriterExtensions.cs
+++ b/src/log4net/Layout/Internal/XmlWriterExtensions.cs
@@ -32,10 +32,11 @@ namespace log4net.Layout.Internal;
internal static partial class XmlWriterExtensions
{
#if NETSTANDARD2_0_OR_GREATER
- private static readonly XmlWriterSettings _settings = new XmlWriterSettings
+ private static readonly XmlWriterSettings _settings = new()
{
Indent = false,
- OmitXmlDeclaration = true
+ OmitXmlDeclaration = true,
+ CheckCharacters = false
};
#endif
@@ -71,4 +72,14 @@ internal static XmlWriter CreateXmlWriter(TextWriter writer)
Namespaces = false
};
#endif
+
+ ///
+ /// writes the attribute and replaces invalid characters
+ ///
+ internal static void WriteAttributeStringSafe(this XmlWriter writer, string localName, string? value, string mask)
+ {
+ if (string.IsNullOrEmpty(value))
+ return;
+ writer.WriteAttributeString(localName, Transform.MaskXmlInvalidCharacters(value!, mask));
+ }
}
\ No newline at end of file
diff --git a/src/log4net/Layout/XmlLayout.cs b/src/log4net/Layout/XmlLayout.cs
index 0233808c..90b59c1c 100644
--- a/src/log4net/Layout/XmlLayout.cs
+++ b/src/log4net/Layout/XmlLayout.cs
@@ -192,28 +192,30 @@ public override void ActivateOptions()
protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
writer.EnsureNotNull().WriteStartElement(_eventElementName, Prefix, DefaultEventElementName, Prefix);
- writer.WriteAttributeString(LoggerAttributeName, loggingEvent.EnsureNotNull().LoggerName!);
+ writer.WriteAttributeStringSafe(LoggerAttributeName, loggingEvent.EnsureNotNull().LoggerName, InvalidCharReplacement);
- writer.WriteAttributeString(TimestampAttributeName, XmlConvert.ToString(loggingEvent.TimeStamp, XmlDateTimeSerializationMode.Local));
+ writer.WriteAttributeStringSafe(TimestampAttributeName,
+ XmlConvert.ToString(loggingEvent.TimeStamp, XmlDateTimeSerializationMode.Local),
+ InvalidCharReplacement);
if (loggingEvent.Level is not null)
{
- writer.WriteAttributeString(LevelAttributeName, loggingEvent.Level.DisplayName);
+ writer.WriteAttributeStringSafe(LevelAttributeName, loggingEvent.Level.DisplayName, InvalidCharReplacement);
}
- writer.WriteAttributeString(ThreadAttributeName, loggingEvent.ThreadName!);
+ writer.WriteAttributeStringSafe(ThreadAttributeName, loggingEvent.ThreadName!, InvalidCharReplacement);
if (loggingEvent.Domain is not null && loggingEvent.Domain.Length > 0)
{
- writer.WriteAttributeString(DomainAttributeName, loggingEvent.Domain);
+ writer.WriteAttributeStringSafe(DomainAttributeName, loggingEvent.Domain, InvalidCharReplacement);
}
if (loggingEvent.Identity is not null && loggingEvent.Identity.Length > 0)
{
- writer.WriteAttributeString(IdentityAttributeName, loggingEvent.Identity);
+ writer.WriteAttributeStringSafe(IdentityAttributeName, loggingEvent.Identity, InvalidCharReplacement);
}
if (loggingEvent.UserName.Length > 0)
{
- writer.WriteAttributeString(UsernameAttributeName, loggingEvent.UserName);
+ writer.WriteAttributeStringSafe(UsernameAttributeName, loggingEvent.UserName, InvalidCharReplacement);
}
// Append the message text
@@ -222,13 +224,13 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
writer.WriteStartElement(_messageElementName, Prefix, DefaultMessageElementName, Prefix);
if (!Base64EncodeMessage)
{
- Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(loggingEvent.RenderedMessage, InvalidCharReplacement);
}
else
{
byte[] messageBytes = Encoding.UTF8.GetBytes(loggingEvent.RenderedMessage);
string base64Message = Convert.ToBase64String(messageBytes, 0, messageBytes.Length);
- Transform.WriteEscapedXmlString(writer, base64Message, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(base64Message, InvalidCharReplacement);
}
writer.WriteEndElement();
}
@@ -242,22 +244,17 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
foreach (KeyValuePair entry in properties)
{
writer.WriteStartElement(_dataElementName, Prefix, DefaultDataElementName, Prefix);
- writer.WriteAttributeString(NameAttributeName, Transform.MaskXmlInvalidCharacters(entry.Key, InvalidCharReplacement));
+ writer.WriteAttributeStringSafe(NameAttributeName, entry.Key, InvalidCharReplacement);
// Use an ObjectRenderer to convert the object to a string
if (loggingEvent.Repository is not null)
{
- string valueStr;
- if (!Base64EncodeProperties)
+ string valueStr = loggingEvent.Repository.RendererMap.FindAndRender(entry.Value);
+ if (Base64EncodeProperties)
{
- valueStr = Transform.MaskXmlInvalidCharacters(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value), InvalidCharReplacement);
+ valueStr = Convert.ToBase64String(Encoding.UTF8.GetBytes(valueStr));
}
- else
- {
- byte[] propertyValueBytes = Encoding.UTF8.GetBytes(loggingEvent.Repository.RendererMap.FindAndRender(entry.Value));
- valueStr = Convert.ToBase64String(propertyValueBytes, 0, propertyValueBytes.Length);
- }
- writer.WriteAttributeString(ValueAttributeName, valueStr);
+ writer.WriteAttributeStringSafe(ValueAttributeName, valueStr, InvalidCharReplacement);
}
writer.WriteEndElement();
@@ -270,7 +267,7 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
// Append the stack trace line
writer.WriteStartElement(_exceptionElementName, Prefix, DefaultExceptionElementName, Prefix);
- Transform.WriteEscapedXmlString(writer, exceptionStr, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(exceptionStr, InvalidCharReplacement);
writer.WriteEndElement();
}
@@ -279,10 +276,10 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
if (loggingEvent.LocationInformation is LocationInfo locationInfo)
{
writer.WriteStartElement(_locationElementName, Prefix, DefaultLocationElementName, Prefix);
- writer.WriteAttributeString(ClassAttributeName, locationInfo.ClassName!);
- writer.WriteAttributeString(MethodAttributeName, locationInfo.MethodName);
- writer.WriteAttributeString(FileAttributeName, locationInfo.FileName!);
- writer.WriteAttributeString(LineAttributeName, locationInfo.LineNumber);
+ writer.WriteAttributeStringSafe(ClassAttributeName, locationInfo.ClassName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe(MethodAttributeName, locationInfo.MethodName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe(FileAttributeName, locationInfo.FileName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe(LineAttributeName, locationInfo.LineNumber, InvalidCharReplacement);
writer.WriteEndElement();
}
}
diff --git a/src/log4net/Layout/XmlLayoutSchemaLog4j.cs b/src/log4net/Layout/XmlLayoutSchemaLog4j.cs
index ff2df82f..c8f1636f 100644
--- a/src/log4net/Layout/XmlLayoutSchemaLog4j.cs
+++ b/src/log4net/Layout/XmlLayoutSchemaLog4j.cs
@@ -47,8 +47,7 @@ public class XmlLayoutSchemaLog4J : XmlLayoutBase
/// Constructs an XMLLayoutSchemaLog4j
///
public XmlLayoutSchemaLog4J()
- {
- }
+ { }
///
/// Constructs an XMLLayoutSchemaLog4j.
@@ -67,9 +66,9 @@ public XmlLayoutSchemaLog4J()
/// appender as well.
///
///
- public XmlLayoutSchemaLog4J(bool locationInfo) : base(locationInfo)
- {
- }
+ public XmlLayoutSchemaLog4J(bool locationInfo)
+ : base(locationInfo)
+ { }
///
/// The version of the log4j schema to use.
@@ -137,8 +136,7 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
}
// translate appdomain name
- if (loggingEvent.LookupProperty("log4japp") is null
- && loggingEvent.Domain?.Length > 0)
+ if (loggingEvent.LookupProperty("log4japp") is null && loggingEvent.Domain?.Length > 0)
{
loggingEvent.GetProperties()["log4japp"] = loggingEvent.Domain;
}
@@ -159,7 +157,7 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
// Write the start element
writer.EnsureNotNull().WriteStartElement("log4j:event", "log4j", "event", "log4net");
- writer.WriteAttributeString("logger", loggingEvent.LoggerName);
+ writer.WriteAttributeStringSafe("logger", loggingEvent.LoggerName, InvalidCharReplacement);
// Calculate the timestamp as the number of milliseconds since january 1970
//
@@ -168,18 +166,18 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
// caused by daylight savings time transitions.
TimeSpan timeSince1970 = loggingEvent.TimeStampUtc - _sDate1970;
- writer.WriteAttributeString("timestamp", XmlConvert.ToString((long)timeSince1970.TotalMilliseconds));
+ writer.WriteAttributeStringSafe("timestamp", XmlConvert.ToString((long)timeSince1970.TotalMilliseconds), InvalidCharReplacement);
if (loggingEvent.Level is not null)
{
- writer.WriteAttributeString("level", loggingEvent.Level.DisplayName);
+ writer.WriteAttributeStringSafe("level", loggingEvent.Level.DisplayName, InvalidCharReplacement);
}
- writer.WriteAttributeString("thread", loggingEvent.ThreadName);
+ writer.WriteAttributeStringSafe("thread", loggingEvent.ThreadName, InvalidCharReplacement);
// Append the message text
if (loggingEvent.RenderedMessage is not null)
{
writer.WriteStartElement("log4j:message", "log4j", "message", "log4net");
- Transform.WriteEscapedXmlString(writer, loggingEvent.RenderedMessage, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(loggingEvent.RenderedMessage, InvalidCharReplacement);
writer.WriteEndElement();
}
@@ -190,7 +188,7 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
// Append the NDC text
writer.WriteStartElement("log4j:NDC", "log4j", "NDC", "log4net");
- Transform.WriteEscapedXmlString(writer, valueStr!, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(valueStr!, InvalidCharReplacement);
writer.WriteEndElement();
}
}
@@ -203,13 +201,13 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
foreach (KeyValuePair entry in properties)
{
writer.WriteStartElement("log4j:data", "log4j", "data", "log4net");
- writer.WriteAttributeString("name", entry.Key);
+ writer.WriteAttributeStringSafe("name", entry.Key, InvalidCharReplacement);
// Use an ObjectRenderer to convert the object to a string
string? valueStr = loggingEvent.Repository?.RendererMap.FindAndRender(entry.Value);
if (!string.IsNullOrEmpty(valueStr))
{
- writer.WriteAttributeString("value", valueStr);
+ writer.WriteAttributeStringSafe("value", valueStr, InvalidCharReplacement);
}
writer.WriteEndElement();
@@ -222,7 +220,7 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
{
// Append the stack trace line
writer.WriteStartElement("log4j:throwable", "log4j", "throwable", "log4net");
- Transform.WriteEscapedXmlString(writer, exceptionStr!, InvalidCharReplacement);
+ writer.WriteEscapedXmlString(exceptionStr!, InvalidCharReplacement);
writer.WriteEndElement();
}
@@ -231,10 +229,10 @@ protected override void FormatXml(XmlWriter writer, LoggingEvent loggingEvent)
if (loggingEvent.LocationInformation is LocationInfo locationInfo)
{
writer.WriteStartElement("log4j:locationInfo", "log4j", "locationInfo", "log4net");
- writer.WriteAttributeString("class", locationInfo.ClassName);
- writer.WriteAttributeString("method", locationInfo.MethodName);
- writer.WriteAttributeString("file", locationInfo.FileName);
- writer.WriteAttributeString("line", locationInfo.LineNumber);
+ writer.WriteAttributeStringSafe("class", locationInfo.ClassName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe("method", locationInfo.MethodName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe("file", locationInfo.FileName, InvalidCharReplacement);
+ writer.WriteAttributeStringSafe("line", locationInfo.LineNumber, InvalidCharReplacement);
writer.WriteEndElement();
}
}
diff --git a/src/log4net/Util/Transform.cs b/src/log4net/Util/Transform.cs
index 6ff80086..b646974b 100644
--- a/src/log4net/Util/Transform.cs
+++ b/src/log4net/Util/Transform.cs
@@ -26,9 +26,6 @@ namespace log4net.Util;
/// Utility class for transforming strings.
///
///
-///
-/// Utility class for transforming strings.
-///
///
/// Nicko Cadell
/// Gert Driesen
@@ -46,7 +43,7 @@ public static class Transform
/// or using CDATA sections.
///
///
- public static void WriteEscapedXmlString(XmlWriter writer, string textData, string invalidCharReplacement)
+ public static void WriteEscapedXmlString(this XmlWriter writer, string textData, string invalidCharReplacement)
{
writer.EnsureNotNull();
string stringData = MaskXmlInvalidCharacters(textData, invalidCharReplacement);