vectordotdev / vectordotdev/vector
file source: `encoding.charset = "auto"` - safe per-file encoding detection (BOM + UTF-8/UTF-16 only)
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 22.6k
- Forks
- 2.3k
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 146
Description
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
ERRORLOGis 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.charsetper 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 setencoding_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. Withauto, the watcher sniffs at open, picks the per-file
delimiter bytes, and annotates emittedLines 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 usesimdutf8
(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
- #2166 (original
encoding.charsetoption) - #25810 (windows service startup-failure fix; same motivation context: Vector as a fleet-managed Windows collector as part of https://sparklogs.com/ )
Version
vector 0.56.0 (x86_64-pc-windows-msvc 7923556 2026-05-02 05:50:46.918905925)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/file-source, especially the FileWatcher per-file state and current byte-level line framing. Review the existing encoding_rs transcoding path and DecoderMalformedReplacement attribution. Done means charset: auto supports the proposed BOM, UTF-16 heuristic, strict UTF-8, and fallback behavior per file without changing explicit charset behavior, with detection and warning observability.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, observability
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100