Skip to content

Make FileLogger platform-independent: UTF-8 encoding and LF line separators - #208

Open
elharo wants to merge 4 commits into
masterfrom
issue-206-mirror-stream-charset
Open

Make FileLogger platform-independent: UTF-8 encoding and LF line separators#208
elharo wants to merge 4 commits into
masterfrom
issue-206-mirror-stream-charset

Conversation

@elharo

@elharo elharo commented Sep 2, 2026

Copy link
Copy Markdown

Partially fixes #206.

Problem

FileLogger was not platform-independent in two ways:

  1. Charset: writing used the platform default charset. The MirrorStreamWrapper.write(byte[], int, int) decoded mirrored bytes via new String(b, off, len), and the PrintStream encoded output with the platform default. On a host whose default charset is not UTF-8, non-ASCII text (e.g. café) was encoded/decoded as ISO-8859-1 instead of UTF-8, producing mojibake like café.
  2. Line separators: line endings depended on System.lineSeparator(), so on Windows the output used \r\n while on Linux/macOS it used \n, making log files differ from platform to platform.

Change

  • Encode and decode UTF-8 unconditionally: the PrintStream is now created with StandardCharsets.UTF_8 (both the mirrored and non-mirrored paths), and MirrorStreamWrapper buffers bytes and decodes them with UTF-8 on flush. Neither path consults the platform default charset anymore. The write(int) byte path now also flows through the UTF-8-buffered mirror (previously it only worked for ASCII).
  • Use a Unix LF (\n) line separator in consumeLine instead of System.lineSeparator(), so logs are byte-identical across Linux, macOS, and Windows.

getPrintStream() is retained as required by the ExecutionLogger API; it is now UTF-8 encoded and its output is still mirrored.

Tests

  • Reworked the UTF-8 test into mirrorAndFileShouldUseUtf8, which writes non-ASCII content, asserts the mirror handler receives the correctly decoded café, and asserts the underlying file contains the exact UTF-8 bytes — verifying both the mirror and the file are UTF-8 independent of the host.
  • Replaced System.lineSeparator() with \n in FileLoggerTest and TestMirrorHandler to assert the new, platform-independent LF output.

All 26 tests pass.

@elharo
elharo marked this pull request as draft September 2, 2026 11:59
@elharo elharo changed the title Use UTF-8 for MirrorStreamWrapper byte decoding instead of platform default charset Make FileLogger platform-independent: UTF-8 encoding and LF line separators Sep 2, 2026
@elharo

elharo commented Sep 3, 2026

Copy link
Copy Markdown
Author

Hmm, this fails on JDK 25 Windows but in a strange way. Might be related:

[INFO] Running org.apache.maven.shared.scriptinterpreter.BeanShellScriptInterpreterTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.122 s -- in org.apache.maven.shared.scriptinterpreter.BeanShellScriptInterpreterTest
[INFO] Running org.apache.maven.shared.scriptinterpreter.FileLoggerTest
[ERROR] Tests run: 6, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.108 s <<< FAILURE! -- in org.apache.maven.shared.scriptinterpreter.FileLoggerTest
[ERROR] org.apache.maven.shared.scriptinterpreter.FileLoggerTest.outputFileNoMirror(File) -- Time elapsed: 0.078 s <<< FAILURE!
org.opentest4j.AssertionFailedError: 
expected: <Test1
Test2
> but was: <Test1
Test2
>
	at org.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:151)
	at org.junit.jupiter.api.AssertionFailureBuilder.buildAndThrow(AssertionFailureBuilder.java:132)
	at org.junit.jupiter.api.AssertEquals.failNotEqual(AssertEquals.java:197)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
	at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
	at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1145)
	at org.apache.maven.shared.scriptinterpreter.FileLoggerTest.outputFileNoMirror(FileLoggerTest.java:93)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1604)

not clear why it would pass on UNix and fail on Windows


/**
* The underlying file stream this logger writes to.
* The underlying file stream this logger writes to. It is always UTF-8 encoded so that

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove "It is always UTF-8 encoded so that
* output is independent of the platform default charset."
as it should be assumed.

@elharo
elharo marked this pull request as ready for review September 4, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Script decoding falls back to platform default charset, breaking reproducibility

1 participant