A note for the community
I plan on submitting a PR implementing this enhancement, and I'd love feedback on the design before I submit the PR.
- Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
- If you are interested in working on this issue or have submitted a pull request, please leave a comment
Use Cases
Collecting Windows log files. Windows log directories routinely mix encodings:
- Windows servicing logs (
C:\Windows\Logs\CBS\CBS.log,
C:\Windows\Logs\DISM\dism.log) are UTF-8 on some Windows versions and
UTF-16 on others: the same file path needs a different fixed charset on
different systems in the same fleet, so no single static config is correct.
- SQL Server
ERRORLOG is UTF-16LE (version-dependent, sometimes without BOM).
- MSI/installer logs are UTF-16 or ANSI depending on the producer, and can
change encoding across rotations of the same logical log.
- PowerShell redirection produces UTF-16LE with BOM; many app logs next to
these files are UTF-8/ASCII.
encoding.charset is per-source today, and the line delimiter is encoded into
that charset for framing, so a single file source with a directory glob cannot
handle a mix of encodings no matter how it is configured. Splitting into one
source per encoding requires knowing each file's encoding in advance, which is
exactly what operators of fleet-deployed agents do not know.
The current failure mode is also mostly silent. UTF-16LE ASCII content read
with charset: utf-8 (or with no encoding at all) contains no invalid UTF-8
byte sequences (0x00 is valid UTF-8), so DecoderMalformedReplacement never
fires and NULL-riddled lines ship to sinks. When replacement warnings do fire,
they carry only the encoding name, not the file, so a single bad file in a
large glob is hard to locate.
Attempted Solutions
- Explicit
encoding.charset per source: works only when every file matched by the source shares one encoding, and breaks when a log changes encoding across rotation.
- Pre-transcoding files outside Vector: not viable for logs being written in place by third-party software.
Proposal
Add an auto mode to the file source's encoding option:
encoding:
charset: auto # existing charset labels keep working unchanged
fallback_charset: utf-8 # optional; used when detection is inconclusive
# (default utf-8 = today's behavior)
Detection is per file, at file open, over the first read chunk (a few bytes
suffice for BOM; a bounded window, e.g. up to 1 KiB, for the heuristics), and
deliberately limited to cases that are (nearly) impossible to get wrong:
- BOM (authoritative):
EF BB BF -> UTF-8, FF FE -> UTF-16LE,
FE FF -> UTF-16BE (exactly the set encoding_rs::Encoding::for_bom
recognizes).
- BOM-less UTF-16 heuristic: NULL bytes heavily concentrated at one byte
parity and near-absent at the other, AND the window decodes without errors
as UTF-16LE/BE (decode_without_bom_handling_and_without_replacement).
- Strict UTF-8 validation of the window -> UTF-8.
- Otherwise ->
fallback_charset, with a once-per-file warning that
detection was inconclusive.
Ordering note: step 2 must precede step 3, because BOM-less UTF-16LE ASCII is
byte-valid UTF-8; a UTF-8-first ladder can never detect the file class that
motivates this feature.
Non-goals (deliberate):
- No detection of legacy single-byte codepages (windows-125x etc.): they are
statistically indistinguishable from each other, and a wrong guess silently
corrupts data. Operators who know the encoding set an explicit charset (or
fallback_charset). This also keeps chardetng-style content guessing out.
- No UTF-32 (not supported by encoding_rs / the Encoding Standard, and
effectively absent from real log files).
Implementation sketch:
- Detection lives with per-file state in
lib/file-source (FileWatcher),
because the line delimiter must become per-file: today the delimiter is
encoded once per source into the configured charset and used for byte-level
framing. With auto, the watcher sniffs at open, picks the per-file
delimiter bytes, and annotates emitted Lines with the detected encoding.
- The source-side transcoding keeps today's decoder-sharing compromise, one
decoder per detected charset instead of one per source.
- Rotation/truncation already produces fresh per-file state via
fingerprinting, so re-detection across rotation (where real logs do change
encoding) falls out naturally.
- Observability: a debug-level internal event per file on detection (file,
detected encoding, via bom|utf16-heuristic|utf8-valid), a warning on
inconclusive fallback, and (small companion improvement) file attribution on
DecoderMalformedReplacement, which currently logs only the encoding name.
- No new dependencies:
encoding_rs (already used for transcoding) provides
BOM sniffing and strict validity checks; UTF-8 validation can use simdutf8
(already in the tree).
Existing behavior is unchanged unless charset: auto is configured.
I want to open a PR and would like feedback before we implement.
References
Version
vector 0.56.0 (x86_64-pc-windows-msvc 7923556 2026-05-02 05:50:46.918905925)
A note for the community
I plan on submitting a PR implementing this enhancement, and I'd love feedback on the design before I submit the PR.
Use Cases
Collecting Windows log files. Windows log directories routinely mix encodings:
C:\Windows\Logs\CBS\CBS.log,C:\Windows\Logs\DISM\dism.log) are UTF-8 on some Windows versions andUTF-16 on others: the same file path needs a different fixed charset on
different systems in the same fleet, so no single static config is correct.
ERRORLOGis UTF-16LE (version-dependent, sometimes without BOM).change encoding across rotations of the same logical log.
these files are UTF-8/ASCII.
encoding.charsetis per-source today, and the line delimiter is encoded intothat charset for framing, so a single file source with a directory glob cannot
handle a mix of encodings no matter how it is configured. Splitting into one
source per encoding requires knowing each file's encoding in advance, which is
exactly what operators of fleet-deployed agents do not know.
The current failure mode is also mostly silent. UTF-16LE ASCII content read
with
charset: utf-8(or with noencodingat all) contains no invalid UTF-8byte sequences (
0x00is valid UTF-8), soDecoderMalformedReplacementneverfires and NULL-riddled lines ship to sinks. When replacement warnings do fire,
they carry only the encoding name, not the file, so a single bad file in a
large glob is hard to locate.
Attempted Solutions
encoding.charsetper source: works only when every file matched by the source shares one encoding, and breaks when a log changes encoding across rotation.Proposal
Add an
automode to the file source's encoding option:Detection is per file, at file open, over the first read chunk (a few bytes
suffice for BOM; a bounded window, e.g. up to 1 KiB, for the heuristics), and
deliberately limited to cases that are (nearly) impossible to get wrong:
EF BB BF-> UTF-8,FF FE-> UTF-16LE,FE FF-> UTF-16BE (exactly the setencoding_rs::Encoding::for_bomrecognizes).
parity and near-absent at the other, AND the window decodes without errors
as UTF-16LE/BE (
decode_without_bom_handling_and_without_replacement).fallback_charset, with a once-per-file warning thatdetection was inconclusive.
Ordering note: step 2 must precede step 3, because BOM-less UTF-16LE ASCII is
byte-valid UTF-8; a UTF-8-first ladder can never detect the file class that
motivates this feature.
Non-goals (deliberate):
statistically indistinguishable from each other, and a wrong guess silently
corrupts data. Operators who know the encoding set an explicit charset (or
fallback_charset). This also keeps chardetng-style content guessing out.effectively absent from real log files).
Implementation sketch:
lib/file-source(FileWatcher),because the line delimiter must become per-file: today the delimiter is
encoded once per source into the configured charset and used for byte-level
framing. With
auto, the watcher sniffs at open, picks the per-filedelimiter bytes, and annotates emitted
Lines with the detected encoding.decoder per detected charset instead of one per source.
fingerprinting, so re-detection across rotation (where real logs do change
encoding) falls out naturally.
detected encoding, via bom|utf16-heuristic|utf8-valid), a warning on
inconclusive fallback, and (small companion improvement) file attribution on
DecoderMalformedReplacement, which currently logs only the encoding name.encoding_rs(already used for transcoding) providesBOM sniffing and strict validity checks; UTF-8 validation can use
simdutf8(already in the tree).
Existing behavior is unchanged unless
charset: autois configured.I want to open a PR and would like feedback before we implement.
References
encoding.charsetoption to sources and sinks #2166 (originalencoding.charsetoption)Version
vector 0.56.0 (x86_64-pc-windows-msvc 7923556 2026-05-02 05:50:46.918905925)