@open-kingdom/shared-frontend-data-access-logger
v0.0.2-16
Published
Redux-based client-side logging infrastructure providing a slice for storing log entries in state, action creators for different log levels, parameterized selectors, and two RTK listener middleware factories — one that mirrors logs to the browser console
Readme
@open-kingdom/shared-frontend-data-access-logger
Redux-based client-side logging infrastructure providing a slice for storing log entries in state, action creators for different log levels, parameterized selectors, and two RTK listener middleware factories — one that mirrors logs to the browser console and one that POSTs logs to a remote endpoint via a configurable callback.
Exports
Slice
| Export | Type | Description |
| --------------- | ---------------------------------------------------- | ------------------------------------------------------------------------- |
| loggerSlice | Slice<LoggerState> | Redux slice, name: 'logger' |
| loggerReducer | Reducer<LoggerState> | loggerSlice.reducer — add to store under LoggerKey |
| addLog | ActionCreator<Omit<LogEntry, 'id' \| 'timestamp'>> | Low-level action to add a log entry (id and timestamp are auto-generated) |
| clearLogs | ActionCreator<void> | Removes all log entries from state |
Actions (convenience helpers)
| Export | Type | Description |
| --------------------------- | -------------- | ----------------------------------------- |
| logInfo(message: string) | () => Action | Dispatches addLog with level: 'info' |
| logWarn(message: string) | () => Action | Dispatches addLog with level: 'warn' |
| logError(message: string) | () => Action | Dispatches addLog with level: 'error' |
Selectors
| Export | Type | Description |
| ------------------- | ----------------------------------------------------------- | --------------------------------------------------------------- |
| selectLoggerState | (state: RootStateContainingLogger) => LoggerState | Selects the full logger slice |
| selectLogs | (state: RootStateContainingLogger) => LogEntry[] | Selects all log entries |
| selectLogsByLevel | (state, level: 'info' \| 'warn' \| 'error') => LogEntry[] | Selects entries filtered by level |
| selectRecentLogs | (state, count: number) => LogEntry[] | Selects the last count entries; returns [] if count === 0 |
Middleware
| Export | Type | Description |
| ------------------------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| createConsoleLoggerMiddleware() | () => Middleware | RTK listener middleware that mirrors each addLog dispatch to console.info/warn/error |
| createHttpLoggerMiddleware(config?) | (config?: HttpLoggerConfig) => Middleware | RTK listener middleware that calls config.onSend for each addLog dispatch |
Types
| Export | Type | Description |
| --------------------------- | ----------- | --------------------------------------------------------------------------------------- |
| LoggerKey | 'logger' | String constant for the reducer path |
| LogEntry | interface | Shape of a stored log entry |
| LoggerState | interface | Shape of the logger slice state |
| RootStateContainingLogger | type | Type helper for typed selectors: { [key: string]: unknown } & { logger: LoggerState } |
| HttpLoggerConfig | interface | Config for createHttpLoggerMiddleware |
| LogPayload | type | Omit<LogEntry, 'id' \| 'timestamp'> — the shape sent to onSend |
| AppDispatch | type | ThunkDispatch<unknown, unknown, UnknownAction> — passed to onSend |
Type Definitions
LogEntry
| Property | Type | Required | Description |
| ----------- | ----------------------------- | ---------- | ------------------------------------------- |
| id | string | Yes (auto) | Auto-generated from Date.now().toString() |
| message | string | Yes | The log message text |
| level | 'info' \| 'warn' \| 'error' | Yes | Severity level |
| timestamp | number | Yes (auto) | Auto-generated from Date.now() |
LoggerState
| Property | Type | Description |
| -------- | ------------ | ---------------------- |
| logs | LogEntry[] | All stored log entries |
HttpLoggerConfig
| Property | Type | Required | Default | Description |
| --------- | ------------------------------------------------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| onSend | (logEntry: LogPayload, dispatch?: AppDispatch) => void \| Promise<void> | No | — | Called for each log entry dispatched to the store. If not provided, logs are only stored in Redux (no HTTP sending) |
| onError | (error: unknown, logEntry: LogPayload) => void | No | Silent | Called when onSend throws. If not provided, errors are silently ignored to prevent infinite error-logging loops |
Setup
Store Registration
import { configureStore } from '@reduxjs/toolkit';
import { LoggerKey, loggerReducer, createConsoleLoggerMiddleware, createHttpLoggerMiddleware } from '@open-kingdom/shared-frontend-data-access-logger';
export const store = configureStore({
reducer: {
[LoggerKey]: loggerReducer, // 'logger'
},
middleware: (getDefault) =>
getDefault()
// Mirror logs to browser console:
.concat(createConsoleLoggerMiddleware())
// Send logs to a remote API endpoint:
.concat(
createHttpLoggerMiddleware({
onSend: async (log) => {
await fetch('/api/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(log),
});
},
onError: (error, log) => {
console.error('Failed to send log', error, log);
},
})
),
});Usage Examples
Dispatching logs
import { logInfo, logWarn, logError } from '@open-kingdom/shared-frontend-data-access-logger';
import { useDispatch } from 'react-redux';
function MyComponent() {
const dispatch = useDispatch();
const handleLogin = () => {
dispatch(logInfo('User logged in successfully'));
};
const handleApiError = (err: unknown) => {
dispatch(logError(`API call failed: ${String(err)}`));
};
const handleSlowRequest = () => {
dispatch(logWarn('Request taking longer than expected'));
};
}Reading logs from state
import { selectLogs, selectLogsByLevel, selectRecentLogs } from '@open-kingdom/shared-frontend-data-access-logger';
import { useSelector } from 'react-redux';
function LogViewer() {
const allLogs = useSelector(selectLogs);
const errorLogs = useSelector((state) => selectLogsByLevel(state, 'error'));
const last10 = useSelector((state) => selectRecentLogs(state, 10));
}HTTP logging with RTK Query dispatch
import { createHttpLoggerMiddleware } from '@open-kingdom/shared-frontend-data-access-logger';
import { myLoggingApi } from './my-logging-api';
createHttpLoggerMiddleware({
onSend: (log, dispatch) => {
// dispatch is the Redux ThunkDispatch — use with RTK Query mutations:
dispatch?.(myLoggingApi.endpoints.sendLog.initiate(log));
},
});Low-level addLog action
import { addLog } from '@open-kingdom/shared-frontend-data-access-logger';
// addLog accepts Omit<LogEntry, 'id' | 'timestamp'>
dispatch(addLog({ message: 'Custom log entry', level: 'info' }));Console Middleware Behavior
createConsoleLoggerMiddleware() listens for addLog actions and calls the corresponding console method:
| Level | Console method | Format |
| --------- | --------------- | ------------------- |
| 'info' | console.info | [INFO] <message> |
| 'warn' | console.warn | [WARN] <message> |
| 'error' | console.error | [ERROR] <message> |
Testing
nx test shared-frontend-data-access-logger