← All reusables

withLogger

extension

Installation

npx jsrepo add github/reatom/reusables withLogger

Documentation

withLogger

Atom/action extension that logs all state changes and action calls.

withLogger(logger?)

Adds logging middleware that intercepts all updates and calls the provided logger function.

Parameters

Parameter Type Description
logger LoggerFn Logger function (defaults to console.log)

LogEntry

The logger function receives a LogEntry object:

Property Type Description
name string Name of the atom or action
params unknown[] Parameters passed to the action or setter
result unknown Result value (new state or return value)
timestamp number Timestamp of the log entry

Example

import { atom } from '@reatom/core'
import { withLogger } from '#reatom/extension/with-logger'

const counter = atom(0, 'counter').extend(withLogger())

counter.set(5) // Logs: [counter] [5] -> 5
counter.set(10) // Logs: [counter] [10] -> 10

Custom logger example

import { withLogger, type LogEntry } from '#reatom/extension/with-logger'

const logs: LogEntry[] = []

const counter = atom(0, 'counter').extend(
  withLogger((entry) => {
    logs.push(entry)
    console.log(`${entry.name} changed to ${entry.result}`)
  }),
)

counter.set(42) // Logs: "counter changed to 42"

Action logging

import { action } from '@reatom/core'
import { withLogger } from '#reatom/extension/with-logger'

const greet = action((name: string) => `Hello, ${name}!`, 'greet').extend(
  withLogger(),
)

greet('World') // Logs: [greet] ["World"] -> "Hello, World!"

Analytics integration

const analyticsLogger = (entry: LogEntry) => {
  analytics.track('state_change', {
    action: entry.name,
    params: entry.params,
    result: entry.result,
    timestamp: entry.timestamp,
  })
}

const settings = atom({ theme: 'light' }, 'settings').extend(
  withLogger(analyticsLogger),
)

Composing with other extensions

import { withReset } from '#reatom/extension/with-reset'
import { withLogger } from '#reatom/extension/with-logger'

const form = atom({ name: '', email: '' }, 'form')
  .extend(withReset({ name: '', email: '' }))
  .extend(withLogger())

form.set({ name: 'John', email: 'john@example.com' }) // Logged
form.reset() // Also logged

Example

import { atom, action, effect } from '@reatom/core'

import { withLogger, type LogEntry } from './with-logger'

// --- Basic usage with default console logger ---

const counter = atom(0, 'counter').extend(withLogger())

counter.set(5) // Logs: [counter] [5] -> 5
counter.set(10) // Logs: [counter] [10] -> 10

// --- Custom logger function ---

const logs: LogEntry[] = []

const customLogger = (entry: LogEntry) => {
  logs.push(entry)
  console.log(`${entry.name} changed to ${entry.result} at ${entry.timestamp}`)
}

const message = atom('', 'message').extend(withLogger(customLogger))

message.set('Hello') // Logs with timestamp
message.set('World')

console.log('All logs:', logs)

// --- Logging actions ---

const greet = action((name: string) => `Hello, ${name}!`, 'greet').extend(
  withLogger(),
)

greet('Reatom') // Logs: [greet] ["Reatom"] -> "Hello, Reatom!"

// --- Analytics integration example ---

const analyticsLogger = (entry: LogEntry) => {
  // Send to analytics service
  // analytics.track('state_change', {
  //   action: entry.name,
  //   params: entry.params,
  //   result: entry.result,
  //   timestamp: entry.timestamp
  // })
  console.log('Analytics:', entry)
}

const settings = atom({ theme: 'light' }, 'settings').extend(
  withLogger(analyticsLogger),
)

settings.set({ theme: 'dark' })

// --- Conditional logging ---

const DEBUG = true // or use import.meta.env.DEV in Vite

const debugLogger = (entry: LogEntry) => {
  if (DEBUG) {
    console.debug(`[DEBUG] ${entry.name}:`, entry.params, '->', entry.result)
  }
}

const config = atom({}, 'config').extend(withLogger(debugLogger))

// --- Composing with other extensions ---

import { withReset } from '../reset/with-reset'

const form = atom({ name: '', email: '' }, 'form')
  .extend(withReset({ name: '', email: '' }))
  .extend(withLogger())

form.set({ name: 'John', email: 'john@example.com' })
form.reset() // Both set and reset are logged