Skip to content

Route native logging through the log crate - #174

Draft
ruccho wants to merge 2 commits into
mainfrom
feature/native-logging
Draft

Route native logging through the log crate#174
ruccho wants to merge 2 commits into
mainfrom
feature/native-logging

Conversation

@ruccho

@ruccho ruccho commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Closes #170.

What

Native-side logging was ad-hoc println! throughout, with an Android-specific hack that
redirected the process-wide stdout/stderr into logcat. This replaces all of it with the log
facade plus a single logger implementation in unienc_c.

All 47 println!/eprintln! call sites across unienc_android_mc, unienc_apple_vt,
unienc_ffmpeg, unienc_windows_mf, unienc_webcodecs, and unienc_c now use
log::{error,warn,info,debug,trace}!, assigned by the nature of each message.

Sinks

The logger picks a destination per record:

  • Android__android_log_write under the unienc tag, regardless of the unity feature.
  • Unity, non-AndroidIUnityLog, once UnityPluginLoad has supplied the plugin
    interfaces.
  • Everything else (NuGet package, CLI tests, wasm, and any record emitted before Unity loads
    the plugin) — stdout/stderr.

Choosing per record rather than at install time means records emitted before UnityPluginLoad
fall back instead of being dropped, and records emitted after UnityPluginUnload stop using an
interface that is being torn down. A Unity version that does not expose IUnityLog also falls
back.

Android stays on logcat even under the unity feature: JNI_OnLoad runs well before
UnityPluginLoad, the encoder logs from MediaCodec, tokio, and Vulkan threads that are not
Unity's, and logcat is where native failures are actually diagnosed.

Windows and Linux now build with -F unity

The unity feature was introduced for the Metal and Vulkan blit paths, so build-unienc.yml
passed -F unity only for macOS, iOS, and Android. Windows and Linux received -F mimalloc
alone.

That meant the Unity builds shipped for Windows and Linux never used IUnityLog — with the
change above they would have silently fallen back to println, and native diagnostics would
never have reached the Unity console on those platforms. The main goal of this issue would have
been missed on two of five platforms.

-F unity did not previously compile there. unienc_c calls
PlatformEncodingSystem::unity_plugin_load unconditionally, but UnityPlugin was implemented
only by the two backends that have a rendering integration. This adds empty implementations for
Media Foundation, FFmpeg, and WebCodecs, which fall through to the trait's existing no-op
defaults, and switches the Windows and Linux unity variants to -F unity,mimalloc.

Each backend gains its own unity feature forwarding to unienc_common/unity, rather than
depending on it unconditionally the way the Metal and Vulkan backends do. This keeps
unity-native-plugin out of the non-Unity (NuGet) builds of those platforms, which currently
exclude it.

This changes the shipped Windows and Linux Unity binaries. They now export
UnityPluginLoad/UnityPluginUnload and Unity will call them. Besides enabling IUnityLog,
that entry point runs mimalloc::unity::init, routing mimalloc's OS-level allocations through
IUnityMemoryManager — the same path already used on macOS, iOS, and Android, but not
previously exercised on Windows or Linux. Please validate on real player builds for both
platforms before release.

wasm keeps --no-default-features: unity-native-plugin-sys asserts 64-bit struct layouts and
cannot compile for a 32-bit target. It was never built with unity before this change either.

Log level

Defaults to Info for release builds of the native library and Debug for debug builds, so
debug!/trace! never ship enabled. A new unienc_set_log_level FFI entry point, exposed as
UniEnc.NativeLogging.SetLevel, changes the threshold at runtime. Several previously
commented-out per-frame diagnostic dumps are now live at trace! rather than requiring a source
edit to enable.

Behavior change on Android

set_stdout_redirect is removed. It dup2'd STDOUT_FILENO and STDERR_FILENO process-wide
from JNI_OnLoad, so every library's stdout/stderr in the process — including Unity's own —
was re-tagged as unienc in logcat, and it collided with any other library doing the same.

After this change, only UniEnc's own records carry the unienc tag. Anything that previously
appeared under unienc merely because it was written to stdout by unrelated code will no longer
appear there. Native code outside UniEnc that relied on this redirect to reach logcat at all
will now need its own logging.

Verification

cargo check -p unienc_c --features unity passes for aarch64-apple-darwin,
aarch64-apple-ios, aarch64-linux-android, x86_64-pc-windows-msvc, and
x86_64-unknown-linux-gnu. Builds without the feature pass for macOS, Windows, Linux, and wasm.
cargo fmt --check is clean and Cargo.lock gains no new dependencies (log was already
present transitively, so THIRD-PARTY-NOTICES.md is unchanged).

Native binaries need a build-unienc.yml run before this is usable from the package.

ruccho and others added 2 commits August 19, 2026 14:52
Replace every println!/eprintln! across the unienc crates with the `log`
facade, and install a single log::Log implementation in unienc_c that picks
a sink per record:

- Android: __android_log_write under the `unienc` tag, regardless of the
  `unity` feature.
- Unity (non-Android): IUnityLog, once UnityPluginLoad has provided the
  plugin interfaces.
- Otherwise: stdout/stderr.

Remove set_stdout_redirect, which redirected the process-wide stdout and
stderr into logcat from JNI_OnLoad. It captured output from every other
library in the process and collided with anything else doing the same.

Add unienc_set_log_level so the managed side can change the threshold; the
default is Info for release builds and Debug for debug builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The IUnityLog sink added in the previous commit was unreachable on Windows
and Linux: build-unienc.yml passed only -F mimalloc for their `unity`
variant, so those players fell back to println and nothing reached the Unity
console.

-F unity did not compile there. `PlatformEncodingSystem::unity_plugin_load`
is called unconditionally from unienc_c, but `UnityPlugin` was implemented
only by the Metal and Vulkan backends, which are the ones with a rendering
integration. Add empty implementations for Media Foundation, FFmpeg, and
WebCodecs so the trait's no-op defaults apply.

Each backend gets its own `unity` feature forwarding to `unienc_common/unity`
rather than depending on it unconditionally, so non-Unity builds of those
crates keep excluding unity-native-plugin.

wasm stays on --no-default-features: unity-native-plugin-sys 0.9.0 asserts
64-bit struct layouts and cannot compile for a 32-bit target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

Organizing native logging

1 participant