logger plugin - greydragon888/real-router GitHub Wiki

@real-router/logger-plugin

1. Overview

  • Name: Logger Plugin
  • Package: @real-router/logger-plugin
  • Purpose: Logging router events using native console API for debugging and navigation monitoring.
  • Typical scenarios: Debugging transitions in development mode; monitoring navigation performance; analyzing route parameter changes.
  • Note: Uses native console methods (log, warn, error) instead of external logger package.

2. Installation and Setup

npm install @real-router/logger-plugin
# or
pnpm add @real-router/logger-plugin
import { createRouter } from "@real-router/core";
import { loggerPluginFactory } from "@real-router/logger-plugin";

const router = createRouter(routes);

router.usePlugin(loggerPluginFactory());

3. Configuration Options

The plugin uses default configuration from DEFAULT_CONFIG. All logging is done via native console methods.

Option Type Default Description
level LogLevel "all" Logging level: "all", "transitions", "errors", "none"
usePerformanceMarks boolean false Create Performance API marks/measures for DevTools
showTiming boolean true Show transition execution time
showParamsDiff boolean true Show diff of changed parameters when navigating within same route
context string "logger-plugin" Context for console messages (useful with multiple routers)

Types

type LogLevel = "all" | "transitions" | "errors" | "none";

interface LoggerPluginConfig {
  usePerformanceMarks?: boolean;
  level?: LogLevel;
  showTiming?: boolean;
  showParamsDiff?: boolean;
  context?: string;
}

Configuration Examples

// Minimal configuration (all default values)
router.usePlugin(loggerPluginFactory());

// With custom context for multiple routers
router.usePlugin(
  loggerPluginFactory({
    context: "main-router",
  }),
);

// Performance profiling enabled
router.usePlugin(
  loggerPluginFactory({
    usePerformanceMarks: true,
    showTiming: true,
  }),
);

// Errors only (for production-like environments)
router.usePlugin(
  loggerPluginFactory({
    level: "errors",
    showTiming: false,
  }),
);

// Minimal output
router.usePlugin(
  loggerPluginFactory({
    level: "transitions",
    showParamsDiff: false,
    showTiming: false,
  }),
);

4. Lifecycle Hooks

The plugin implements all lifecycle hooks:

Hook Implemented Description
onStart βœ… Logs "Router started", creates performance mark
onStop βœ… Logs "Router stopped", creates measure router:lifetime
onTransitionStart βœ… Opens console.group, logs transition start, shows params diff
onTransitionSuccess βœ… Logs success with timing, closes group
onTransitionError βœ… Logs error with code and timing, closes group
onTransitionCancel βœ… Logs cancellation with timing, closes group
teardown βœ… Closes open groups, resets timing

Hook Implementation Details

onStart

  • Creates performance mark router:start
  • Logs "Router started"

onStop

  • Closes open console groups
  • Creates performance mark router:stop
  • Creates measure router:lifetime between start and stop
  • Logs "Router stopped"

onTransitionStart

  • Opens console.group "Router transition"
  • Records start time for timing
  • Creates mark router:transition-start:{from}β†’{to}
  • Logs "Transition: {from} β†’ {to}" with from/to objects
  • Calls logParamsDiff if route hasn't changed

onTransitionSuccess

  • Creates mark router:transition-end:{label}
  • Creates measure router:transition:{label}
  • Logs "Transition success (X.XXms)"
  • Closes console.group
  • Resets timing

onTransitionCancel

  • Creates mark router:transition-cancel:{label}
  • Creates measure router:transition-cancelled:{label}
  • Logs warning "Transition cancelled (X.XXms)"
  • Closes console.group
  • Resets timing

onTransitionError

  • Creates mark router:transition-error:{label}
  • Creates measure router:transition-failed:{label}
  • Logs error "Transition error: {code} (X.XXms)" with error/stack
  • Closes console.group
  • Resets timing

teardown

  • Closes open console groups
  • Resets transitionStartTime

5. Router Interaction

Router Interface Extension

The plugin does not extend the Router interface. It's a purely observational plugin.

Used Router Methods

Method Purpose
- Plugin doesn't call router methods

Data Received from Router

Data Description
toState Target transition state
fromState Source transition state
err Error object in onTransitionError

6. Side Effects

Console APIs

  • console.log: For regular messages (router start/stop, transitions)
  • console.warn: For transition cancellations and warnings
  • console.error: For transition errors
  • console.group / console.groupEnd: Grouping transition messages

Performance API

When usePerformanceMarks is enabled:

  • performance.mark(): Creating marks
  • performance.measure(): Creating measurements between marks

