Skip to content

Allow clients to provide a custom logger instance#159

Merged
jayohms merged 21 commits into
mainfrom
mb/custom_logger
Jul 2, 2026
Merged

Allow clients to provide a custom logger instance#159
jayohms merged 21 commits into
mainfrom
mb/custom_logger

Conversation

@mbarta

@mbarta mbarta commented Aug 21, 2025

Copy link
Copy Markdown
Collaborator

Summary

This branch replaces Hotwire Native's built-in, Logcat-only logging with a pluggable logging system. Previously the library logged directly to Android's Logcat and only offered a single on/off switch (debugLoggingEnabled) to control debug output. Apps had no way to capture library logs, route them to their own logging/crash-reporting stack, or control verbosity.

Now the library logs through a HotwireLogger interface. Apps can either keep the built-in default (which prints to Logcat), adjust the log level, or supply their own implementation to handle log messages however they like.

What changed

  • Introduced a HotwireLogger interface plus a HotwireLogLevel enum for filtering by severity.
  • Added a DefaultHotwireLogger that prints to Logcat, used automatically unless an app opts out.
  • All internal core and navigation logging now flows through the configured logger, including the OkHttp HTTP logging interceptor.
  • The HTTP logging interceptor is now always installed and simply defers to the logger to decide what (if anything) to emit, rather than being toggled on and off.
  • The demo app now demonstrates setting the log level based on the build type.

Public API changes

Removed

  • Hotwire.config.debugLoggingEnabled — the boolean flag no longer exists. Behavior is now controlled through the logger and its log level instead.

Added

  • Hotwire.config.logger: HotwireLogger — set this to provide a custom logger. Defaults to DefaultHotwireLogger.
  • HotwireLogger interface (public, implementable by apps).
  • HotwireLogLevel enum: VERBOSE, DEBUG, INFO, WARN, ERROR, NONE.
  • DefaultHotwireLogger — the built-in Logcat implementation.

Migration: replace Hotwire.config.debugLoggingEnabled = BuildConfig.DEBUG with a log-level assignment, e.g.:

Hotwire.config.logger.logLevel = if (BuildConfig.DEBUG) HotwireLogLevel.DEBUG else HotwireLogLevel.NONE

The HotwireLogger interface

Apps can implement this interface to receive every log message the library produces and handle it however they want — forward to their own logger, send to a crash reporter, filter, or drop entirely.

interface HotwireLogger {
    var logLevel: HotwireLogLevel
    fun v(tag: String, msg: () -> String)
    fun d(tag: String, msg: () -> String)
    fun i(tag: String, msg: () -> String)
    fun w(tag: String, msg: () -> String)
    fun e(tag: String, throwable: Throwable?, msg: () -> String)
}

Notes on the design:

  • Level-based filtering. logLevel lets consumers set a minimum severity. Setting it to NONE silences all output. The default logger uses this to decide what reaches Logcat, and custom loggers are expected to honor it too.
  • Lazy message providers. Messages are passed as lambdas (() -> String) so that potentially expensive log strings are only built when the message will actually be emitted.
  • Standard severity methods mirror Android's conventions: verbose, debug, info, warn, and error. The error method accepts an optional Throwable so exceptions can be logged with full context.

To use a custom logger:

Hotwire.config.logger = MyCustomLogger()

@mbarta mbarta self-assigned this Aug 21, 2025
@mbarta
mbarta requested review from jayohms and jhutarek August 21, 2025 15:43
@mbarta
mbarta marked this pull request as ready for review August 21, 2025 15:43

@jhutarek jhutarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks! I'd consider a couple of tweaks. 🙏

Comment thread core/src/main/kotlin/dev/hotwire/core/logging/DefaultHotwireLogger.kt Outdated
Comment thread core/src/main/kotlin/dev/hotwire/core/logging/HotwireLogger.kt
Comment thread core/src/main/kotlin/dev/hotwire/core/logging/HotwireLogger.kt Outdated
Comment thread core/src/main/kotlin/dev/hotwire/core/logging/DefaultHotwireLogger.kt Outdated
Comment thread core/src/main/kotlin/dev/hotwire/core/logging/CoreLog.kt Outdated
@zoejessica

Copy link
Copy Markdown

I started a draft PR to update the website here: hotwired/hotwire-native-site#91

* main:
  Add Session tests to cover visits with restorationIdentifier logic
  Fix restorationIdentifier + "restore" logic so that "replace" visit actions don't get unintentionally rewritten
  Fix test formatting
  Bring back the synethic current visit restore approach, since turbo.js does not always play nicely with same-page "restore" visits
  Fix documentation
  Maintain same-page "restore" visits and dispatch a custom event so the web bridge library can be notified when this occurs
  Fix restoring a web visit after visiting a native screen by manually caching a snapshot when leavin the web screen.
…t what the application wants logged. This also allows consumers to change the log level without writing a full custom logger implementation.
@jayohms

jayohms commented Nov 6, 2025

Copy link
Copy Markdown
Contributor

@mbarta @jhutarek Please take a look at the latest changes. By introducing a HotwireLogLevel into the HotwireLogger interface, we no longer need to make assumptions about what to log. We don't log anything by default. The consumer can explicitly set the Hotwire.config.logger.logLevel in their application without having to write a custom HotwireLogger implementation.

Comment thread core/src/main/kotlin/dev/hotwire/core/logging/CoreLog.kt Outdated
@mbarta

mbarta commented Nov 7, 2025

Copy link
Copy Markdown
Collaborator Author

Happy with the latest changes @jayohms! It's closer to a standard logger implementation where you can set the desired log level for the default logger while still being able to provide your own if more customisation is needed 👍 Thanks!

@jayohms jayohms added this to the v1.3.0 milestone Jan 13, 2026
jayohms added 2 commits July 2, 2026 12:51
* main: (44 commits)
  Add remaining code documentation for the HotwireBottomNavigationController
  Cleanup
  Check if isGraphInitialized in the host's isReady() function
  Improve the internal APIs for letting lazy navigator hosts
  Remove the intent trust gate and sanitize unconditionally
  Make the initControllerGraph() function private and add an explicit resetControllerGraph()
  Update the api name in the demo
  Change load load flag name
  Initial work to defer lazy-loaded NavigatorHost tabs until they're initially selected
  Gate deep-link sanitization on the launching intent's origin
  Validate deep-link args when resolving the start location
  Fix unhandled exceptions in route decision handlers
  Make WebView permission requests cancellation-safe
  Handle WebView media-capture permission requests in a delegate
  Use lowercase log event keys for consistency with other logging
  Remove unneeded CoroutinesTestRule
  Make PathConfiguration have an internal constructor since using it outside of the global instance is an error
  Upgrade OkHttp and implement the okhttp-coroutines dependency so fetching the remote configuration is cancellable
  Use sealed interfaces to simplify state setup
  Cancel the previous job before loading the bundled/cached config
  ...

# Conflicts:
#	demo/src/main/kotlin/dev/hotwire/demo/DemoApplication.kt
@jayohms
jayohms merged commit 6a4f929 into main Jul 2, 2026
1 check passed
@jayohms
jayohms deleted the mb/custom_logger branch July 2, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants