core/redux-action-logging
Use when configuring logReduxActions or inspecting dispatch logs, state diffs, and reduxAction stream events across Store families. Owns shared traceStreams/loggerFactory lifecycle.
SKILL.mdskills/core/redux-action-logging/SKILL.md
GitHubRedux action logging
Use this skill when an agent needs to diagnose a dispatch by reading the Redux action logger. This is a construction-time diagnostic for the shared Store runtime; it is separate from selector tracing and from the browser devtools inspection API.
1. Enable it for a focused reproduction
Pass logReduxActions: true in the third Store constructor argument. Pass
undefined as the middleware argument when no middleware is configured.
import { Store } from '@themislib/themis/svelte-store';
import { ReactStore } from '@themislib/themis/react-store';
import { StreamingStore } from '@themislib/themis/streaming-store';
const svelteStore = new Store(reducers, undefined, { logReduxActions: true });
const reactStore = new ReactStore(reducers, undefined, { logReduxActions: true });
const streamingStore = new StreamingStore(reducers, undefined, { logReduxActions: true });
Use the constructor corresponding to the app's Store family; do not combine
Svelte, React, and Streaming lifecycle patterns in one app. The option is
shared by all three families and is disabled when omitted or set to false.
Store-owned logging streams
The Store exposes a frozen traceStreams collection of six read-only Kefir streams:
selectorDetail, selectorSummary, selectorCadence, sagaMonitor,
runtimeError, and reduxAction. The collection exposes no emitters and does not
permit consumers to publish events. Public StoreTraceStreams and
StoreLoggerFactory types are available from @themislib/themis/types and
re-exported by each Store-family entrypoint.
When logReduxActions: true, reduxAction is produced by pure middleware:
it calls next(action) before publishing one shallow-immutable event with the
action, previous/next state references, and stateChanged. Errors and return
values from next are preserved, and failed dispatches do not publish an event.
The event does not eagerly compute a state diff; default rendering computes it
lazily as described in Read one action's group below. Action/state payloads
may contain application data; redact secrets and sensitive values before sharing.
Selector metadata has a separate privacy contract; follow Scope and safety rules.
2. Read one action's group
With no loggerFactory, StoreRuntime's default logger prints a one-time 🔧 Redux Logger Active legend, then renders each
dispatched action with console.groupCollapsed. Expand the action group before
interpreting it:
- Read the action title to identify the dispatch. Primitive payloads, and a one-element array containing a primitive, may be included in the title; complex payloads are intentionally not rendered in the title.
- Read the styled
actionrecord (%c action) to see the dispatched action, then the styled state record. The CSS/style argument attached to a console call controls presentation only; it is not part of the action or state data. The blue action label and green state label are visual legend entries, not extra fields. - A gray
state (no changes)record contains{ state: nextState }and means the reducer returned the same state reference. It is a successful no-change dispatch, not missing logger output and not proof that the action was rejected. The group title is gray and lighter for this case; changed-state titles are bold. - For a changed state, expand the
staterecord and then itschangesproperty. It is a path-keyed diff: each key is a state path and its value contains the previous and next value for that path. The diff is lazy, so inspect or expand it in the console only when needed rather than assuming the logger eagerly captured a full state snapshot.
The title uses color: inherit; font-weight: 600 for changed state and
color: #9E9E9E; font-weight: 300 for unchanged state. Record labels use blue
for action, green for changed state, and gray/lighter styling for state (no changes); these styles are presentation hints only.
Treat paths and values in changes as diagnostic evidence for the current
Store instance. Redact sensitive values before sharing logs. Do not infer
changes from the group title alone.
3. Keep logging opt-in and temporary
There is no dev-mode switch, localStorage toggle, global debug-console toggle,
or runtime enable/disable API for action logging. The pure logger middleware and
default action rendering are enabled only when the normalized constructor option
is true; omitted and false options do not publish action events or enable
action console groups. Other diagnostic streams have independent options.
For default/custom rendering, factory attachment, and cleanup, follow Logger factory lifecycle.
Selector aggregation is independent of action logging. For summaryEnabled
and collector allocation/publication, follow
Aggregate summaries.
To disable logging, omit the option or set logReduxActions: false and
construct a new Store instance. Changing an options object, calling init()
again, or disposing/reusing the existing instance does not reconfigure its
middleware pipeline.
After reproducing the issue, dispose the diagnostic Store through its normal
family lifecycle and remove the temporary true option from application code.
Logger factory lifecycle
With no loggerFactory, StoreRuntime attaches its default console logger,
preserving severity and [themis] diagnostic prefixes; enabled action logging
uses the legend/groups described in Read one action's group above.
Pass a typed loggerFactory to replace the default stream-subscriber logger. It
receives only this Store instance's six read-only streams and may return one
disposer; the built-in legend and default subscriber rendering do not attach.
This is not a global console-silencing or privacy boundary: with
traceSelectors.summaryEnabled: true, the runtime still prints an eligible,
non-empty period aggregate directly, independently of the custom logger. See
Aggregate summaries.
import type { StoreLoggerFactory, StoreOptions } from '@themislib/themis/types';
const loggerFactory: StoreLoggerFactory = (streams) => {
const subscription = streams.runtimeError.observe(reportRuntimeError);
return () => subscription.unsubscribe();
};
const options: StoreOptions = {
loggerFactory,
logReduxActions: true, // events still publish; default action groups do not
traceSelectors: { summaryEnabled: true, summaryIntervalMs: 1000 },
}; // non-empty selector period aggregates still reach console.info
Pass options as the third Store constructor argument. reportRuntimeError is
your app-owned reporter; redact sensitive data there. Omit summaryEnabled or
set it to false at construction if these runtime-owned aggregates are unwanted;
redacting in the custom logger does not redact that separate console path. The factory
attaches during initialization; its disposer runs during store.dispose() and
before a later successful initialization attaches it again. Dispose custom stream
subscriptions in that callback and retain the Store initializer's disposer for
the end of the owning Store lifetime.
4. Common mistakes
- Do not look for a localStorage key or development-mode gate; neither controls this option.
- Do not treat CSS style strings as logger payloads.
- Do not call the unchanged-state record a logger failure.
- Do not request a complete before/after state dump when a path in
changesanswers the question; expand only the relevant lazy diff entries. - Do not use
traceSelectorsto enable action logging. Selector tracing has a separate contract and option. - Do not assume a custom logger silences selector aggregates;
summaryEnabledcontrols that separate runtime-owned output.
See also
../debugging/SKILL.md— Store inspection and lifecycle boundaries.../selector-tracing/SKILL.md— selector diagnostics, which are separate from Redux dispatch logging.