mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): clean up logging namespaces, allow skipping namespaces
This commit is contained in:
parent
e83513882a
commit
4da4b3bd50
@ -1106,7 +1106,6 @@
|
||||
"confirmOnDelete": "Confirm On Delete",
|
||||
"developer": "Developer",
|
||||
"displayInProgress": "Display Progress Images",
|
||||
"enableImageDebugging": "Enable Image Debugging",
|
||||
"enableInformationalPopovers": "Enable Informational Popovers",
|
||||
"informationalPopoversDisabled": "Informational Popovers Disabled",
|
||||
"informationalPopoversDisabledDesc": "Informational popovers have been disabled. Enable them in Settings.",
|
||||
@ -1817,5 +1816,30 @@
|
||||
"upscaling": "Upscaling",
|
||||
"upscalingTab": "$t(ui.tabs.upscaling) $t(common.tab)"
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"enableLogging": "Enable Logging",
|
||||
"logLevel": {
|
||||
"logLevel": "Log Level",
|
||||
"trace": "Trace",
|
||||
"debug": "Debug",
|
||||
"info": "Info",
|
||||
"warn": "Warn",
|
||||
"error": "Error",
|
||||
"fatal": "Fatal"
|
||||
},
|
||||
"logNamespaces": {
|
||||
"logNamespaces": "Log Namespaces",
|
||||
"gallery": "Gallery",
|
||||
"models": "Models",
|
||||
"config": "Config",
|
||||
"canvas": "Canvas",
|
||||
"generation": "Generation",
|
||||
"workflows": "Workflows",
|
||||
"system": "System",
|
||||
"events": "Events",
|
||||
"queue": "Queue",
|
||||
"metadata": "Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,24 +15,23 @@ export const BASE_CONTEXT = {};
|
||||
|
||||
export const $logger = atom<Logger>(Roarr.child(BASE_CONTEXT));
|
||||
|
||||
export type LoggerNamespace =
|
||||
| 'images'
|
||||
| 'models'
|
||||
| 'config'
|
||||
| 'canvas'
|
||||
| 'generation'
|
||||
| 'nodes'
|
||||
| 'system'
|
||||
| 'socketio'
|
||||
| 'session'
|
||||
| 'queue'
|
||||
| 'dnd'
|
||||
| 'controlLayers'
|
||||
| 'metadata'
|
||||
| 'konva'
|
||||
| 'worker';
|
||||
export const zLogNamespace = z.enum([
|
||||
'gallery',
|
||||
'models',
|
||||
'config',
|
||||
'canvas',
|
||||
'generation',
|
||||
'workflows',
|
||||
'system',
|
||||
'events',
|
||||
'queue',
|
||||
'metadata',
|
||||
]);
|
||||
const zLogNamespacesArray = z.array(zLogNamespace);
|
||||
export type LogNamespace = z.infer<typeof zLogNamespace>;
|
||||
export const isLogNamespaceArray = (v: unknown): v is LogNamespace[] => zLogNamespacesArray.safeParse(v).success;
|
||||
|
||||
export const logger = (namespace: LoggerNamespace) => $logger.get().child({ namespace });
|
||||
export const logger = (namespace: LogNamespace) => $logger.get().child({ namespace });
|
||||
|
||||
export const zLogLevel = z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal']);
|
||||
export type LogLevel = z.infer<typeof zLogLevel>;
|
||||
|
@ -3,27 +3,35 @@ import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { ROARR, Roarr } from 'roarr';
|
||||
|
||||
import type { LoggerNamespace } from './logger';
|
||||
import type { LogNamespace } from './logger';
|
||||
import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP, logger } from './logger';
|
||||
|
||||
export const useLogger = (namespace: LoggerNamespace) => {
|
||||
const consoleLogLevel = useAppSelector((s) => s.system.consoleLogLevel);
|
||||
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
|
||||
export const useLogger = (namespace: LogNamespace) => {
|
||||
const logLevel = useAppSelector((s) => s.system.logLevel);
|
||||
const logNamespaces = useAppSelector((s) => s.system.logNamespaces);
|
||||
const logIsEnabled = useAppSelector((s) => s.system.logIsEnabled);
|
||||
|
||||
// The provided Roarr browser log writer uses localStorage to config logging to console
|
||||
useEffect(() => {
|
||||
if (shouldLogToConsole) {
|
||||
if (logIsEnabled) {
|
||||
// Enable console log output
|
||||
localStorage.setItem('ROARR_LOG', 'true');
|
||||
|
||||
// Use a filter to show only logs of the given level
|
||||
localStorage.setItem('ROARR_FILTER', `context.logLevel:>=${LOG_LEVEL_MAP[consoleLogLevel]}`);
|
||||
let filter = `context.logLevel:>=${LOG_LEVEL_MAP[logLevel]}`;
|
||||
if (logNamespaces.length > 0) {
|
||||
filter += ' AND ';
|
||||
filter = `(${logNamespaces.map((ns) => `context.namespace:${ns}`).join(' OR ')})`;
|
||||
} else {
|
||||
filter += ' AND context.namespace:undefined';
|
||||
}
|
||||
localStorage.setItem('ROARR_FILTER', filter);
|
||||
} else {
|
||||
// Disable console log output
|
||||
localStorage.setItem('ROARR_LOG', 'false');
|
||||
}
|
||||
ROARR.write = createLogWriter();
|
||||
}, [consoleLogLevel, shouldLogToConsole]);
|
||||
}, [logLevel, logIsEnabled, logNamespaces]);
|
||||
|
||||
// Update the module-scoped logger context as needed
|
||||
useEffect(() => {
|
||||
|
@ -8,14 +8,14 @@ import { t } from 'i18next';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import type { BatchConfig, ImageDTO } from 'services/api/types';
|
||||
|
||||
const log = logger('queue');
|
||||
|
||||
export const adHocPostProcessingRequested = createAction<{ imageDTO: ImageDTO }>(`upscaling/postProcessingRequested`);
|
||||
|
||||
export const addAdHocPostProcessingRequestedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: adHocPostProcessingRequested,
|
||||
effect: async (action, { dispatch, getState }) => {
|
||||
const log = logger('session');
|
||||
|
||||
const { imageDTO } = action.payload;
|
||||
const state = getState();
|
||||
|
||||
|
@ -13,12 +13,12 @@ import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { $lastCanvasProgressEvent } from 'services/events/setEventListeners';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
const log = logger('canvas');
|
||||
|
||||
export const addStagingListeners = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: sessionStagingAreaReset,
|
||||
effect: async (_, { dispatch }) => {
|
||||
const log = logger('canvas');
|
||||
|
||||
try {
|
||||
const req = dispatch(
|
||||
queueApi.endpoints.cancelByBatchOrigin.initiate(
|
||||
|
@ -7,6 +7,8 @@ import { t } from 'i18next';
|
||||
import { truncate, upperFirst } from 'lodash-es';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
|
||||
const log = logger('queue');
|
||||
|
||||
export const addBatchEnqueuedListener = (startAppListening: AppStartListening) => {
|
||||
// success
|
||||
startAppListening({
|
||||
@ -14,7 +16,7 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
|
||||
effect: async (action) => {
|
||||
const response = action.payload;
|
||||
const arg = action.meta.arg.originalArgs;
|
||||
logger('queue').debug({ enqueueResult: parseify(response) }, 'Batch enqueued');
|
||||
log.debug({ enqueueResult: parseify(response) }, 'Batch enqueued');
|
||||
|
||||
toast({
|
||||
id: 'QUEUE_BATCH_SUCCEEDED',
|
||||
@ -42,7 +44,7 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
|
||||
status: 'error',
|
||||
description: t('common.unknownError'),
|
||||
});
|
||||
logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
|
||||
log.error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -68,7 +70,7 @@ export const addBatchEnqueuedListener = (startAppListening: AppStartListening) =
|
||||
description: t('common.unknownError'),
|
||||
});
|
||||
}
|
||||
logger('queue').error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
|
||||
log.error({ batchConfig: parseify(arg), error: parseify(response) }, t('queue.batchFailedToQueue'));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
const log = logger('images');
|
||||
const log = logger('gallery');
|
||||
|
||||
export const addBulkDownloadListeners = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
|
@ -7,11 +7,12 @@ import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
const log = logger('canvas');
|
||||
|
||||
export const addCanvasImageToControlNetListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: canvasImageToControlAdapter,
|
||||
effect: async (action, { dispatch, getState }) => {
|
||||
const log = logger('canvas');
|
||||
const state = getState();
|
||||
const { id } = action.payload;
|
||||
|
||||
|
@ -25,7 +25,7 @@ import { assert } from 'tsafe';
|
||||
const matcher = isAnyOf(caImageChanged, caProcessedImageChanged, caProcessorConfigChanged, caModelChanged, caRecalled);
|
||||
|
||||
const DEBOUNCE_MS = 300;
|
||||
const log = logger('session');
|
||||
const log = logger('queue');
|
||||
|
||||
/**
|
||||
* Simple helper to cancel a batch and reset the pending batch ID
|
||||
|
@ -6,7 +6,7 @@ import { parseify } from 'common/util/serialize';
|
||||
import { canvasBatchIdAdded, stagingAreaInitialized } from 'features/canvas/store/canvasSlice';
|
||||
import { getCanvasData } from 'features/canvas/util/getCanvasData';
|
||||
import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode';
|
||||
import { blobToDataURL } from "features/controlLayers/konva/util";
|
||||
import { blobToDataURL } from 'features/controlLayers/konva/util';
|
||||
import { canvasGraphBuilt } from 'features/nodes/store/actions';
|
||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||
import { buildCanvasGraph } from 'features/nodes/util/graph/canvas/buildCanvasGraph';
|
||||
@ -14,6 +14,8 @@ import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const log = logger('queue');
|
||||
|
||||
/**
|
||||
* This listener is responsible invoking the canvas. This involves a number of steps:
|
||||
*
|
||||
@ -32,7 +34,6 @@ export const addEnqueueRequestedCanvasListener = (startAppListening: AppStartLis
|
||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||
enqueueRequested.match(action) && action.payload.tabName === 'canvas',
|
||||
effect: async (action, { getState, dispatch }) => {
|
||||
const log = logger('queue');
|
||||
const { prepend } = action.payload;
|
||||
const state = getState();
|
||||
|
||||
|
@ -6,11 +6,12 @@ import { parseSchema } from 'features/nodes/util/schema/parseSchema';
|
||||
import { size } from 'lodash-es';
|
||||
import { appInfoApi } from 'services/api/endpoints/appInfo';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
export const addGetOpenAPISchemaListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
matcher: appInfoApi.endpoints.getOpenAPISchema.matchFulfilled,
|
||||
effect: (action, { getState }) => {
|
||||
const log = logger('system');
|
||||
const schemaJSON = action.payload;
|
||||
|
||||
log.debug({ schemaJSON: parseify(schemaJSON) }, 'Received OpenAPI schema');
|
||||
@ -30,7 +31,6 @@ export const addGetOpenAPISchemaListener = (startAppListening: AppStartListening
|
||||
// If action.meta.condition === true, the request was canceled/skipped because another request was in flight or
|
||||
// the value was already in the cache. We don't want to log these errors.
|
||||
if (!action.meta.condition) {
|
||||
const log = logger('system');
|
||||
log.error({ error: parseify(action.error) }, 'Problem retrieving OpenAPI Schema');
|
||||
}
|
||||
},
|
||||
|
@ -2,15 +2,13 @@ import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
const log = logger('gallery');
|
||||
|
||||
export const addImageAddedToBoardFulfilledListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.addImageToBoard.matchFulfilled,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
const { board_id, imageDTO } = action.meta.arg.originalArgs;
|
||||
|
||||
// TODO: update listImages cache for this board
|
||||
|
||||
log.debug({ board_id, imageDTO }, 'Image added to board');
|
||||
},
|
||||
});
|
||||
@ -18,9 +16,7 @@ export const addImageAddedToBoardFulfilledListener = (startAppListening: AppStar
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.addImageToBoard.matchRejected,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
const { board_id, imageDTO } = action.meta.arg.originalArgs;
|
||||
|
||||
log.debug({ board_id, imageDTO }, 'Problem adding image to board');
|
||||
},
|
||||
});
|
||||
|
@ -13,6 +13,8 @@ import { forEach, intersectionBy } from 'lodash-es';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
const log = logger('gallery');
|
||||
|
||||
// Some utils to delete images from different parts of the app
|
||||
const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||
state.nodes.present.nodes.forEach((node) => {
|
||||
@ -185,7 +187,6 @@ export const addImageDeletionListeners = (startAppListening: AppStartListening)
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.deleteImage.matchFulfilled,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
log.debug({ imageDTO: action.meta.arg.originalArgs }, 'Image deleted');
|
||||
},
|
||||
});
|
||||
@ -193,7 +194,6 @@ export const addImageDeletionListeners = (startAppListening: AppStartListening)
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.deleteImage.matchRejected,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
log.debug({ imageDTO: action.meta.arg.originalArgs }, 'Unable to delete image');
|
||||
},
|
||||
});
|
||||
|
@ -2,11 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { parseify } from 'common/util/serialize';
|
||||
import {
|
||||
ipaImageChanged,
|
||||
rasterLayerAdded,
|
||||
rgIPAdapterImageChanged,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { ipaImageChanged, rasterLayerAdded, rgIPAdapterImageChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import type { CanvasRasterLayerState } from 'features/controlLayers/store/types';
|
||||
import { imageDTOToImageObject } from 'features/controlLayers/store/types';
|
||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||
@ -26,11 +22,12 @@ export const dndDropped = createAction<{
|
||||
activeData: TypesafeDraggableData;
|
||||
}>('dnd/dndDropped');
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
export const addImageDroppedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: dndDropped,
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
const log = logger('dnd');
|
||||
const { activeData, overData } = action.payload;
|
||||
if (!isValidDrop(overData, activeData)) {
|
||||
return;
|
||||
|
@ -2,13 +2,13 @@ import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
const log = logger('gallery');
|
||||
|
||||
export const addImageRemovedFromBoardFulfilledListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.removeImageFromBoard.matchFulfilled,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
const imageDTO = action.meta.arg.originalArgs;
|
||||
|
||||
log.debug({ imageDTO }, 'Image removed from board');
|
||||
},
|
||||
});
|
||||
@ -16,9 +16,7 @@ export const addImageRemovedFromBoardFulfilledListener = (startAppListening: App
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.removeImageFromBoard.matchRejected,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
const imageDTO = action.meta.arg.originalArgs;
|
||||
|
||||
log.debug({ imageDTO }, 'Problem removing image from board');
|
||||
},
|
||||
});
|
||||
|
@ -10,11 +10,12 @@ import { omit } from 'lodash-es';
|
||||
import { boardsApi } from 'services/api/endpoints/boards';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
const log = logger('gallery');
|
||||
|
||||
export const addImageUploadedFulfilledListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.uploadImage.matchFulfilled,
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
const log = logger('images');
|
||||
const imageDTO = action.payload;
|
||||
const state = getState();
|
||||
const { autoAddBoardId } = state.gallery;
|
||||
@ -112,7 +113,6 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.uploadImage.matchRejected,
|
||||
effect: (action) => {
|
||||
const log = logger('images');
|
||||
const sanitizedData = {
|
||||
arg: {
|
||||
...omit(action.meta.arg.originalArgs, ['file', 'postUploadAction']),
|
||||
|
@ -1,21 +1,17 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||
import {
|
||||
loraDeleted,
|
||||
modelChanged,
|
||||
vaeSelected,
|
||||
} from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { loraDeleted, modelChanged, vaeSelected } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const log = logger('models');
|
||||
|
||||
export const addModelSelectedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: modelSelected,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const log = logger('models');
|
||||
|
||||
const state = getState();
|
||||
const result = zParameterModel.safeParse(action.payload);
|
||||
|
||||
|
@ -30,12 +30,13 @@ import {
|
||||
isVAEModelConfig,
|
||||
} from 'services/api/types';
|
||||
|
||||
const log = logger('models');
|
||||
|
||||
export const addModelsLoadedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
predicate: modelsApi.endpoints.getModelConfigs.matchFulfilled,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
// models loaded, we need to ensure the selected model is available and if not, select the first one
|
||||
const log = logger('models');
|
||||
log.info({ models: action.payload.entities }, `Models loaded (${action.payload.ids.length})`);
|
||||
|
||||
const state = getState();
|
||||
|
@ -8,7 +8,7 @@ import { modelsApi } from 'services/api/endpoints/models';
|
||||
import { queueApi, selectQueueStatus } from 'services/api/endpoints/queue';
|
||||
import { socketConnected } from 'services/events/setEventListeners';
|
||||
|
||||
const log = logger('socketio');
|
||||
const log = logger('events');
|
||||
|
||||
const $isFirstConnection = atom(true);
|
||||
|
||||
|
@ -8,11 +8,12 @@ import { getNeedsUpdate, updateNode } from 'features/nodes/util/node/nodeUpdate'
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { t } from 'i18next';
|
||||
|
||||
const log = logger('workflows');
|
||||
|
||||
export const addUpdateAllNodesRequestedListener = (startAppListening: AppStartListening) => {
|
||||
startAppListening({
|
||||
actionCreator: updateAllNodesRequested,
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
const log = logger('nodes');
|
||||
const { nodes } = getState().nodes.present;
|
||||
const templates = $templates.get();
|
||||
|
||||
|
@ -16,6 +16,8 @@ import type { GraphAndWorkflowResponse, NonNullableGraph } from 'services/api/ty
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
const log = logger('workflows');
|
||||
|
||||
const getWorkflow = async (data: GraphAndWorkflowResponse, templates: Templates) => {
|
||||
if (data.workflow) {
|
||||
// Prefer to load the workflow if it's available - it has more information
|
||||
@ -35,7 +37,6 @@ export const addWorkflowLoadRequestedListener = (startAppListening: AppStartList
|
||||
startAppListening({
|
||||
actionCreator: workflowLoadRequested,
|
||||
effect: async (action, { dispatch }) => {
|
||||
const log = logger('nodes');
|
||||
const { data, asCopy } = action.payload;
|
||||
const nodeTemplates = $templates.get();
|
||||
|
||||
|
@ -36,6 +36,8 @@ import { actionsDenylist } from './middleware/devtools/actionsDenylist';
|
||||
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
const allReducers = {
|
||||
[api.reducerPath]: api.reducer,
|
||||
[gallerySlice.name]: gallerySlice.reducer,
|
||||
@ -98,7 +100,6 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = {
|
||||
};
|
||||
|
||||
const unserialize: UnserializeFunction = (data, key) => {
|
||||
const log = logger('system');
|
||||
const persistConfig = persistConfigs[key as keyof typeof persistConfigs];
|
||||
if (!persistConfig) {
|
||||
throw new Error(`No persist config for slice "${key}"`);
|
||||
|
@ -54,7 +54,6 @@ export class CanvasManager {
|
||||
background: CanvasBackground;
|
||||
|
||||
log: Logger;
|
||||
workerLog: Logger;
|
||||
socket: AppSocket;
|
||||
|
||||
_store: AppStore;
|
||||
@ -85,7 +84,6 @@ export class CanvasManager {
|
||||
},
|
||||
};
|
||||
});
|
||||
this.workerLog = logger('worker');
|
||||
|
||||
this.preview = new CanvasPreview(this);
|
||||
this.stage.add(this.preview.getLayer());
|
||||
@ -97,9 +95,9 @@ export class CanvasManager {
|
||||
const { type, data } = event.data;
|
||||
if (type === 'log') {
|
||||
if (data.ctx) {
|
||||
this.workerLog[data.level](data.ctx, data.message);
|
||||
this.log[data.level](data.ctx, data.message);
|
||||
} else {
|
||||
this.workerLog[data.level](data.message);
|
||||
this.log[data.level](data.message);
|
||||
}
|
||||
} else if (type === 'extents') {
|
||||
const task = this._tasks.get(data.id);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
|
||||
import {
|
||||
alignCoordForTool,
|
||||
getObjectId,
|
||||
getPrefixedId,
|
||||
getScaledCursorPosition,
|
||||
offsetCoord,
|
||||
} from 'features/controlLayers/konva/util';
|
||||
@ -216,7 +216,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
}
|
||||
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('brush_line', true),
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [
|
||||
// The last point of the last line is already normalized to the entity's coordinates
|
||||
@ -234,7 +234,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
selectedEntity.adapter.renderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('brush_line', true),
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
strokeWidth: toolState.brush.width,
|
||||
@ -254,7 +254,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
selectedEntity.adapter.renderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('eraser_line', true),
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [
|
||||
// The last point of the last line is already normalized to the entity's coordinates
|
||||
@ -271,7 +271,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
selectedEntity.adapter.renderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('eraser_line', true),
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
strokeWidth: toolState.eraser.width,
|
||||
@ -286,7 +286,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
selectedEntity.adapter.renderer.commitBuffer();
|
||||
}
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('rect', true),
|
||||
id: getPrefixedId('rect'),
|
||||
type: 'rect',
|
||||
rect: { x: Math.round(normalizedPoint.x), y: Math.round(normalizedPoint.y), width: 0, height: 0 },
|
||||
color: getCurrentFill(),
|
||||
@ -375,7 +375,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.brush.width);
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('brush_line', true),
|
||||
id: getPrefixedId('brush_line'),
|
||||
type: 'brush_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
strokeWidth: toolState.brush.width,
|
||||
@ -412,7 +412,7 @@ export const setStageEventHandlers = (manager: CanvasManager): (() => void) => {
|
||||
const normalizedPoint = offsetCoord(pos, selectedEntity.state.position);
|
||||
const alignedPoint = alignCoordForTool(normalizedPoint, toolState.eraser.width);
|
||||
await selectedEntity.adapter.renderer.setBuffer({
|
||||
id: getObjectId('eraser_line', true),
|
||||
id: getPrefixedId('eraser_line'),
|
||||
type: 'eraser_line',
|
||||
points: [alignedPoint.x, alignedPoint.y],
|
||||
strokeWidth: toolState.eraser.width,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { CanvasObjectState, Coordinate, Rect, RgbaColor } from 'features/controlLayers/store/types';
|
||||
import type { Coordinate, Rect, RgbaColor } from 'features/controlLayers/store/types';
|
||||
import Konva from 'konva';
|
||||
import type { KonvaEventObject } from 'konva/lib/Node';
|
||||
import type { Vector2d } from 'konva/lib/types';
|
||||
@ -363,14 +363,6 @@ export function getPrefixedId(prefix: string): string {
|
||||
return `${prefix}:${nanoid()}`;
|
||||
}
|
||||
|
||||
export function getObjectId(type: CanvasObjectState['type'], isBuffer?: boolean): string {
|
||||
if (isBuffer) {
|
||||
return getPrefixedId(`buffer_${type}`);
|
||||
} else {
|
||||
return getPrefixedId(type);
|
||||
}
|
||||
}
|
||||
|
||||
export const getEmptyRect = (): Rect => {
|
||||
return { x: 0, y: 0, width: 0, height: 0 };
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { CanvasControlAdapter } from 'features/controlLayers/konva/CanvasControlAdapter';
|
||||
import { CanvasLayerAdapter } from 'features/controlLayers/konva/CanvasLayerAdapter';
|
||||
import { CanvasMaskAdapter } from 'features/controlLayers/konva/CanvasMaskAdapter';
|
||||
import { getObjectId } from 'features/controlLayers/konva/util';
|
||||
import { getPrefixedId } from 'features/controlLayers/konva/util';
|
||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||
import type { AspectRatioState } from 'features/parameters/components/DocumentSize/types';
|
||||
import type {
|
||||
@ -803,7 +803,7 @@ export const imageDTOToImageWithDims = ({ image_name, width, height }: ImageDTO)
|
||||
export const imageDTOToImageObject = (imageDTO: ImageDTO, overrides?: Partial<CanvasImageState>): CanvasImageState => {
|
||||
const { width, height, image_name } = imageDTO;
|
||||
return {
|
||||
id: getObjectId('image'),
|
||||
id: getPrefixedId('image'),
|
||||
type: 'image',
|
||||
image: {
|
||||
image_name,
|
||||
|
@ -11,23 +11,21 @@ import { memo, useCallback, useState } from 'react';
|
||||
|
||||
import { DndContextTypesafe } from './DndContextTypesafe';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
const AppDndContext = (props: PropsWithChildren) => {
|
||||
const [activeDragData, setActiveDragData] = useState<TypesafeDraggableData | null>(null);
|
||||
const log = logger('images');
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleDragStart = useCallback(
|
||||
(event: DragStartEvent) => {
|
||||
log.trace({ dragData: parseify(event.active.data.current) }, 'Drag started');
|
||||
const activeData = event.active.data.current;
|
||||
if (!activeData) {
|
||||
return;
|
||||
}
|
||||
setActiveDragData(activeData);
|
||||
},
|
||||
[log]
|
||||
);
|
||||
const handleDragStart = useCallback((event: DragStartEvent) => {
|
||||
log.trace({ dragData: parseify(event.active.data.current) }, 'Drag started');
|
||||
const activeData = event.active.data.current;
|
||||
if (!activeData) {
|
||||
return;
|
||||
}
|
||||
setActiveDragData(activeData);
|
||||
}, []);
|
||||
|
||||
const handleDragEnd = useCallback(
|
||||
(event: DragEndEvent) => {
|
||||
@ -39,7 +37,7 @@ const AppDndContext = (props: PropsWithChildren) => {
|
||||
dispatch(dndDropped({ overData, activeData: activeDragData }));
|
||||
setActiveDragData(null);
|
||||
},
|
||||
[activeDragData, dispatch, log]
|
||||
[activeDragData, dispatch]
|
||||
);
|
||||
|
||||
const mouseSensor = useSensor(MouseSensor, {
|
||||
|
@ -15,14 +15,14 @@ import {
|
||||
bboxWidthChanged,
|
||||
// caRecalled,
|
||||
ipaRecalled,
|
||||
rasterLayerAllDeleted,
|
||||
rasterLayerRecalled,
|
||||
loraAllDeleted,
|
||||
loraRecalled,
|
||||
negativePrompt2Changed,
|
||||
negativePromptChanged,
|
||||
positivePrompt2Changed,
|
||||
positivePromptChanged,
|
||||
rasterLayerAllDeleted,
|
||||
rasterLayerRecalled,
|
||||
refinerModelChanged,
|
||||
rgRecalled,
|
||||
setCfgRescaleMultiplier,
|
||||
@ -80,6 +80,8 @@ import type {
|
||||
import { getImageDTO } from 'services/api/endpoints/images';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const log = logger('metadata');
|
||||
|
||||
const recallPositivePrompt: MetadataRecallFunc<ParameterPositivePrompt> = (positivePrompt) => {
|
||||
getStore().dispatch(positivePromptChanged(positivePrompt));
|
||||
};
|
||||
@ -351,7 +353,7 @@ const recallLayer: MetadataRecallFunc<CanvasRasterLayerState> = async (layer) =>
|
||||
} else if (obj.type === 'rect') {
|
||||
obj.id = getRectShapeId(clone.id, uuidv4());
|
||||
} else {
|
||||
logger('metadata').error(`Unknown object type ${obj.type}`);
|
||||
log.error(`Unknown object type ${obj.type}`);
|
||||
}
|
||||
}
|
||||
clone.id = getRGId(uuidv4());
|
||||
|
@ -34,6 +34,7 @@ import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import { addRegions } from './addRegions';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
export const buildSD1Graph = async (state: RootState, manager: CanvasManager): Promise<Graph> => {
|
||||
|
@ -33,6 +33,7 @@ import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
import { addRegions } from './addRegions';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
export const buildSDXLGraph = async (state: RootState, manager: CanvasManager): Promise<Graph> => {
|
||||
|
@ -25,6 +25,8 @@ import { buildFieldInputTemplate } from './buildFieldInputTemplate';
|
||||
import { buildFieldOutputTemplate } from './buildFieldOutputTemplate';
|
||||
import { isCollectionFieldType, parseFieldType } from './parseFieldType';
|
||||
|
||||
const log = logger('system');
|
||||
|
||||
const RESERVED_INPUT_FIELD_NAMES = ['id', 'type', 'use_cache'];
|
||||
const RESERVED_OUTPUT_FIELD_NAMES = ['type'];
|
||||
const RESERVED_FIELD_TYPES = ['IsIntermediate'];
|
||||
@ -85,18 +87,12 @@ export const parseSchema = (
|
||||
schema.properties,
|
||||
(inputsAccumulator: Record<string, FieldInputTemplate>, property, propertyName) => {
|
||||
if (isReservedInputField(type, propertyName)) {
|
||||
logger('nodes').trace(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Skipped reserved input field'
|
||||
);
|
||||
log.trace({ node: type, field: propertyName, schema: parseify(property) }, 'Skipped reserved input field');
|
||||
return inputsAccumulator;
|
||||
}
|
||||
|
||||
if (!isInvocationFieldSchema(property)) {
|
||||
logger('nodes').warn(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Unhandled input property'
|
||||
);
|
||||
log.warn({ node: type, field: propertyName, schema: parseify(property) }, 'Unhandled input property');
|
||||
return inputsAccumulator;
|
||||
}
|
||||
|
||||
@ -111,18 +107,12 @@ export const parseSchema = (
|
||||
|
||||
const fieldType = fieldTypeOverride ?? originalFieldType;
|
||||
if (!fieldType) {
|
||||
logger('nodes').trace(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Unable to parse field type'
|
||||
);
|
||||
log.trace({ node: type, field: propertyName, schema: parseify(property) }, 'Unable to parse field type');
|
||||
return inputsAccumulator;
|
||||
}
|
||||
|
||||
if (isReservedFieldType(fieldType.name)) {
|
||||
logger('nodes').trace(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Skipped reserved input field'
|
||||
);
|
||||
log.trace({ node: type, field: propertyName, schema: parseify(property) }, 'Skipped reserved input field');
|
||||
return inputsAccumulator;
|
||||
}
|
||||
|
||||
@ -141,18 +131,18 @@ export const parseSchema = (
|
||||
const outputSchemaName = schema.output.$ref.split('/').pop();
|
||||
|
||||
if (!outputSchemaName) {
|
||||
logger('nodes').warn({ outputRefObject: parseify(schema.output) }, 'No output schema name found in ref object');
|
||||
log.warn({ outputRefObject: parseify(schema.output) }, 'No output schema name found in ref object');
|
||||
return invocationsAccumulator;
|
||||
}
|
||||
|
||||
const outputSchema = openAPI.components?.schemas?.[outputSchemaName];
|
||||
if (!outputSchema) {
|
||||
logger('nodes').warn({ outputSchemaName }, 'Output schema not found');
|
||||
log.warn({ outputSchemaName }, 'Output schema not found');
|
||||
return invocationsAccumulator;
|
||||
}
|
||||
|
||||
if (!isInvocationOutputSchemaObject(outputSchema)) {
|
||||
logger('nodes').error({ outputSchema: parseify(outputSchema) }, 'Invalid output schema');
|
||||
log.error({ outputSchema: parseify(outputSchema) }, 'Invalid output schema');
|
||||
return invocationsAccumulator;
|
||||
}
|
||||
|
||||
@ -162,18 +152,12 @@ export const parseSchema = (
|
||||
outputSchema.properties,
|
||||
(outputsAccumulator, property, propertyName) => {
|
||||
if (!isAllowedOutputField(type, propertyName)) {
|
||||
logger('nodes').trace(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Skipped reserved output field'
|
||||
);
|
||||
log.trace({ node: type, field: propertyName, schema: parseify(property) }, 'Skipped reserved output field');
|
||||
return outputsAccumulator;
|
||||
}
|
||||
|
||||
if (!isInvocationFieldSchema(property)) {
|
||||
logger('nodes').warn(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Unhandled output property'
|
||||
);
|
||||
log.warn({ node: type, field: propertyName, schema: parseify(property) }, 'Unhandled output property');
|
||||
return outputsAccumulator;
|
||||
}
|
||||
|
||||
@ -188,10 +172,7 @@ export const parseSchema = (
|
||||
|
||||
const fieldType = fieldTypeOverride ?? originalFieldType;
|
||||
if (!fieldType) {
|
||||
logger('nodes').trace(
|
||||
{ node: type, field: propertyName, schema: parseify(property) },
|
||||
'Unable to parse field type'
|
||||
);
|
||||
log.trace({ node: type, field: propertyName, schema: parseify(property) }, 'Unable to parse field type');
|
||||
return outputsAccumulator;
|
||||
}
|
||||
|
||||
@ -242,7 +223,7 @@ const getFieldType = (
|
||||
} catch (e) {
|
||||
const tKey = kind === 'input' ? 'nodes.inputFieldTypeParseError' : 'nodes.outputFieldTypeParseError';
|
||||
if (e instanceof FieldParseError) {
|
||||
logger('nodes').warn(
|
||||
log.warn(
|
||||
{
|
||||
node: type,
|
||||
field: propertyName,
|
||||
@ -255,7 +236,7 @@ const getFieldType = (
|
||||
})
|
||||
);
|
||||
} else {
|
||||
logger('nodes').warn(
|
||||
log.warn(
|
||||
{
|
||||
node: type,
|
||||
field: propertyName,
|
||||
|
@ -9,6 +9,8 @@ import i18n from 'i18n';
|
||||
import { pick } from 'lodash-es';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
const log = logger('workflows');
|
||||
|
||||
export type BuildWorkflowArg = {
|
||||
nodes: NodesState['nodes'];
|
||||
edges: NodesState['edges'];
|
||||
@ -93,7 +95,7 @@ export const buildWorkflowWithValidation = ({ nodes, edges, workflow }: BuildWor
|
||||
prefix: i18n.t('nodes.unableToValidateWorkflow'),
|
||||
});
|
||||
|
||||
logger('nodes').warn({ workflow: parseify(workflowToValidate) }, message);
|
||||
log.warn({ workflow: parseify(workflowToValidate) }, message);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ import { forEach } from 'lodash-es';
|
||||
import type { NonNullableGraph } from 'services/api/types';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const log = logger('workflows');
|
||||
|
||||
/**
|
||||
* Converts a graph to a workflow. This is a best-effort conversion and may not be perfect.
|
||||
* For example, if a graph references an unknown node type, that node will be skipped.
|
||||
@ -43,7 +45,7 @@ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): Wor
|
||||
|
||||
// Skip missing node templates - this is a best-effort
|
||||
if (!template) {
|
||||
logger('nodes').warn(`Node type ${node.type} not found in templates`);
|
||||
log.warn(`Node type ${node.type} not found in templates`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -60,7 +62,7 @@ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): Wor
|
||||
|
||||
// Skip missing input templates
|
||||
if (!inputTemplate) {
|
||||
logger('nodes').warn(`Input ${key} not found in template for node type ${node.type}`);
|
||||
log.warn(`Input ${key} not found in template for node type ${node.type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
import { Flex } from '@invoke-ai/ui-library';
|
||||
import { SettingsDeveloperLogIsEnabled } from 'features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled';
|
||||
import { SettingsDeveloperLogLevel } from 'features/system/components/SettingsModal/SettingsDeveloperLogLevel';
|
||||
import { SettingsDeveloperLogNamespaces } from 'features/system/components/SettingsModal/SettingsDeveloperLogNamespaces';
|
||||
import { memo } from 'react';
|
||||
|
||||
export const SettingsDeveloperContent = memo(() => {
|
||||
return (
|
||||
<Flex flexDir="column" gap={4}>
|
||||
<SettingsDeveloperLogIsEnabled />
|
||||
<SettingsDeveloperLogLevel />
|
||||
<SettingsDeveloperLogNamespaces />
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
|
||||
SettingsDeveloperContent.displayName = 'SettingsDeveloperContent';
|
@ -0,0 +1,29 @@
|
||||
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { logIsEnabledChanged } from 'features/system/store/systemSlice';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
export const SettingsDeveloperLogIsEnabled = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const logIsEnabled = useAppSelector((s) => s.system.logIsEnabled);
|
||||
|
||||
const onChangeLogIsEnabled = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(logIsEnabledChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>{t('system.enableLogging')}</FormLabel>
|
||||
<Switch isChecked={logIsEnabled} onChange={onChangeLogIsEnabled} />
|
||||
</FormControl>
|
||||
);
|
||||
});
|
||||
|
||||
SettingsDeveloperLogIsEnabled.displayName = 'SettingsDeveloperLogIsEnabled';
|
@ -0,0 +1,34 @@
|
||||
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { isLogLevel, zLogLevel } from 'app/logging/logger';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { logLevelChanged } from 'features/system/store/systemSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const SettingsDeveloperLogLevel = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const logLevel = useAppSelector((s) => s.system.logLevel);
|
||||
const options = useMemo(() => zLogLevel.options.map((o) => ({ label: t(`system.logLevel.${o}`), value: o })), [t]);
|
||||
|
||||
const value = useMemo(() => options.find((o) => o.value === logLevel), [logLevel, options]);
|
||||
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
if (!isLogLevel(v?.value)) {
|
||||
return;
|
||||
}
|
||||
dispatch(logLevelChanged(v.value));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>{t('system.logLevel.logLevel')}</FormLabel>
|
||||
<Combobox value={value} options={options} onChange={onChange} isSearchable={false} />
|
||||
</FormControl>
|
||||
);
|
||||
});
|
||||
|
||||
SettingsDeveloperLogLevel.displayName = 'SettingsDeveloperLogLevel';
|
@ -0,0 +1,70 @@
|
||||
import { Flex, FormControl, FormLabel, Tag, TagCloseButton, Text } from '@invoke-ai/ui-library';
|
||||
import type { LogNamespace } from 'app/logging/logger';
|
||||
import { zLogNamespace } from 'app/logging/logger';
|
||||
import { EMPTY_ARRAY } from 'app/store/constants';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { logNamespaceToggled } from 'features/system/store/systemSlice';
|
||||
import { difference } from 'lodash-es';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
export const SettingsDeveloperLogNamespaces = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const enabledLogNamespaces = useAppSelector((s) => {
|
||||
if (s.system.logNamespaces.length === 0) {
|
||||
return EMPTY_ARRAY;
|
||||
} else {
|
||||
return s.system.logNamespaces.toSorted();
|
||||
}
|
||||
});
|
||||
const disabledLogNamespaces = useMemo(
|
||||
() => difference(zLogNamespace.options, enabledLogNamespaces).toSorted(),
|
||||
[enabledLogNamespaces]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl orientation="vertical">
|
||||
<FormLabel>{t('system.logNamespaces.logNamespaces')}</FormLabel>
|
||||
<Flex w="full" gap={2} flexWrap="wrap" minH="32px" borderRadius="base" borderWidth={1} p={2} alignItems="center">
|
||||
{enabledLogNamespaces.map((namespace) => (
|
||||
<LogLevelTag key={`enabled-${namespace}`} namespace={namespace} isEnabled={true} />
|
||||
))}
|
||||
</Flex>
|
||||
<Flex gap={2} flexWrap="wrap">
|
||||
{disabledLogNamespaces.map((namespace) => (
|
||||
<LogLevelTag key={`disabled-${namespace}`} namespace={namespace} isEnabled={false} />
|
||||
))}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
);
|
||||
});
|
||||
|
||||
SettingsDeveloperLogNamespaces.displayName = 'SettingsDeveloperLogNamespaces';
|
||||
|
||||
const LogLevelTag = ({ namespace, isEnabled }: { namespace: LogNamespace; isEnabled: boolean }) => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(logNamespaceToggled(namespace));
|
||||
}, [dispatch, namespace]);
|
||||
|
||||
return (
|
||||
<Tag
|
||||
h="min-content"
|
||||
borderRadius="base"
|
||||
onClick={onClick}
|
||||
colorScheme={isEnabled ? 'invokeBlue' : 'base'}
|
||||
userSelect="none"
|
||||
role="button"
|
||||
size="md"
|
||||
color="base.900"
|
||||
bg={isEnabled ? 'invokeBlue.300' : 'base.300'}
|
||||
>
|
||||
<Text fontSize="sm" fontWeight="semibold">
|
||||
{t(`system.logNamespaces.${namespace}`)}
|
||||
</Text>
|
||||
{isEnabled && <TagCloseButton />}
|
||||
</Tag>
|
||||
);
|
||||
};
|
@ -2,30 +2,30 @@ import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { isLogLevel, zLogLevel } from 'app/logging/logger';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { consoleLogLevelChanged } from 'features/system/store/systemSlice';
|
||||
import { logLevelChanged } from 'features/system/store/systemSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const SettingsLogLevelSelect = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const consoleLogLevel = useAppSelector((s) => s.system.consoleLogLevel);
|
||||
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
|
||||
const logLevel = useAppSelector((s) => s.system.logLevel);
|
||||
const logIsEnabled = useAppSelector((s) => s.system.logIsEnabled);
|
||||
const options = useMemo(() => zLogLevel.options.map((o) => ({ label: o, value: o })), []);
|
||||
|
||||
const value = useMemo(() => options.find((o) => o.value === consoleLogLevel), [consoleLogLevel, options]);
|
||||
const value = useMemo(() => options.find((o) => o.value === logLevel), [logLevel, options]);
|
||||
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
if (!isLogLevel(v?.value)) {
|
||||
return;
|
||||
}
|
||||
dispatch(consoleLogLevelChanged(v.value));
|
||||
dispatch(logLevelChanged(v.value));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
return (
|
||||
<FormControl isDisabled={!shouldLogToConsole}>
|
||||
<FormControl isDisabled={!logIsEnabled}>
|
||||
<FormLabel>{t('common.loglevel')}</FormLabel>
|
||||
<Combobox value={value} options={options} onChange={onChange} />
|
||||
</FormControl>
|
||||
|
@ -20,14 +20,16 @@ import { InformationalPopover } from 'common/components/InformationalPopover/Inf
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { useClearStorage } from 'common/hooks/useClearStorage';
|
||||
import { shouldUseCpuNoiseChanged } from 'features/controlLayers/store/canvasV2Slice';
|
||||
import { SettingsDeveloperLogIsEnabled } from 'features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled';
|
||||
import { SettingsDeveloperLogLevel } from 'features/system/components/SettingsModal/SettingsDeveloperLogLevel';
|
||||
import { SettingsDeveloperLogNamespaces } from 'features/system/components/SettingsModal/SettingsDeveloperLogNamespaces';
|
||||
import { useClearIntermediates } from 'features/system/components/SettingsModal/useClearIntermediates';
|
||||
import { StickyScrollable } from 'features/system/components/StickyScrollable';
|
||||
import {
|
||||
setEnableImageDebugging,
|
||||
logIsEnabledChanged,
|
||||
setShouldConfirmOnDelete,
|
||||
setShouldEnableInformationalPopovers,
|
||||
shouldAntialiasProgressImageChanged,
|
||||
shouldLogToConsoleChanged,
|
||||
shouldUseNSFWCheckerChanged,
|
||||
shouldUseWatermarkerChanged,
|
||||
} from 'features/system/store/systemSlice';
|
||||
@ -38,7 +40,6 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useGetAppConfigQuery } from 'services/api/endpoints/appInfo';
|
||||
|
||||
import { SettingsLanguageSelect } from './SettingsLanguageSelect';
|
||||
import { SettingsLogLevelSelect } from './SettingsLogLevelSelect';
|
||||
|
||||
type ConfigOptions = {
|
||||
shouldShowDeveloperSettings?: boolean;
|
||||
@ -65,7 +66,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldShowDeveloperSettings) {
|
||||
dispatch(shouldLogToConsoleChanged(false));
|
||||
dispatch(logIsEnabledChanged(false));
|
||||
}
|
||||
}, [shouldShowDeveloperSettings, dispatch]);
|
||||
|
||||
@ -90,9 +91,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||
|
||||
const shouldUseCpuNoise = useAppSelector((s) => s.canvasV2.params.shouldUseCpuNoise);
|
||||
const shouldConfirmOnDelete = useAppSelector((s) => s.system.shouldConfirmOnDelete);
|
||||
const enableImageDebugging = useAppSelector((s) => s.system.enableImageDebugging);
|
||||
const shouldShowProgressInViewer = useAppSelector((s) => s.ui.shouldShowProgressInViewer);
|
||||
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
|
||||
const shouldAntialiasProgressImage = useAppSelector((s) => s.system.shouldAntialiasProgressImage);
|
||||
const shouldUseNSFWChecker = useAppSelector((s) => s.system.shouldUseNSFWChecker);
|
||||
const shouldUseWatermarker = useAppSelector((s) => s.system.shouldUseWatermarker);
|
||||
@ -120,13 +119,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||
}
|
||||
}, [countdown]);
|
||||
|
||||
const handleLogToConsoleChanged = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldLogToConsoleChanged(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleChangeShouldConfirmOnDelete = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(setShouldConfirmOnDelete(e.target.checked));
|
||||
@ -163,12 +155,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
const handleChangeEnableImageDebugging = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(setEnableImageDebugging(e.target.checked));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
const handleChangeShouldUseCpuNoise = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(shouldUseCpuNoiseChanged(e.target.checked));
|
||||
@ -242,15 +228,9 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
|
||||
|
||||
{shouldShowDeveloperSettings && (
|
||||
<StickyScrollable title={t('settings.developer')}>
|
||||
<FormControl>
|
||||
<FormLabel>{t('settings.shouldLogToConsole')}</FormLabel>
|
||||
<Switch isChecked={shouldLogToConsole} onChange={handleLogToConsoleChanged} />
|
||||
</FormControl>
|
||||
<SettingsLogLevelSelect />
|
||||
<FormControl>
|
||||
<FormLabel>{t('settings.enableImageDebugging')}</FormLabel>
|
||||
<Switch isChecked={enableImageDebugging} onChange={handleChangeEnableImageDebugging} />
|
||||
</FormControl>
|
||||
<SettingsDeveloperLogIsEnabled />
|
||||
<SettingsDeveloperLogLevel />
|
||||
<SettingsDeveloperLogNamespaces />
|
||||
</StickyScrollable>
|
||||
)}
|
||||
|
||||
|
@ -1,21 +1,23 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { LogNamespace } from 'app/logging/logger';
|
||||
import { zLogNamespace } from 'app/logging/logger';
|
||||
import type { PersistConfig, RootState } from 'app/store/store';
|
||||
import type { LogLevelName } from 'roarr';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
import type { Language, SystemState } from './types';
|
||||
|
||||
const initialSystemState: SystemState = {
|
||||
_version: 1,
|
||||
shouldConfirmOnDelete: true,
|
||||
enableImageDebugging: false,
|
||||
shouldAntialiasProgressImage: false,
|
||||
consoleLogLevel: 'debug',
|
||||
shouldLogToConsole: true,
|
||||
language: 'en',
|
||||
shouldUseNSFWChecker: false,
|
||||
shouldUseWatermarker: false,
|
||||
shouldEnableInformationalPopovers: true,
|
||||
logIsEnabled: true,
|
||||
logLevel: 'debug',
|
||||
logNamespaces: [...zLogNamespace.options],
|
||||
};
|
||||
|
||||
export const systemSlice = createSlice({
|
||||
@ -25,14 +27,18 @@ export const systemSlice = createSlice({
|
||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldConfirmOnDelete = action.payload;
|
||||
},
|
||||
setEnableImageDebugging: (state, action: PayloadAction<boolean>) => {
|
||||
state.enableImageDebugging = action.payload;
|
||||
logIsEnabledChanged: (state, action: PayloadAction<SystemState['logIsEnabled']>) => {
|
||||
state.logIsEnabled = action.payload;
|
||||
},
|
||||
consoleLogLevelChanged: (state, action: PayloadAction<LogLevelName>) => {
|
||||
state.consoleLogLevel = action.payload;
|
||||
logLevelChanged: (state, action: PayloadAction<SystemState['logLevel']>) => {
|
||||
state.logLevel = action.payload;
|
||||
},
|
||||
shouldLogToConsoleChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldLogToConsole = action.payload;
|
||||
logNamespaceToggled: (state, action: PayloadAction<LogNamespace>) => {
|
||||
if (state.logNamespaces.includes(action.payload)) {
|
||||
state.logNamespaces = uniq(state.logNamespaces.filter((n) => n !== action.payload));
|
||||
} else {
|
||||
state.logNamespaces = uniq([...state.logNamespaces, action.payload]);
|
||||
}
|
||||
},
|
||||
shouldAntialiasProgressImageChanged: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldAntialiasProgressImage = action.payload;
|
||||
@ -54,9 +60,9 @@ export const systemSlice = createSlice({
|
||||
|
||||
export const {
|
||||
setShouldConfirmOnDelete,
|
||||
setEnableImageDebugging,
|
||||
consoleLogLevelChanged,
|
||||
shouldLogToConsoleChanged,
|
||||
logIsEnabledChanged,
|
||||
logLevelChanged,
|
||||
logNamespaceToggled,
|
||||
shouldAntialiasProgressImageChanged,
|
||||
languageChanged,
|
||||
shouldUseNSFWCheckerChanged,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { LogLevel } from 'app/logging/logger';
|
||||
import type { LogLevel, LogNamespace } from 'app/logging/logger';
|
||||
import { z } from 'zod';
|
||||
|
||||
const zLanguage = z.enum([
|
||||
@ -31,12 +31,12 @@ export const isLanguage = (v: unknown): v is Language => zLanguage.safeParse(v).
|
||||
export interface SystemState {
|
||||
_version: 1;
|
||||
shouldConfirmOnDelete: boolean;
|
||||
enableImageDebugging: boolean;
|
||||
consoleLogLevel: LogLevel;
|
||||
shouldLogToConsole: boolean;
|
||||
shouldAntialiasProgressImage: boolean;
|
||||
language: Language;
|
||||
shouldUseNSFWChecker: boolean;
|
||||
shouldUseWatermarker: boolean;
|
||||
shouldEnableInformationalPopovers: boolean;
|
||||
logIsEnabled: boolean;
|
||||
logLevel: LogLevel;
|
||||
logNamespaces: LogNamespace[];
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import type { Socket } from 'socket.io-client';
|
||||
|
||||
export const socketConnected = createAction('socket/connected');
|
||||
|
||||
const log = logger('socketio');
|
||||
const log = logger('events');
|
||||
|
||||
type SetEventListenersArg = {
|
||||
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
||||
|
Loading…
Reference in New Issue
Block a user