Effector Logger

repository·master·Indexed 41 days ago

https://github.com/effector/logger

A utility for providing pretty, informative logs for Effector units such as stores, events, effects, and domains in the browser console. It includes features like attachLogger for enabling logs, configure for controlling unit visibility, and createDeclarationsLogger for tracking initialization. Supports metadata via Babel or SWC plugins to provide exact code locations and names in logs.

Tokens
2.5K
Snippets
12
Records
13
Agent score
51%

What's inside Effector Logger

  1. Install effector-logger

    master

    Install effector-logger as a development dependency. Note that effector must also be installed in your project.

    npm add effector
    npm add --dev effector-logger

    or

    yarn add effector
    yarn add -D effector-logger
  2. Prepare metadata for better logs

    master

    To make logs more useful (including names and code locations), you must use an Effector plugin to provide metadata.

    Babel-plugin

    Add effector/babel-plugin to your Babel configuration. To see exact code locations in your development environment, enable addLoc.

    SWC Plugin

    Use the effector SWC plugin for SWC-based environments.

    {
      "plugins": [["effector/babel-plugin", { "addLoc": true }]]
    }
  3. Configure attachLogger with Scope and Name

    master

    You can pass an options object to attachLogger to customize the output:

    • scope: If your app uses Scope (e.g., in SSR), pass the scope instance. Updates related to this scope will be prefixed with the scope ID.
    • name: An optional string prefix for all logs. This is useful for distinguishing between multiple app instances or providing a more readable identifier than a scope ID.
    attachLogger({
      scope,
      name: `my-cool-app-${appId}`, // all logs will be prefixed with this string
    });
  4. Start logging with attachLogger

    master

    To enable logging, call attachLogger() in your entrypoint module.

    CRITICAL: attachLogger must be imported at the very top of your entrypoint module to ensure that initial states of stores (created via createStore, createEvent, etc.) are properly logged.

    // src/index.tsx
    import { attachLogger } from 'effector-logger';
    
    import React from 'react';
    import { createRoot } from 'react-dom/client';
    import { App } from './app';
    import { appStarted } from './shared/app-events';
    
    attachLogger();
    
    appStarted();
    
    createRoot(document.querySelector('#app')).render(<App />);
  5. Hide or force logging for specific units

    master

    Use the configure function to control which units appear in the logs. You can pass a single unit or an array of units.

    • Hide a unit: Set log: 'disabled'.
    • Force a unit to be logged: By default, only non-derived units are logged. To force a derived unit (like a mapped unit) to be logged, set log: 'enabled'.
    import { createEvent } from 'effector';
    import { configure } from 'effector-logger';
    import { $data, loadDataFx } from './model';
    
    const pageMounted = createEvent<number>();
    
    // Hide units
    configure(pageMounted, { log: 'disabled' });
    configure([$data, loadDataFx], { log: 'disabled' });
    
    // Force derived units to be logged
    const mappedMounted = pageMounter.map((x) => x);
    configure(mappedMounted, { log: 'enabled' });
  6. Monitor store updates with storeUpdated

    master

    Call storeUpdated to log whenever a store's value changes. The log entry includes the new value, the file where the store is defined, and the store's name.

    import { storeUpdated } from 'effector-logger';
    
    // Example usage within a subscription or middleware
    storeUpdated('myStore', 'src/store.ts', newValue);
  7. Initialize the declarations logger with createDeclarationsLogger

    master

    Use createDeclarationsLogger to log the initialization of stores, events, and effects. This function uses a debounced mechanism to collect all declarations that were not explicitly named during the initial graph inspection and logs them in a single grouped console output.

    Configuration

    • name: A string used as a prefix or context for the logged units.
    • scope (optional): An Effector Scope instance used to retrieve the initial state of stores during initialization logging.
    import { createDeclarationsLogger } from 'effector-logger';
    
    // Initialize the logger
    const logger = createDeclarationsLogger({ 
      name: 'my-app', 
      scope: myScope 
    });
    
    // Note: The logger is debounced, so it will trigger after the initial execution context.
  8. Monitor event calls with eventCalled

    master

    Call eventCalled to log when an event is triggered. The log entry includes the event payload, the file where the event is defined, and the event's name.

    import { eventCalled } from 'effector-logger';
    
    // Example usage
    eventCalled('myEvent', 'src/events.ts', payload);
  9. Configure logging for specific units

    master

    Use configure() to explicitly enable or disable logging for specific Effector units (stores, events, or effects). This is useful for reducing noise in the console by hiding specific units or forcing the logging of derived units.

    Arguments:

    • unitOrUnits: A single Unit or an array of Units.
    • config: An object with a log property set to either 'enabled' or 'disabled'.

    Note: If you disable an Effect, its finally unit is also automatically disabled to ensure consistent lifecycle logging.

    import { configure } from 'effector-logger';
    import { myStore, myEvent, myEffect } from './model';
    
    // Disable logging for specific units
    configure([myStore, myEvent], { log: 'disabled' });
    
    // Force logging for specific units (including derived ones)
    configure(myEffect, { log: 'enabled' });
  10. Attach the logger to your application

    master

    Use attachLogger() to enable logging for your Effector application. It sets up listeners for unit updates, event calls, and effect lifecycles.

    It accepts an optional configuration object:

    • scope: An Effector Scope to limit logging to a specific scope.
    • name: A custom string to prefix your logs.

    attachLogger() returns a cleanup function. You should call this returned function when you want to stop logging (e.g., during teardown or when switching environments) to prevent memory leaks and stop the inspection process.

    import { attachLogger } from 'effector-logger';
    
    // Attach logger to the global scope
    const stopLogging = attachLogger();
    
    // Or attach to a specific scope with a custom name
    const stopLoggingWithScope = attachLogger({ 
      scope: myScope, 
      name: 'my-app' 
    });
    
    // When done:
    stopLogging();