Console Groups Logic

  • Group opens on onTransitionStart
  • Group closes on onTransitionSuccess, onTransitionCancel, onTransitionError, onStop, teardown
  • Double group opening is prevented
  • Closing unclosed group is prevented

7. Integration with Other Plugins

Plugin Interaction
@real-router/browser-plugin Works correctly, logs all transitions including popstate
@real-router/persistent-params-plugin Shows persistent params in diff
Custom plugins Logs all transitions regardless of source

Execution Order

Logger plugin is usually registered first to capture all events:

router.usePlugin(loggerPluginFactory()); // First
router.usePlugin(browserPluginFactory());
router.usePlugin(persistentParamsPluginFactory(["lang"]));

8. Behavior

Main Scenarios

  • Router start/stop: Logged with performance marks
  • Transition success: Logged with timing and states
  • Transition error: Logged with error code, stack trace and timing
  • Transition cancel: Logged as warning with timing
  • Nested routes: Full route name (e.g., "users.view")
  • Parameters: Included in logged state objects

Timing Format

  • Microseconds (ΞΌs) for time < 0.1ms
  • Milliseconds (ms) for time β‰₯ 0.1ms
  • Format: (X.XXΞΌs) or (X.XXms)
  • (?) for invalid time

Params Diff Format

When navigating within same route with different parameters:

Changed: { id: "123" β†’ "456" }
Added: {"newParam": "value"}
Removed: {"oldParam": "value"}

Edge Cases

  • Missing console.group: Graceful degradation, logging continues
  • Missing console: Doesn't cause errors
  • Rapid transitions: Correctly handles multiple transitions
  • Router restart: Correctly logs repeated start
  • 100 transitions: Stable operation without leaks

Guarantees

  • No side effects on router: Plugin only reads, doesn't modify router
  • Group balance: Groups always close (success, error, cancel, stop, teardown)
  • Timing reset: Timing resets after each transition
  • Single group: Multiple groups don't open for one transition

9. Limitations and Known Issues

  • Production: Recommended to disable (level: "none") or use level: "errors" in production for performance
  • usePerformanceMarks: Disabled by default, requires explicit enabling
  • Shallow diff: Params diff performs only shallow comparison
  • Performance API: Safe fallback if Performance API unavailable

10. Performance Marks and Measures

When usePerformanceMarks: true:

Marks

Mark When Created
router:start On router start
router:stop On router stop
router:transition-start:{from}β†’{to} On transition start
router:transition-end:{from}β†’{to} On successful completion
router:transition-cancel:{from}β†’{to} On transition cancellation
router:transition-error:{from}β†’{to} On transition error

Measures

Measure Start End
router:lifetime router:start router:stop
router:transition:{label} transition-start transition-end
router:transition-cancelled:{label} transition-start transition-cancel
router:transition-failed:{label} transition-start transition-error

11. Usage Examples

Basic Example

import { createRouter } from "@real-router/core";
import { loggerPluginFactory } from "@real-router/logger-plugin";

const routes = [
  { name: "home", path: "/" },
  { name: "users", path: "/users" },
  { name: "users.view", path: "/:id" },
];

const router = createRouter(routes);

router.usePlugin(loggerPluginFactory());
await router.start();

router.navigate("users");
// Console output:
// β–Ό Router transition
//   [logger-plugin] Transition: home β†’ users {from: {...}, to: {...}}
//   [logger-plugin] Transition success (0.15ms) {to: {...}, from: {...}}

router.navigate("users.view", { id: "123" });
// Console output:
// β–Ό Router transition
//   [logger-plugin] Transition: users β†’ users.view {from: {...}, to: {...}}
//   [logger-plugin] Transition success (0.12ms) {to: {...}, from: {...}}

router.navigate("users.view", { id: "456" });
// Console output:
// β–Ό Router transition
//   [logger-plugin] Transition: users.view β†’ users.view {from: {...}, to: {...}}
//   [logger-plugin]   Changed: { id: "123" β†’ "456" }
//   [logger-plugin] Transition success (0.08ms) {to: {...}, from: {...}}

With Performance API

import { createRouter } from "@real-router/core";
import { loggerPluginFactory } from "@real-router/logger-plugin";

const router = createRouter(routes);

// Performance marks visible in DevTools Performance tab
router.usePlugin(
  loggerPluginFactory({
    usePerformanceMarks: true,
  }),
);

await router.start();
router.navigate("users");
router.stop();

// In DevTools Performance tab you'll see:
// - router:start
// - router:transition-start:home→users
// - router:transition-end:home→users
// - router:transition:home→users (measure)
// - router:stop
// - router:lifetime (measure)

