From 4da4b3bd508b7e46accd4851193bb906bc959c2d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 20 Aug 2024 16:11:49 +1000 Subject: [PATCH] feat(ui): clean up logging namespaces, allow skipping namespaces --- invokeai/frontend/web/public/locales/en.json | 26 ++++++- .../frontend/web/src/app/logging/logger.ts | 33 +++++---- .../frontend/web/src/app/logging/useLogger.ts | 22 ++++-- ...addAdHocPostProcessingRequestedListener.ts | 4 +- .../addCommitStagingAreaImageListener.ts | 4 +- .../listeners/batchEnqueued.ts | 8 ++- .../listeners/bulkDownload.tsx | 2 +- .../listeners/canvasImageToControlNet.ts | 3 +- .../listeners/controlAdapterPreprocessor.ts | 2 +- .../listeners/enqueueRequestedCanvas.ts | 5 +- .../listeners/getOpenAPISchema.ts | 4 +- .../listeners/imageAddedToBoard.ts | 8 +-- .../listeners/imageDeletionListeners.ts | 4 +- .../listeners/imageDropped.ts | 9 +-- .../listeners/imageRemovedFromBoard.ts | 6 +- .../listeners/imageUploaded.ts | 4 +- .../listeners/modelSelected.ts | 10 +-- .../listeners/modelsLoaded.ts | 3 +- .../listeners/socketConnected.ts | 2 +- .../listeners/updateAllNodesRequested.ts | 3 +- .../listeners/workflowLoadRequested.ts | 3 +- invokeai/frontend/web/src/app/store/store.ts | 3 +- .../controlLayers/konva/CanvasManager.ts | 6 +- .../features/controlLayers/konva/events.ts | 16 ++--- .../src/features/controlLayers/konva/util.ts | 10 +-- .../src/features/controlLayers/store/types.ts | 4 +- .../features/dnd/components/AppDndContext.tsx | 24 +++---- .../src/features/metadata/util/recallers.ts | 8 ++- .../util/graph/generation/buildSD1Graph.ts | 1 + .../util/graph/generation/buildSDXLGraph.ts | 1 + .../features/nodes/util/schema/parseSchema.ts | 47 ++++--------- .../nodes/util/workflow/buildWorkflow.ts | 4 +- .../nodes/util/workflow/graphToWorkflow.ts | 6 +- .../SettingsDeveloperContent.tsx | 17 +++++ .../SettingsDeveloperLogIsEnabled.tsx | 29 ++++++++ .../SettingsDeveloperLogLevel.tsx | 34 +++++++++ .../SettingsDeveloperLogNamespaces.tsx | 70 +++++++++++++++++++ .../SettingsModal/SettingsLogLevelSelect.tsx | 12 ++-- .../SettingsModal/SettingsModal.tsx | 36 +++------- .../src/features/system/store/systemSlice.ts | 32 +++++---- .../web/src/features/system/store/types.ts | 8 +-- .../src/services/events/setEventListeners.tsx | 2 +- 42 files changed, 337 insertions(+), 198 deletions(-) create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperContent.tsx create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 9b1c5361af..2af53a0ea1 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -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" + } } } diff --git a/invokeai/frontend/web/src/app/logging/logger.ts b/invokeai/frontend/web/src/app/logging/logger.ts index 3d35681dc4..d8450db8bb 100644 --- a/invokeai/frontend/web/src/app/logging/logger.ts +++ b/invokeai/frontend/web/src/app/logging/logger.ts @@ -15,24 +15,23 @@ export const BASE_CONTEXT = {}; export const $logger = atom(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; +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; diff --git a/invokeai/frontend/web/src/app/logging/useLogger.ts b/invokeai/frontend/web/src/app/logging/useLogger.ts index 6e170ca376..f292f1f929 100644 --- a/invokeai/frontend/web/src/app/logging/useLogger.ts +++ b/invokeai/frontend/web/src/app/logging/useLogger.ts @@ -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(() => { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts index 65f3198b91..8358a1192d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addAdHocPostProcessingRequestedListener.ts @@ -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(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts index 6b8d9782ca..8363c0ff66 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts @@ -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( diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts index 3f74bf9b61..0b5e181bcb 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/batchEnqueued.ts @@ -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')); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx index 049b28ff84..738bd3af13 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/bulkDownload.tsx @@ -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({ diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts index eccf8b5889..6f7f6a9e9a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/canvasImageToControlNet.ts @@ -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; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts index 707820bda3..5e5193f290 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts @@ -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 diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts index ac32d6fa01..ad16650bf7 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts @@ -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 => enqueueRequested.match(action) && action.payload.tabName === 'canvas', effect: async (action, { getState, dispatch }) => { - const log = logger('queue'); const { prepend } = action.payload; const state = getState(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts index 923b2c0197..4622ff2d8c 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts @@ -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'); } }, diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageAddedToBoard.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageAddedToBoard.ts index 5412e0f236..38e2127c0d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageAddedToBoard.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageAddedToBoard.ts @@ -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'); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts index 2459f2db4f..858a8cea0b 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeletionListeners.ts @@ -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'); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts index 9adb0da5bc..6a7302a55a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDropped.ts @@ -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; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageRemovedFromBoard.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageRemovedFromBoard.ts index 274e4c51c2..c25f521609 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageRemovedFromBoard.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageRemovedFromBoard.ts @@ -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'); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index b0b26fbd1a..e5eaf71c30 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -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']), diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts index bbb9cd1cef..d9c0bf6e67 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelSelected.ts @@ -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); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts index cf3088789c..244f7ebb93 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/modelsLoaded.ts @@ -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(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts index babed47d0b..b73ab7e0fa 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketConnected.ts @@ -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); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts index 07df2a4f42..834b961ece 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts @@ -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(); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts index ebce2f80b2..d5e2269cdb 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts @@ -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(); diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index fae121c547..88a51b73b9 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -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}"`); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts index 2c31ec174b..11feea75b4 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasManager.ts @@ -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); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts index a55dc3752d..0e2f79a51b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/events.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/events.ts @@ -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, diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts index 092ca853fb..e1e66ac07d 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/util.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/util.ts @@ -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 }; }; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index ab160fd433..aec0ca9891 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -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 => { const { width, height, image_name } = imageDTO; return { - id: getObjectId('image'), + id: getPrefixedId('image'), type: 'image', image: { image_name, diff --git a/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx b/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx index f800e5c869..49af67696c 100644 --- a/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx +++ b/invokeai/frontend/web/src/features/dnd/components/AppDndContext.tsx @@ -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(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, { diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts index c1bf41d1f4..72c7249533 100644 --- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts @@ -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 = (positivePrompt) => { getStore().dispatch(positivePromptChanged(positivePrompt)); }; @@ -351,7 +353,7 @@ const recallLayer: MetadataRecallFunc = 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()); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts index 2471d24e06..580fee42b0 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts @@ -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 => { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts index 94a195b7d4..7a30befb42 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts @@ -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 => { diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts index 3981b759db..77b94bba9d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts +++ b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts @@ -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, 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, diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts index cec8b0a2b7..ba01b258f2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/buildWorkflow.ts @@ -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; } diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts index af66d3cc6b..6560977ece 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts @@ -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; } diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperContent.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperContent.tsx new file mode 100644 index 0000000000..9269132f71 --- /dev/null +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperContent.tsx @@ -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 ( + + + + + + ); +}); + +SettingsDeveloperContent.displayName = 'SettingsDeveloperContent'; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx new file mode 100644 index 0000000000..315546d3da --- /dev/null +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogIsEnabled.tsx @@ -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) => { + dispatch(logIsEnabledChanged(e.target.checked)); + }, + [dispatch] + ); + + return ( + + {t('system.enableLogging')} + + + ); +}); + +SettingsDeveloperLogIsEnabled.displayName = 'SettingsDeveloperLogIsEnabled'; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx new file mode 100644 index 0000000000..06d05b7761 --- /dev/null +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogLevel.tsx @@ -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( + (v) => { + if (!isLogLevel(v?.value)) { + return; + } + dispatch(logLevelChanged(v.value)); + }, + [dispatch] + ); + return ( + + {t('system.logLevel.logLevel')} + + + ); +}); + +SettingsDeveloperLogLevel.displayName = 'SettingsDeveloperLogLevel'; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx new file mode 100644 index 0000000000..a0f4b63aff --- /dev/null +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsDeveloperLogNamespaces.tsx @@ -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 ( + + {t('system.logNamespaces.logNamespaces')} + + {enabledLogNamespaces.map((namespace) => ( + + ))} + + + {disabledLogNamespaces.map((namespace) => ( + + ))} + + + ); +}); + +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 ( + + + {t(`system.logNamespaces.${namespace}`)} + + {isEnabled && } + + ); +}; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx index 7a3eb06992..ae05a1471a 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx @@ -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( (v) => { if (!isLogLevel(v?.value)) { return; } - dispatch(consoleLogLevelChanged(v.value)); + dispatch(logLevelChanged(v.value)); }, [dispatch] ); return ( - + {t('common.loglevel')} diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index 1853183c9b..7f72eb37a7 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -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) => { - dispatch(shouldLogToConsoleChanged(e.target.checked)); - }, - [dispatch] - ); - const handleChangeShouldConfirmOnDelete = useCallback( (e: ChangeEvent) => { dispatch(setShouldConfirmOnDelete(e.target.checked)); @@ -163,12 +155,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { }, [dispatch] ); - const handleChangeEnableImageDebugging = useCallback( - (e: ChangeEvent) => { - dispatch(setEnableImageDebugging(e.target.checked)); - }, - [dispatch] - ); const handleChangeShouldUseCpuNoise = useCallback( (e: ChangeEvent) => { dispatch(shouldUseCpuNoiseChanged(e.target.checked)); @@ -242,15 +228,9 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { {shouldShowDeveloperSettings && ( - - {t('settings.shouldLogToConsole')} - - - - - {t('settings.enableImageDebugging')} - - + + + )} diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 0b6131c92b..44b5af7ea4 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -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) => { state.shouldConfirmOnDelete = action.payload; }, - setEnableImageDebugging: (state, action: PayloadAction) => { - state.enableImageDebugging = action.payload; + logIsEnabledChanged: (state, action: PayloadAction) => { + state.logIsEnabled = action.payload; }, - consoleLogLevelChanged: (state, action: PayloadAction) => { - state.consoleLogLevel = action.payload; + logLevelChanged: (state, action: PayloadAction) => { + state.logLevel = action.payload; }, - shouldLogToConsoleChanged: (state, action: PayloadAction) => { - state.shouldLogToConsole = action.payload; + logNamespaceToggled: (state, action: PayloadAction) => { + 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) => { state.shouldAntialiasProgressImage = action.payload; @@ -54,9 +60,9 @@ export const systemSlice = createSlice({ export const { setShouldConfirmOnDelete, - setEnableImageDebugging, - consoleLogLevelChanged, - shouldLogToConsoleChanged, + logIsEnabledChanged, + logLevelChanged, + logNamespaceToggled, shouldAntialiasProgressImageChanged, languageChanged, shouldUseNSFWCheckerChanged, diff --git a/invokeai/frontend/web/src/features/system/store/types.ts b/invokeai/frontend/web/src/features/system/store/types.ts index 6aa200ebaa..10d559690d 100644 --- a/invokeai/frontend/web/src/features/system/store/types.ts +++ b/invokeai/frontend/web/src/features/system/store/types.ts @@ -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[]; } diff --git a/invokeai/frontend/web/src/services/events/setEventListeners.tsx b/invokeai/frontend/web/src/services/events/setEventListeners.tsx index 213249da99..3c08c7953e 100644 --- a/invokeai/frontend/web/src/services/events/setEventListeners.tsx +++ b/invokeai/frontend/web/src/services/events/setEventListeners.tsx @@ -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;