12. Migration from router5

Comparison of plugin versions (master β†’ current).

Version Comparison

Master Current
Factory signature loggerPlugin (constant PluginFactory) loggerPluginFactory(options?)
Options None LoggerPluginConfig (5 options)
Export export default loggerPlugin export { loggerPluginFactory }
Structure Single file index.ts Modular structure (factory.ts, plugin.ts, types.ts, internal/*)

1. Breaking Changes

Severity Levels:

  • CRITICAL β€” Plugin will definitely break
  • HIGH β€” Plugin will likely break
  • MEDIUM β€” Plugin may break in some cases
  • LOW β€” Backward compatible, but needs attention

Changes Table

Severity What Changed Was Now Impact
CRITICAL Export method export default loggerPlugin export { loggerPluginFactory } Import will break
LOW Message format "Router started", "Transition started from state" "[logger-plugin] Router started", etc. (context) Log appearance will change

Examples

// ❌ Code that will break (import)
import loggerPlugin from "@real-router/logger-plugin";

// βœ… Code after migration
import { loggerPluginFactory } from "@real-router/logger-plugin";
router.usePlugin(loggerPluginFactory());

2. Implementation Changes

New Options

  • usePerformanceMarks (boolean, default: false): Create Performance API marks/measures
  • level (LogLevel, default: "all"): Logging level
  • showTiming (boolean, default: true): Show execution time
  • showParamsDiff (boolean, default: true): Show params diff
  • context (string, default: "logger-plugin"): Context for logger

Removed Options

  • None (master had no options)

Lifecycle Hook Changes

Hook Master Current Change
onStart ❌ Not implemented (logging in constructor) βœ… Implemented Added hook, Performance mark
onStop βœ… console.info("Router stopped") βœ… Restructured + Performance mark/measure, context prefix
onTransitionStart βœ… Basic log βœ… Extended + Timing, params diff, Performance marks, improved format
onTransitionSuccess βœ… console.log("Transition success") βœ… Extended + Timing, Performance marks, states in object
onTransitionCancel βœ… console.warn("Transition cancelled") βœ… Extended + Timing, Performance marks, states
onTransitionError βœ… console.warn("...code") βœ… Extended + Timing, Performance marks, stack trace, console.error
teardown ❌ Not implemented βœ… Added Closing groups, resetting timing

New Features

  • Performance API integration: Marks and measures for DevTools Performance tab
  • Timing measurement: Transition execution time with adaptive units (ΞΌs/ms)
  • Params diff: Showing parameter changes when navigating within same route
  • Context prefix: All messages prefixed with [logger-plugin]
  • Console groups management: Protection from double open/close
  • Teardown hook: Proper resource cleanup
  • Modular structure: Split into internal modules

Fixed Bugs

  • Group balance: In master group was closed before onTransitionStart (wrong), now correct lifecycle
  • Console.groupCollapsed: In master groupCollapsed was preferred, now group is used
  • Missing onStart hook: In master "Router started" logging was in constructor, not in hook

Performance Improvements

  • Lazy group detection: console.group support check once on creation
  • Performance API conditional: Support check once, not on every call

3. Dependency Changes

Dependency Was Now
@real-router/core peer dependency workspace:^

4. Migration Guide

Checklist

  • Update import: import { loggerPluginFactory } from "@real-router/logger-plugin"
  • Replace router.usePlugin(loggerPlugin) with router.usePlugin(loggerPluginFactory())
  • Account for log format change (context at message start)
  • Account for new timing messages
  • If Performance API needed, pass usePerformanceMarks: true

Step-by-Step Migration

  1. Update import

    // Was
    import loggerPlugin from "@real-router/logger-plugin";
    
    // Now
    import { loggerPluginFactory } from "@real-router/logger-plugin";
    router.usePlugin(loggerPluginFactory());
    
  2. Adapt log parsing (if used)

    // Was
    // "Router started"
    // "Transition started from state"
    
    // Now
    // "[logger-plugin] Router started"
    // "[logger-plugin] Transition: home β†’ users"
    
  3. Use new features (optional)

    // Timing shown by default (showTiming: true)
    // Params diff shown by default (showParamsDiff: true)
    // Performance marks require explicit opt-in (usePerformanceMarks: true)
    

5. Summary

Category Status
Breaking Changes CRITICAL (import)
New options βœ… 5 options
Removed options βž– None
Hook changes βœ… Significant (all hooks extended)
New hooks βœ… onStart, teardown
Optimizations βœ… Lazy detection
Performance API βœ… Full integration

Maximum severity: CRITICAL β€” Import change required