Просмотр исходного кода

replace bunyan syntax with pino syntax

Yuki Takei 1 неделя назад
Родитель
Сommit
2a4fe18c3b
48 измененных файлов с 305 добавлено и 239 удалено
  1. 1 1
      .kiro/specs/migrate-logger-to-pino/spec.json
  2. 10 10
      .kiro/specs/migrate-logger-to-pino/tasks.md
  3. 6 8
      apps/app/src/client/components/PageEditor/PageEditor.tsx
  4. 1 1
      apps/app/src/client/components/RecentActivity/RecentActivity.tsx
  5. 2 2
      apps/app/src/client/components/StickyStretchableScroller.tsx
  6. 7 3
      apps/app/src/features/admin/states/socket-io.ts
  7. 2 2
      apps/app/src/features/comment/server/models/comment.ts
  8. 24 15
      apps/app/src/features/growi-plugin/server/services/growi-plugin/growi-plugin.ts
  9. 2 2
      apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantSidebar.tsx
  10. 2 2
      apps/app/src/features/openai/server/routes/edit/index.ts
  11. 22 13
      apps/app/src/features/openai/server/services/editor-assistant/llm-response-stream-processor.ts
  12. 49 31
      apps/app/src/features/openai/server/services/openai.ts
  13. 5 4
      apps/app/src/features/opentelemetry/server/custom-resource-attributes/application-resource-attributes.ts
  14. 1 1
      apps/app/src/features/opentelemetry/server/custom-resource-attributes/os-resource-attributes.ts
  15. 3 3
      apps/app/src/pages/common-props/commons.ts
  16. 3 3
      apps/app/src/pages/general-page/type-guards.ts
  17. 3 5
      apps/app/src/server/app.ts
  18. 2 2
      apps/app/src/server/events/user.ts
  19. 4 1
      apps/app/src/server/middlewares/access-token-parser/api-token.ts
  20. 3 3
      apps/app/src/server/middlewares/apiv1-form-validator.ts
  21. 3 3
      apps/app/src/server/middlewares/apiv3-form-validator.ts
  22. 16 10
      apps/app/src/server/middlewares/certify-shared-page-attachment/validate-referer/validate-referer.ts
  23. 1 1
      apps/app/src/server/middlewares/login-required.ts
  24. 6 3
      apps/app/src/server/middlewares/safe-redirect.ts
  25. 1 1
      apps/app/src/server/models/activity.ts
  26. 1 1
      apps/app/src/server/models/external-account.ts
  27. 2 2
      apps/app/src/server/models/user-group-relation.ts
  28. 2 2
      apps/app/src/server/routes/apiv3/bookmark-folder.ts
  29. 10 10
      apps/app/src/server/routes/apiv3/g2g-transfer.ts
  30. 18 12
      apps/app/src/server/routes/apiv3/page/update-page.ts
  31. 2 2
      apps/app/src/server/service/config-manager/config-loader.ts
  32. 1 1
      apps/app/src/server/service/external-account.ts
  33. 1 1
      apps/app/src/server/service/file-uploader/gridfs.ts
  34. 38 26
      apps/app/src/server/service/mail/mail.ts
  35. 1 1
      apps/app/src/server/service/mail/oauth2.ts
  36. 1 1
      apps/app/src/server/service/mail/ses.ts
  37. 2 2
      apps/app/src/server/service/mail/smtp.ts
  38. 3 3
      apps/app/src/server/service/page/events/seen.ts
  39. 6 9
      apps/app/src/server/service/page/index.ts
  40. 7 8
      apps/app/src/server/service/s2s-messaging/nchan.ts
  41. 4 4
      apps/app/src/server/service/search-delegator/elasticsearch.ts
  42. 4 4
      apps/app/src/server/service/slack-integration.ts
  43. 3 3
      apps/app/src/server/service/socket-io/socket-io.ts
  44. 2 2
      apps/app/src/server/service/yjs/create-mongodb-persistence.ts
  45. 9 6
      apps/app/src/server/service/yjs/upgrade-handler.ts
  46. 2 2
      apps/app/src/server/service/yjs/yjs.ts
  47. 4 4
      apps/app/src/server/util/slack-legacy.ts
  48. 3 3
      apps/app/src/states/socket-io/global-socket.ts

+ 1 - 1
.kiro/specs/migrate-logger-to-pino/spec.json

@@ -3,7 +3,7 @@
   "created_at": "2026-03-23T00:00:00.000Z",
   "created_at": "2026-03-23T00:00:00.000Z",
   "updated_at": "2026-03-27T00:00:00.000Z",
   "updated_at": "2026-03-27T00:00:00.000Z",
   "language": "en",
   "language": "en",
-  "phase": "tasks-generated",
+  "phase": "implementation-complete",
   "approvals": {
   "approvals": {
     "requirements": {
     "requirements": {
       "generated": true,
       "generated": true,

+ 10 - 10
.kiro/specs/migrate-logger-to-pino/tasks.md

@@ -123,49 +123,49 @@
   - Update unit tests in `logger-factory.spec.ts` to verify that calling `loggerFactory` for N distinct namespaces does not create N independent pino instances (all children share the root transport)
   - Update unit tests in `logger-factory.spec.ts` to verify that calling `loggerFactory` for N distinct namespaces does not create N independent pino instances (all children share the root transport)
   - _Requirements: 11.1, 11.2, 11.3, 11.4_
   - _Requirements: 11.1, 11.2, 11.3, 11.4_
 
 
-- [ ] 7. Migrate apps/app to @growi/logger (largest scope)
-- [ ] 7.1 Replace the logger factory module in apps/app
+- [x] 7. Migrate apps/app to @growi/logger (largest scope)
+- [x] 7.1 Replace the logger factory module in apps/app
   - Update the apps/app logger utility to import from `@growi/logger` instead of `universal-bunyan`
   - Update the apps/app logger utility to import from `@growi/logger` instead of `universal-bunyan`
   - Call `initializeLoggerFactory` at application startup with the existing dev/prod config files (preserve current config content)
   - Call `initializeLoggerFactory` at application startup with the existing dev/prod config files (preserve current config content)
   - Re-export `loggerFactory` as the default export so all existing consumer imports continue to work unchanged
   - Re-export `loggerFactory` as the default export so all existing consumer imports continue to work unchanged
   - Add `@growi/logger` to apps/app dependencies and ensure pino-pretty is available for development formatting
   - Add `@growi/logger` to apps/app dependencies and ensure pino-pretty is available for development formatting
   - _Requirements: 8.1, 2.2_
   - _Requirements: 8.1, 2.2_
 
 
-- [ ] 7.2 Replace HTTP request logging middleware in apps/app
+- [x] 7.2 Replace HTTP request logging middleware in apps/app
   - Remove the morgan middleware (development mode) and express-bunyan-logger middleware (production mode) from the Express initialization
   - Remove the morgan middleware (development mode) and express-bunyan-logger middleware (production mode) from the Express initialization
   - Add pino-http middleware configured with a logger from the factory using the `express` namespace
   - Add pino-http middleware configured with a logger from the factory using the `express` namespace
   - Configure route skipping to exclude `/_next/static/` paths in non-production mode
   - Configure route skipping to exclude `/_next/static/` paths in non-production mode
   - Verify the middleware produces log entries containing method, URL, status code, and response time
   - Verify the middleware produces log entries containing method, URL, status code, and response time
   - _Requirements: 6.1, 6.2, 6.3, 6.4_
   - _Requirements: 6.1, 6.2, 6.3, 6.4_
 
 
-- [ ] 7.3 Update the OpenTelemetry diagnostic logger adapter
+- [x] 7.3 Update the OpenTelemetry diagnostic logger adapter
   - Rename the adapter class from `DiagLoggerBunyanAdapter` to `DiagLoggerPinoAdapter` and update the import to use pino types
   - Rename the adapter class from `DiagLoggerBunyanAdapter` to `DiagLoggerPinoAdapter` and update the import to use pino types
   - Preserve the existing `parseMessage` helper logic that parses JSON strings and merges argument objects
   - Preserve the existing `parseMessage` helper logic that parses JSON strings and merges argument objects
   - Confirm the verbose-to-trace level mapping continues to work with pino's trace level
   - Confirm the verbose-to-trace level mapping continues to work with pino's trace level
   - Update the OpenTelemetry SDK configuration to disable `@opentelemetry/instrumentation-pino` instead of `@opentelemetry/instrumentation-bunyan`
   - Update the OpenTelemetry SDK configuration to disable `@opentelemetry/instrumentation-pino` instead of `@opentelemetry/instrumentation-bunyan`
   - _Requirements: 7.1, 7.2, 7.3_
   - _Requirements: 7.1, 7.2, 7.3_
 
 
-- [ ] 7.4 Update all bunyan type references in apps/app source files
+- [x] 7.4 Update all bunyan type references in apps/app source files
   - Replace `import type Logger from 'bunyan'` with the Logger type exported from `@growi/logger` across all source files in apps/app
   - Replace `import type Logger from 'bunyan'` with the Logger type exported from `@growi/logger` across all source files in apps/app
   - Verify that pino's Logger type is compatible with all existing usage patterns (info, debug, warn, error, trace, fatal method calls)
   - Verify that pino's Logger type is compatible with all existing usage patterns (info, debug, warn, error, trace, fatal method calls)
   - Run the TypeScript compiler to confirm no type errors
   - Run the TypeScript compiler to confirm no type errors
   - _Requirements: 10.1, 10.2, 10.3_
   - _Requirements: 10.1, 10.2, 10.3_
 
 
-- [ ] 8. Remove old logging dependencies and verify cleanup
-- [ ] 8.1 Remove bunyan-related packages from all package.json files
+- [x] 8. Remove old logging dependencies and verify cleanup
+- [x] 8.1 Remove bunyan-related packages from all package.json files
   - Remove `bunyan`, `universal-bunyan`, `bunyan-format`, `express-bunyan-logger`, `browser-bunyan`, `@browser-bunyan/console-formatted-stream`, `@types/bunyan` from every package.json in the monorepo
   - Remove `bunyan`, `universal-bunyan`, `bunyan-format`, `express-bunyan-logger`, `browser-bunyan`, `@browser-bunyan/console-formatted-stream`, `@types/bunyan` from every package.json in the monorepo
   - Remove `morgan` and `@types/morgan` from every package.json in the monorepo
   - Remove `morgan` and `@types/morgan` from every package.json in the monorepo
   - Run `pnpm install` to update the lockfile and verify no broken peer dependency warnings
   - Run `pnpm install` to update the lockfile and verify no broken peer dependency warnings
   - _Requirements: 9.1, 9.2_
   - _Requirements: 9.1, 9.2_
 
 
-- [ ] 8.2 Verify no residual references to removed packages
+- [x] 8.2 Verify no residual references to removed packages
   - Search all source files for any remaining imports or requires of the removed packages (bunyan, universal-bunyan, browser-bunyan, express-bunyan-logger, morgan, bunyan-format)
   - Search all source files for any remaining imports or requires of the removed packages (bunyan, universal-bunyan, browser-bunyan, express-bunyan-logger, morgan, bunyan-format)
   - Search all configuration and type definition files for stale bunyan references
   - Search all configuration and type definition files for stale bunyan references
   - Fix any remaining references found during the search
   - Fix any remaining references found during the search
   - _Requirements: 9.3_
   - _Requirements: 9.3_
 
 
-- [ ] 9. Run full monorepo validation
-- [ ] 9.1 Execute lint, type-check, test, and build across the monorepo
+- [x] 9. Run full monorepo validation
+- [x] 9.1 Execute lint, type-check, test, and build across the monorepo
   - Run `turbo run lint --filter @growi/app` and fix any lint errors related to the migration
   - Run `turbo run lint --filter @growi/app` and fix any lint errors related to the migration
   - Run `turbo run test --filter @growi/app` and verify all existing tests pass
   - Run `turbo run test --filter @growi/app` and verify all existing tests pass
   - Run `turbo run build --filter @growi/app` and confirm the production build succeeds
   - Run `turbo run build --filter @growi/app` and confirm the production build succeeds

+ 6 - 8
apps/app/src/client/components/PageEditor/PageEditor.tsx

@@ -221,10 +221,10 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
   const save: Save = useCallback(
   const save: Save = useCallback(
     async (revisionId, markdown, opts, onConflict) => {
     async (revisionId, markdown, opts, onConflict) => {
       if (pageId == null || selectedGrant == null) {
       if (pageId == null || selectedGrant == null) {
-        logger.error('Some materials to save are invalid', {
-          pageId,
-          selectedGrant,
-        });
+        logger.error(
+          { pageId, selectedGrant },
+          'Some materials to save are invalid',
+        );
         throw new Error('Some materials to save are invalid');
         throw new Error('Some materials to save are invalid');
       }
       }
 
 
@@ -251,7 +251,7 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
 
 
         return page;
         return page;
       } catch (error) {
       } catch (error) {
-        logger.error('failed to save', error);
+        logger.error({ err: error }, 'failed to save');
 
 
         const remoteRevisionData = extractRemoteRevisionDataFromErrorObj(error);
         const remoteRevisionData = extractRemoteRevisionDataFromErrorObj(error);
         if (remoteRevisionData != null) {
         if (remoteRevisionData != null) {
@@ -329,9 +329,7 @@ export const PageEditorSubstance = (props: Props): JSX.Element => {
   const uploadHandler = useCallback(
   const uploadHandler = useCallback(
     (files: File[]) => {
     (files: File[]) => {
       if (pageId == null) {
       if (pageId == null) {
-        logger.error('pageId is invalid', {
-          pageId,
-        });
+        logger.error({ pageId }, 'pageId is invalid');
         throw new Error('pageId is invalid');
         throw new Error('pageId is invalid');
       }
       }
 
 

+ 1 - 1
apps/app/src/client/components/RecentActivity/RecentActivity.tsx

@@ -54,7 +54,7 @@ export const RecentActivity = (props: RecentActivityProps): JSX.Element => {
 
 
   useEffect(() => {
   useEffect(() => {
     if (error) {
     if (error) {
-      logger.error('Failed to fetch recent activity data', error);
+      logger.error({ err: error }, 'Failed to fetch recent activity data');
       toastError(error);
       toastError(error);
       return;
       return;
     }
     }

+ 2 - 2
apps/app/src/client/components/StickyStretchableScroller.tsx

@@ -1,5 +1,5 @@
 import type { RefObject } from 'react';
 import type { RefObject } from 'react';
-import React, {
+import {
   type JSX,
   type JSX,
   useCallback,
   useCallback,
   useEffect,
   useEffect,
@@ -73,7 +73,7 @@ export const StickyStretchableScroller = (
     const scrollElement = simplebarRef.current.getScrollElement();
     const scrollElement = simplebarRef.current.getScrollElement();
     const newHeight = calcViewHeight(scrollElement);
     const newHeight = calcViewHeight(scrollElement);
 
 
-    logger.debug('Set new height to simplebar', newHeight);
+    logger.debug({ newHeight }, 'Set new height to simplebar');
 
 
     // set new height
     // set new height
     setSimplebarMaxHeight(newHeight);
     setSimplebarMaxHeight(newHeight);

+ 7 - 3
apps/app/src/features/admin/states/socket-io.ts

@@ -27,12 +27,16 @@ export const useSetupAdminSocket = (): void => {
       .then(({ default: io }) => {
       .then(({ default: io }) => {
         if (cancelled) return;
         if (cancelled) return;
         const newSocket = io('/admin', { transports: ['websocket'] });
         const newSocket = io('/admin', { transports: ['websocket'] });
-        newSocket.on('connect_error', (error) => logger.error('/admin', error));
-        newSocket.on('error', (error) => logger.error('/admin', error));
+        newSocket.on('connect_error', (error) =>
+          logger.error({ err: error }, '/admin'),
+        );
+        newSocket.on('error', (error) =>
+          logger.error({ err: error }, '/admin'),
+        );
         setSocket(newSocket);
         setSocket(newSocket);
       })
       })
       .catch((error) =>
       .catch((error) =>
-        logger.error('Failed to initialize admin WebSocket:', error),
+        logger.error({ err: error }, 'Failed to initialize admin WebSocket'),
       );
       );
 
 
     return () => {
     return () => {

+ 2 - 2
apps/app/src/features/comment/server/models/comment.ts

@@ -70,11 +70,11 @@ const add: Add = async function (
       commentPosition,
       commentPosition,
       replyTo,
       replyTo,
     });
     });
-    logger.debug('Comment saved.', data);
+    logger.debug({ data }, 'Comment saved.');
 
 
     return data;
     return data;
   } catch (err) {
   } catch (err) {
-    logger.debug('Error on saving comment.', err);
+    logger.debug({ err }, 'Error on saving comment.');
     throw err;
     throw err;
   }
   }
 };
 };

+ 24 - 15
apps/app/src/features/growi-plugin/server/services/growi-plugin/growi-plugin.ts

@@ -98,7 +98,7 @@ export class GrowiPluginService implements IGrowiPluginService {
             growiPlugin.organizationName,
             growiPlugin.organizationName,
           );
           );
         } catch (err) {
         } catch (err) {
-          logger.error(err);
+          logger.error({ err }, 'Plugin path validation failed');
           continue;
           continue;
         }
         }
         if (fs.existsSync(pluginPath)) {
         if (fs.existsSync(pluginPath)) {
@@ -135,12 +135,15 @@ export class GrowiPluginService implements IGrowiPluginService {
               await fs.promises.rm(unzippedReposPath, { recursive: true });
               await fs.promises.rm(unzippedReposPath, { recursive: true });
             if (fs.existsSync(pluginPath))
             if (fs.existsSync(pluginPath))
               await fs.promises.rm(pluginPath, { recursive: true });
               await fs.promises.rm(pluginPath, { recursive: true });
-            logger.error(err);
+            logger.error({ err }, 'Failed to download plugin repository');
           }
           }
         }
         }
       }
       }
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error(
+        { err },
+        'Failed to download non-existent plugin repositories',
+      );
     }
     }
   }
   }
 
 
@@ -199,7 +202,7 @@ export class GrowiPluginService implements IGrowiPluginService {
       // move new repository from temporary path to storing path.
       // move new repository from temporary path to storing path.
       fs.renameSync(temporaryReposPath, reposPath);
       fs.renameSync(temporaryReposPath, reposPath);
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error({ err }, 'Failed to install plugin');
       throw err;
       throw err;
     } finally {
     } finally {
       // clean up
       // clean up
@@ -222,7 +225,7 @@ export class GrowiPluginService implements IGrowiPluginService {
         await fs.promises.rm(reposPath, { recursive: true });
         await fs.promises.rm(reposPath, { recursive: true });
       await this.deleteOldPluginDocument(installedPath);
       await this.deleteOldPluginDocument(installedPath);
 
 
-      logger.error(err);
+      logger.error({ err }, 'Failed to save plugin metadata');
       throw err;
       throw err;
     }
     }
   }
   }
@@ -253,7 +256,7 @@ export class GrowiPluginService implements IGrowiPluginService {
           }
           }
         })
         })
         .catch((err) => {
         .catch((err) => {
-          logger.error(err);
+          logger.error({ err }, 'Failed to download file');
           rejects('Failed to download file.');
           rejects('Failed to download file.');
         });
         });
     });
     });
@@ -270,7 +273,7 @@ export class GrowiPluginService implements IGrowiPluginService {
         unzipStream.Extract({ path: destPath.toString() }),
         unzipStream.Extract({ path: destPath.toString() }),
       );
       );
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error({ err }, 'Failed to unzip');
       throw new Error('Failed to unzip.');
       throw new Error('Failed to unzip.');
     }
     }
   }
   }
@@ -345,7 +348,7 @@ export class GrowiPluginService implements IGrowiPluginService {
       plugin.meta = await generateTemplatePluginMeta(plugin, validationData);
       plugin.meta = await generateTemplatePluginMeta(plugin, validationData);
     }
     }
 
 
-    logger.info('Plugin detected => ', plugin);
+    logger.info({ plugin }, 'Plugin detected');
 
 
     return [plugin];
     return [plugin];
   }
   }
@@ -371,7 +374,10 @@ export class GrowiPluginService implements IGrowiPluginService {
     try {
     try {
       await GrowiPlugin.deleteOne({ _id: pluginId });
       await GrowiPlugin.deleteOne({ _id: pluginId });
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error(
+        { err },
+        'Failed to delete plugin from GrowiPlugin documents',
+      );
       throw new Error('Failed to delete plugin from GrowiPlugin documents.');
       throw new Error('Failed to delete plugin from GrowiPlugin documents.');
     }
     }
 
 
@@ -382,7 +388,7 @@ export class GrowiPluginService implements IGrowiPluginService {
         growiPlugins.installedPath,
         growiPlugins.installedPath,
       );
       );
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error({ err }, 'Invalid plugin installedPath');
       throw new Error(
       throw new Error(
         'The installedPath for the plugin is invalid, and the plugin has already been removed.',
         'The installedPath for the plugin is invalid, and the plugin has already been removed.',
       );
       );
@@ -392,7 +398,7 @@ export class GrowiPluginService implements IGrowiPluginService {
       try {
       try {
         await deleteFolder(growiPluginsPath);
         await deleteFolder(growiPluginsPath);
       } catch (err) {
       } catch (err) {
-        logger.error(err);
+        logger.error({ err }, 'Failed to delete plugin repository');
         throw new Error('Failed to delete plugin repository.');
         throw new Error('Failed to delete plugin repository.');
       }
       }
     } else {
     } else {
@@ -423,8 +429,8 @@ export class GrowiPluginService implements IGrowiPluginService {
       });
       });
     } catch (e) {
     } catch (e) {
       logger.error(
       logger.error(
+        { err: e },
         `Could not find the theme '${theme}' from GrowiPlugin documents.`,
         `Could not find the theme '${theme}' from GrowiPlugin documents.`,
-        e,
       );
       );
     }
     }
 
 
@@ -440,7 +446,10 @@ export class GrowiPluginService implements IGrowiPluginService {
       }
       }
       themeHref = `${PLUGIN_EXPRESS_STATIC_DIR}/${matchedPlugin.installedPath}/dist/${manifest[matchedThemeMetadata.manifestKey].file}`;
       themeHref = `${PLUGIN_EXPRESS_STATIC_DIR}/${matchedPlugin.installedPath}/dist/${manifest[matchedThemeMetadata.manifestKey].file}`;
     } catch (e) {
     } catch (e) {
-      logger.error(`Could not read manifest file for the theme '${theme}'`, e);
+      logger.error(
+        { err: e },
+        `Could not read manifest file for the theme '${theme}'`,
+      );
     }
     }
 
 
     return {
     return {
@@ -479,11 +488,11 @@ export class GrowiPluginService implements IGrowiPluginService {
             entries.push([growiPlugin.installedPath, href]);
             entries.push([growiPlugin.installedPath, href]);
           }
           }
         } catch (e) {
         } catch (e) {
-          logger.warn(e);
+          logger.warn({ err: e }, 'Failed to retrieve plugin manifest');
         }
         }
       });
       });
     } catch (e) {
     } catch (e) {
-      logger.error('Could not retrieve GrowiPlugin documents.', e);
+      logger.error({ err: e }, 'Could not retrieve GrowiPlugin documents.');
     }
     }
 
 
     return entries;
     return entries;

+ 2 - 2
apps/app/src/features/openai/client/components/AiAssistant/AiAssistantSidebar/AiAssistantSidebar.tsx

@@ -373,10 +373,10 @@ const AiAssistantSidebarSubstance: React.FC<
                   mainMessages.push(data.appendedMessage);
                   mainMessages.push(data.appendedMessage);
                 },
                 },
                 onDetectedDiff: (data) => {
                 onDetectedDiff: (data) => {
-                  logger.debug('sse diff', { data });
+                  logger.debug({ data }, 'sse diff');
                 },
                 },
                 onFinalized: (data) => {
                 onFinalized: (data) => {
-                  logger.debug('sse finalized', { data });
+                  logger.debug({ data }, 'sse finalized');
                 },
                 },
               });
               });
             } else if (trimmedLine.startsWith('error:')) {
             } else if (trimmedLine.startsWith('error:')) {

+ 2 - 2
apps/app/src/features/openai/server/routes/edit/index.ts

@@ -385,7 +385,7 @@ export const postMessageToEditHandlersFactory = (
 
 
         // Error handler
         // Error handler
         stream.once('error', (err) => {
         stream.once('error', (err) => {
-          logger.error('Stream error:', err);
+          logger.error({ err }, 'Stream error');
 
 
           // Clean up
           // Clean up
           streamProcessor.destroy();
           streamProcessor.destroy();
@@ -409,7 +409,7 @@ export const postMessageToEditHandlersFactory = (
         });
         });
       } catch (err) {
       } catch (err) {
         // Clean up and respond on error
         // Clean up and respond on error
-        logger.error('Error in edit handler:', err);
+        logger.error({ err }, 'Error in edit handler');
         streamProcessor.destroy();
         streamProcessor.destroy();
         return res.status(500).send(err.message);
         return res.status(500).send(err.message);
       }
       }

+ 22 - 13
apps/app/src/features/openai/server/services/editor-assistant/llm-response-stream-processor.ts

@@ -132,13 +132,16 @@ export class LlmResponseStreamProcessor {
             const validDiff = LlmEditorAssistantDiffSchema.safeParse(item);
             const validDiff = LlmEditorAssistantDiffSchema.safeParse(item);
             if (!validDiff.success) {
             if (!validDiff.success) {
               // Phase 2B: Enhanced error logging for diff validation failures
               // Phase 2B: Enhanced error logging for diff validation failures
-              logger.warn('Diff validation failed', {
-                errors: validDiff.error.errors,
-                item: JSON.stringify(item).substring(0, 200),
-                hasStartLine: 'startLine' in item,
-                hasSearch: 'search' in item,
-                hasReplace: 'replace' in item,
-              });
+              logger.warn(
+                {
+                  errors: validDiff.error.errors,
+                  item: JSON.stringify(item).substring(0, 200),
+                  hasStartLine: 'startLine' in item,
+                  hasSearch: 'search' in item,
+                  hasReplace: 'replace' in item,
+                },
+                'Diff validation failed',
+              );
               continue;
               continue;
             }
             }
 
 
@@ -146,10 +149,13 @@ export class LlmResponseStreamProcessor {
 
 
             // Phase 2B: Additional validation for required fields
             // Phase 2B: Additional validation for required fields
             if (!diff.startLine) {
             if (!diff.startLine) {
-              logger.error('startLine is required but missing in diff', {
-                search: diff.search?.substring(0, 50),
-                replace: diff.replace?.substring(0, 50),
-              });
+              logger.error(
+                {
+                  search: diff.search?.substring(0, 50),
+                  replace: diff.replace?.substring(0, 50),
+                },
+                'startLine is required but missing in diff',
+              );
               continue;
               continue;
             }
             }
 
 
@@ -187,7 +193,10 @@ export class LlmResponseStreamProcessor {
       }
       }
     } catch (e) {
     } catch (e) {
       // Ignore parse errors (expected for incomplete JSON)
       // Ignore parse errors (expected for incomplete JSON)
-      logger.debug('JSON parsing error (expected for partial data):', e);
+      logger.debug(
+        { err: e },
+        'JSON parsing error (expected for partial data)',
+      );
     }
     }
   }
   }
 
 
@@ -254,7 +263,7 @@ export class LlmResponseStreamProcessor {
       const finalMessage = this.extractFinalMessage(rawBuffer);
       const finalMessage = this.extractFinalMessage(rawBuffer);
       this.options?.dataFinalizedCallback?.(finalMessage, this.replacements);
       this.options?.dataFinalizedCallback?.(finalMessage, this.replacements);
     } catch (e) {
     } catch (e) {
-      logger.debug('Failed to parse final JSON response:', e);
+      logger.debug({ err: e }, 'Failed to parse final JSON response');
 
 
       // Send final notification even on error
       // Send final notification even on error
       const finalMessage = this.extractFinalMessage(rawBuffer);
       const finalMessage = this.extractFinalMessage(rawBuffer);

+ 49 - 31
apps/app/src/features/openai/server/services/openai.ts

@@ -259,8 +259,8 @@ class OpenaiService implements IOpenaiService {
           })
           })
           .catch((err) => {
           .catch((err) => {
             logger.error(
             logger.error(
-              `Failed to generate thread title for threadId ${thread.id}:`,
-              err,
+              { err },
+              `Failed to generate thread title for threadId ${thread.id}`,
             );
             );
           });
           });
       }
       }
@@ -282,9 +282,9 @@ class OpenaiService implements IOpenaiService {
           threadRelation.threadId,
           threadRelation.threadId,
           vectorStoreId,
           vectorStoreId,
         );
         );
-        logger.debug('Update thread', updatedThreadResponse);
+        logger.debug({ data: updatedThreadResponse }, 'Update thread');
       } catch (err) {
       } catch (err) {
-        logger.error(err);
+        logger.error({ err }, 'Failed to update thread');
       }
       }
     }
     }
   }
   }
@@ -321,7 +321,7 @@ class OpenaiService implements IOpenaiService {
       const deletedThreadResponse = await this.client.deleteThread(
       const deletedThreadResponse = await this.client.deleteThread(
         threadRelation.threadId,
         threadRelation.threadId,
       );
       );
-      logger.debug('Delete thread', deletedThreadResponse);
+      logger.debug({ data: deletedThreadResponse }, 'Delete thread');
       await threadRelation.remove();
       await threadRelation.remove();
     } catch (err) {
     } catch (err) {
       await openaiApiErrorHandler(err, {
       await openaiApiErrorHandler(err, {
@@ -351,13 +351,13 @@ class OpenaiService implements IOpenaiService {
         const deleteThreadResponse = await this.client.deleteThread(
         const deleteThreadResponse = await this.client.deleteThread(
           expiredThreadRelation.threadId,
           expiredThreadRelation.threadId,
         );
         );
-        logger.debug('Delete thread', deleteThreadResponse);
+        logger.debug({ data: deleteThreadResponse }, 'Delete thread');
         deletedThreadIds.push(expiredThreadRelation.threadId);
         deletedThreadIds.push(expiredThreadRelation.threadId);
 
 
         // sleep
         // sleep
         await new Promise((resolve) => setTimeout(resolve, apiCallInterval));
         await new Promise((resolve) => setTimeout(resolve, apiCallInterval));
       } catch (err) {
       } catch (err) {
-        logger.error(err);
+        logger.error({ err }, 'Failed to delete expired thread');
       }
       }
     }
     }
 
 
@@ -509,7 +509,7 @@ class OpenaiService implements IOpenaiService {
       const deleteVectorStoreResponse = await this.client.deleteVectorStore(
       const deleteVectorStoreResponse = await this.client.deleteVectorStore(
         vectorStoreDocument.vectorStoreId,
         vectorStoreDocument.vectorStoreId,
       );
       );
-      logger.debug('Delete vector store', deleteVectorStoreResponse);
+      logger.debug({ data: deleteVectorStoreResponse }, 'Delete vector store');
       await vectorStoreDocument.markAsDeleted();
       await vectorStoreDocument.markAsDeleted();
     } catch (err) {
     } catch (err) {
       await openaiApiErrorHandler(err, {
       await openaiApiErrorHandler(err, {
@@ -563,7 +563,7 @@ class OpenaiService implements IOpenaiService {
               attachment._id,
               attachment._id,
             );
             );
           } catch (err) {
           } catch (err) {
-            logger.error(err);
+            logger.error({ err }, 'Failed to upload attachment file');
           }
           }
         }
         }
         callback();
         callback();
@@ -647,7 +647,7 @@ class OpenaiService implements IOpenaiService {
     const fileUploadResult = await Promise.allSettled(workers);
     const fileUploadResult = await Promise.allSettled(workers);
     fileUploadResult.forEach((result) => {
     fileUploadResult.forEach((result) => {
       if (result.status === 'rejected') {
       if (result.status === 'rejected') {
-        logger.error(result.reason);
+        logger.error({ err: result.reason }, 'File upload failed');
       }
       }
     });
     });
 
 
@@ -677,14 +677,14 @@ class OpenaiService implements IOpenaiService {
           uploadedFileIds,
           uploadedFileIds,
         );
         );
       logger.debug(
       logger.debug(
+        { data: createVectorStoreFileBatchResponse },
         'Create vector store file',
         'Create vector store file',
-        createVectorStoreFileBatchResponse,
       );
       );
 
 
       // Set isAttachedToVectorStore: true when the uploaded file is attached to VectorStore
       // Set isAttachedToVectorStore: true when the uploaded file is attached to VectorStore
       await VectorStoreFileRelationModel.markAsAttachedToVectorStore(pageIds);
       await VectorStoreFileRelationModel.markAsAttachedToVectorStore(pageIds);
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error({ err }, 'Failed to create vector store file batch');
 
 
       // Delete all uploaded files if createVectorStoreFileBatch fails
       // Delete all uploaded files if createVectorStoreFileBatch fails
       for await (const pageId of pageIds) {
       for await (const pageId of pageIds) {
@@ -742,8 +742,8 @@ class OpenaiService implements IOpenaiService {
       const fileId = vectorStoreFileRelation.fileIds[0];
       const fileId = vectorStoreFileRelation.fileIds[0];
       const deleteFileResponse = await this.client.deleteFile(fileId);
       const deleteFileResponse = await this.client.deleteFile(fileId);
       logger.debug(
       logger.debug(
-        'Delete vector store file (attachment) ',
-        deleteFileResponse,
+        { data: deleteFileResponse },
+        'Delete vector store file (attachment)',
       );
       );
 
 
       // Delete related VectorStoreFileRelation document
       // Delete related VectorStoreFileRelation document
@@ -752,7 +752,10 @@ class OpenaiService implements IOpenaiService {
         await deleteAllAttachmentVectorStoreFileRelations();
         await deleteAllAttachmentVectorStoreFileRelations();
       }
       }
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error(
+        { err },
+        'Failed to delete vector store file for attachment',
+      );
       await openaiApiErrorHandler(err, {
       await openaiApiErrorHandler(err, {
         notFoundError: () => deleteAllAttachmentVectorStoreFileRelations(),
         notFoundError: () => deleteAllAttachmentVectorStoreFileRelations(),
       });
       });
@@ -781,7 +784,10 @@ class OpenaiService implements IOpenaiService {
               vectorStoreFileRelation,
               vectorStoreFileRelation,
             );
             );
           } catch (err) {
           } catch (err) {
-            logger.error(err);
+            logger.error(
+              { err },
+              'Failed to delete vector store file for attachment',
+            );
           }
           }
         }
         }
       }
       }
@@ -800,7 +806,7 @@ class OpenaiService implements IOpenaiService {
     for await (const fileId of vectorStoreFileRelation.fileIds) {
     for await (const fileId of vectorStoreFileRelation.fileIds) {
       try {
       try {
         const deleteFileResponse = await this.client.deleteFile(fileId);
         const deleteFileResponse = await this.client.deleteFile(fileId);
-        logger.debug('Delete vector store file', deleteFileResponse);
+        logger.debug({ data: deleteFileResponse }, 'Delete vector store file');
         deletedFileIds.push(fileId);
         deletedFileIds.push(fileId);
         if (apiCallInterval != null) {
         if (apiCallInterval != null) {
           // sleep
           // sleep
@@ -812,7 +818,7 @@ class OpenaiService implements IOpenaiService {
             deletedFileIds.push(fileId);
             deletedFileIds.push(fileId);
           },
           },
         });
         });
-        logger.error(err);
+        logger.error({ err }, 'Failed to delete file');
       }
       }
     }
     }
 
 
@@ -880,7 +886,7 @@ class OpenaiService implements IOpenaiService {
           apiCallInterval,
           apiCallInterval,
         );
         );
       } catch (err) {
       } catch (err) {
-        logger.error(err);
+        logger.error({ err }, 'Failed to delete vector store file');
       }
       }
     }
     }
   }
   }
@@ -896,7 +902,10 @@ class OpenaiService implements IOpenaiService {
     try {
     try {
       await this.deleteVectorStoreFileForAttachment(vectorStoreFileRelation);
       await this.deleteVectorStoreFileForAttachment(vectorStoreFileRelation);
     } catch (err) {
     } catch (err) {
-      logger.error(err);
+      logger.error(
+        { err },
+        'Failed to delete vector store file on attachment delete',
+      );
     }
     }
   }
   }
 
 
@@ -1002,10 +1011,13 @@ class OpenaiService implements IOpenaiService {
       }
       }
 
 
       logger.debug('--------- createVectorStoreFileOnPageCreate ---------');
       logger.debug('--------- createVectorStoreFileOnPageCreate ---------');
-      logger.debug('AccessScopeType of aiAssistant: ', aiAssistant.accessScope);
       logger.debug(
       logger.debug(
-        'VectorStoreFile pagePath to be created: ',
-        pagesToVectorize.map((page) => page.path),
+        { accessScope: aiAssistant.accessScope },
+        'AccessScopeType of aiAssistant',
+      );
+      logger.debug(
+        { pagePaths: pagesToVectorize.map((page) => page.path) },
+        'VectorStoreFile pagePath to be created',
       );
       );
       logger.debug('-----------------------------------------------------');
       logger.debug('-----------------------------------------------------');
 
 
@@ -1038,11 +1050,17 @@ class OpenaiService implements IOpenaiService {
       }
       }
 
 
       logger.debug('---------- updateVectorStoreOnPageUpdate ------------');
       logger.debug('---------- updateVectorStoreOnPageUpdate ------------');
-      logger.debug('AccessScopeType of aiAssistant: ', aiAssistant.accessScope);
-      logger.debug('PagePath of VectorStoreFile to be deleted: ', page.path);
       logger.debug(
       logger.debug(
-        'pagePath of VectorStoreFile to be created: ',
-        pagesToVectorize.map((page) => page.path),
+        { accessScope: aiAssistant.accessScope },
+        'AccessScopeType of aiAssistant',
+      );
+      logger.debug(
+        { pagePath: page.path },
+        'PagePath of VectorStoreFile to be deleted',
+      );
+      logger.debug(
+        { pagePaths: pagesToVectorize.map((page) => page.path) },
+        'pagePath of VectorStoreFile to be created',
       );
       );
       logger.debug('-----------------------------------------------------');
       logger.debug('-----------------------------------------------------');
 
 
@@ -1089,7 +1107,7 @@ class OpenaiService implements IOpenaiService {
       undefined,
       undefined,
       file.path,
       file.path,
     );
     );
-    logger.debug('Uploaded file', uploadedFile);
+    logger.debug({ data: uploadedFile }, 'Uploaded file');
 
 
     for await (const aiAssistant of aiAssistants) {
     for await (const aiAssistant of aiAssistants) {
       const pagesToVectorize = await this.filterPagesByAccessScope(
       const pagesToVectorize = await this.filterPagesByAccessScope(
@@ -1152,8 +1170,8 @@ class OpenaiService implements IOpenaiService {
       ) {
       ) {
         try {
         try {
           logger.debug(
           logger.debug(
-            'Target page path for VectorStoreFile generation: ',
-            chunk.map((page) => page.path),
+            { pagePaths: chunk.map((page) => page.path) },
+            'Target page path for VectorStoreFile generation',
           );
           );
           await createVectorStoreFile(vectorStoreRelation, chunk);
           await createVectorStoreFile(vectorStoreRelation, chunk);
           this.push(chunk);
           this.push(chunk);
@@ -1585,7 +1603,7 @@ class OpenaiService implements IOpenaiService {
       0,
       0,
     );
     );
 
 
-    logger.debug('TotalPageCount: ', totalPageCount);
+    logger.debug({ totalPageCount }, 'TotalPageCount');
 
 
     const limitLearnablePageCountPerAssistant = configManager.getConfig(
     const limitLearnablePageCountPerAssistant = configManager.getConfig(
       'openai:limitLearnablePageCountPerAssistant',
       'openai:limitLearnablePageCountPerAssistant',

+ 5 - 4
apps/app/src/features/opentelemetry/server/custom-resource-attributes/application-resource-attributes.ts

@@ -33,13 +33,14 @@ export async function getApplicationResourceAttributes(): Promise<Attributes> {
         growiInfo.additionalInfo?.installedAtByOldestUser?.toISOString(),
         growiInfo.additionalInfo?.installedAtByOldestUser?.toISOString(),
     };
     };
 
 
-    logger.info('Application resource attributes collected', { attributes });
+    logger.info({ attributes }, 'Application resource attributes collected');
 
 
     return attributes;
     return attributes;
   } catch (error) {
   } catch (error) {
-    logger.error('Failed to collect application resource attributes', {
-      error,
-    });
+    logger.error(
+      { err: error },
+      'Failed to collect application resource attributes',
+    );
     return {};
     return {};
   }
   }
 }
 }

+ 1 - 1
apps/app/src/features/opentelemetry/server/custom-resource-attributes/os-resource-attributes.ts

@@ -28,7 +28,7 @@ export function getOsResourceAttributes(): Attributes {
     'os.totalmem': osInfo.totalmem,
     'os.totalmem': osInfo.totalmem,
   };
   };
 
 
-  logger.info('OS resource attributes collected', { attributes });
+  logger.info({ attributes }, 'OS resource attributes collected');
 
 
   return attributes;
   return attributes;
 }
 }

+ 3 - 3
apps/app/src/pages/common-props/commons.ts

@@ -116,23 +116,23 @@ function isValidCommonEachRouteProps(
       p.nextjsRoutingPage !== undefined
       p.nextjsRoutingPage !== undefined
     ) {
     ) {
       logger.warn(
       logger.warn(
-        'isValidCommonEachRouteProps: nextjsRoutingPage is not a string or null',
         { nextjsRoutingPage: p.nextjsRoutingPage },
         { nextjsRoutingPage: p.nextjsRoutingPage },
+        'isValidCommonEachRouteProps: nextjsRoutingPage is not a string or null',
       );
       );
       return false;
       return false;
     }
     }
   }
   }
   if (typeof p.currentPathname !== 'string') {
   if (typeof p.currentPathname !== 'string') {
     logger.warn(
     logger.warn(
-      'isValidCommonEachRouteProps: currentPathname is not a string',
       { currentPathname: p.currentPathname },
       { currentPathname: p.currentPathname },
+      'isValidCommonEachRouteProps: currentPathname is not a string',
     );
     );
     return false;
     return false;
   }
   }
   if (typeof p.isMaintenanceMode !== 'boolean') {
   if (typeof p.isMaintenanceMode !== 'boolean') {
     logger.warn(
     logger.warn(
-      'isValidCommonEachRouteProps: isMaintenanceMode is not a boolean',
       { isMaintenanceMode: p.isMaintenanceMode },
       { isMaintenanceMode: p.isMaintenanceMode },
+      'isValidCommonEachRouteProps: isMaintenanceMode is not a boolean',
     );
     );
     return false;
     return false;
   }
   }

+ 3 - 3
apps/app/src/pages/general-page/type-guards.ts

@@ -20,15 +20,15 @@ export function isValidGeneralPageInitialProps(
   // CommonPageInitialProps
   // CommonPageInitialProps
   if (p.nextjsRoutingType === NextjsRoutingType.SAME_ROUTE) {
   if (p.nextjsRoutingType === NextjsRoutingType.SAME_ROUTE) {
     logger.warn(
     logger.warn(
-      'isValidGeneralPageInitialProps: nextjsRoutingType must be equal to NextjsRoutingType.INITIAL or NextjsRoutingType.FROM_OUTSIDE',
       { nextjsRoutingType: p.nextjsRoutingType },
       { nextjsRoutingType: p.nextjsRoutingType },
+      'isValidGeneralPageInitialProps: nextjsRoutingType must be equal to NextjsRoutingType.INITIAL or NextjsRoutingType.FROM_OUTSIDE',
     );
     );
     return false;
     return false;
   }
   }
   if (typeof p.growiVersion !== 'string') {
   if (typeof p.growiVersion !== 'string') {
     logger.warn(
     logger.warn(
-      'isValidGeneralPageInitialProps: growiVersion is not a string',
       { growiVersion: p.growiVersion },
       { growiVersion: p.growiVersion },
+      'isValidGeneralPageInitialProps: growiVersion is not a string',
     );
     );
     return false;
     return false;
   }
   }
@@ -37,8 +37,8 @@ export function isValidGeneralPageInitialProps(
   if (p.meta != null && typeof p.meta === 'object') {
   if (p.meta != null && typeof p.meta === 'object') {
     if (!isIPageInfo(p.meta)) {
     if (!isIPageInfo(p.meta)) {
       logger.warn(
       logger.warn(
-        'isValidGeneralPageInitialProps: meta is not a valid IPageInfo',
         { meta: p.meta },
         { meta: p.meta },
+        'isValidGeneralPageInitialProps: meta is not a valid IPageInfo',
       );
       );
       return false;
       return false;
     }
     }

+ 3 - 5
apps/app/src/server/app.ts

@@ -1,5 +1,3 @@
-import type Logger from 'bunyan';
-
 import {
 import {
   initInstrumentation,
   initInstrumentation,
   setupAdditionalResourceAttributes,
   setupAdditionalResourceAttributes,
@@ -8,17 +6,17 @@ import {
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { hasProcessFlag } from '~/utils/process-utils';
 import { hasProcessFlag } from '~/utils/process-utils';
 
 
-const logger: Logger = loggerFactory('growi');
+const logger = loggerFactory('growi');
 
 
 /** **********************************
 /** **********************************
  *          Main Process
  *          Main Process
  ********************************** */
  ********************************** */
 process.on('uncaughtException', (err?: Error) => {
 process.on('uncaughtException', (err?: Error) => {
-  logger.error('Uncaught Exception: ', err);
+  logger.error({ err }, 'Uncaught Exception');
 });
 });
 
 
 process.on('unhandledRejection', (reason, p) => {
 process.on('unhandledRejection', (reason, p) => {
-  logger.error('Unhandled Rejection: Promise:', p, 'Reason:', reason);
+  logger.error({ reason, promise: p }, 'Unhandled Rejection');
 });
 });
 
 
 async function main() {
 async function main() {

+ 2 - 2
apps/app/src/server/events/user.ts

@@ -48,10 +48,10 @@ class UserEvent extends EventEmitter {
         const body = `# ${user.username}\nThis is ${user.username}'s page`;
         const body = `# ${user.username}\nThis is ${user.username}'s page`;
 
 
         await this.crowi.pageService.create(userHomepagePath, body, user, {});
         await this.crowi.pageService.create(userHomepagePath, body, user, {});
-        logger.debug('User page created', page);
+        logger.debug({ page }, 'User page created');
       }
       }
     } catch (err) {
     } catch (err) {
-      logger.error('Failed to create user page', err);
+      logger.error({ err }, 'Failed to create user page');
     }
     }
   }
   }
 }
 }

+ 4 - 1
apps/app/src/server/middlewares/access-token-parser/api-token.ts

@@ -27,7 +27,10 @@ export const parserForApiToken = async (
     return;
     return;
   }
   }
 
 
-  logger.debug('accessToken is', accessToken);
+  logger.debug(
+    { accessToken: `${accessToken.slice(0, 4)}...${accessToken.slice(-4)}` },
+    'accessToken is',
+  );
 
 
   const User = mongoose.model<HydratedDocument<IUser>, { findUserByApiToken }>(
   const User = mongoose.model<HydratedDocument<IUser>, { findUserByApiToken }>(
     'User',
     'User',

+ 3 - 3
apps/app/src/server/middlewares/apiv1-form-validator.ts

@@ -8,9 +8,9 @@ import ApiResponse from '../util/apiResponse';
 const logger = loggerFactory('growi:middlewares:ApiV1FormValidator');
 const logger = loggerFactory('growi:middlewares:ApiV1FormValidator');
 
 
 export default (req: Request, res: Response, next: NextFunction): void => {
 export default (req: Request, res: Response, next: NextFunction): void => {
-  logger.debug('req.query', req.query);
-  logger.debug('req.params', req.params);
-  logger.debug('req.body', req.body);
+  logger.debug({ query: req.query }, 'req.query');
+  logger.debug({ params: req.params }, 'req.params');
+  logger.debug({ body: req.body }, 'req.body');
 
 
   const errObjArray = validationResult(req);
   const errObjArray = validationResult(req);
   if (errObjArray.isEmpty()) {
   if (errObjArray.isEmpty()) {

+ 3 - 3
apps/app/src/server/middlewares/apiv3-form-validator.ts

@@ -11,9 +11,9 @@ export const apiV3FormValidator = (
   res: Response & { apiv3Err },
   res: Response & { apiv3Err },
   next: NextFunction,
   next: NextFunction,
 ): void => {
 ): void => {
-  logger.debug('req.query', req.query);
-  logger.debug('req.params', req.params);
-  logger.debug('req.body', req.body);
+  logger.debug({ query: req.query }, 'req.query');
+  logger.debug({ params: req.params }, 'req.params');
+  logger.debug({ body: req.body }, 'req.body');
 
 
   const errObjArray = validationResult(req);
   const errObjArray = validationResult(req);
   if (errObjArray.isEmpty()) {
   if (errObjArray.isEmpty()) {

+ 16 - 10
apps/app/src/server/middlewares/certify-shared-page-attachment/validate-referer/validate-referer.ts

@@ -38,16 +38,19 @@ export const validateReferer = (
     refererUrl.hostname !== siteUrl.hostname ||
     refererUrl.hostname !== siteUrl.hostname ||
     refererUrl.port !== siteUrl.port
     refererUrl.port !== siteUrl.port
   ) {
   ) {
-    logger.warn('The hostname or port mismatched.', {
-      refererUrl: {
-        hostname: refererUrl.hostname,
-        port: refererUrl.port,
-      },
-      siteUrl: {
-        hostname: siteUrl.hostname,
-        port: siteUrl.port,
+    logger.warn(
+      {
+        refererUrl: {
+          hostname: refererUrl.hostname,
+          port: refererUrl.port,
+        },
+        siteUrl: {
+          hostname: siteUrl.hostname,
+          port: siteUrl.port,
+        },
       },
       },
-    });
+      'The hostname or port mismatched.',
+    );
     return false;
     return false;
   }
   }
 
 
@@ -60,7 +63,10 @@ export const validateReferer = (
     return false;
     return false;
   }
   }
   if (match.groups?.shareLinkId == null) {
   if (match.groups?.shareLinkId == null) {
-    logger.warn(`The pathname ('${refererUrl.pathname}') is invalid.`, match);
+    logger.warn(
+      { match },
+      `The pathname ('${refererUrl.pathname}') is invalid.`,
+    );
     return false;
     return false;
   }
   }
 
 

+ 1 - 1
apps/app/src/server/middlewares/login-required.ts

@@ -49,7 +49,7 @@ const loginRequiredFactory = (
 
 
     // check the route config and ACL
     // check the route config and ACL
     if (isGuestAllowed && crowi.aclService.isGuestAllowedToRead()) {
     if (isGuestAllowed && crowi.aclService.isGuestAllowedToRead()) {
-      logger.debug('Allowed to read: ', req.path);
+      logger.debug({ path: req.path }, 'Allowed to read');
       return next();
       return next();
     }
     }
 
 

+ 6 - 3
apps/app/src/server/middlewares/safe-redirect.ts

@@ -61,17 +61,20 @@ const factory = (whitelistOfHosts: string[]) => {
         const isWhitelisted = isInWhitelist(whitelistOfHosts, redirectTo);
         const isWhitelisted = isInWhitelist(whitelistOfHosts, redirectTo);
         if (isWhitelisted) {
         if (isWhitelisted) {
           logger.debug(
           logger.debug(
+            { whitelist: whitelistOfHosts },
             `Requested redirect URL (${redirectTo}) is in whitelist.`,
             `Requested redirect URL (${redirectTo}) is in whitelist.`,
-            `whitelist=${whitelistOfHosts}`,
           );
           );
           return res.redirect(redirectTo);
           return res.redirect(redirectTo);
         }
         }
         logger.debug(
         logger.debug(
+          { whitelist: whitelistOfHosts },
           `Requested redirect URL (${redirectTo}) is NOT in whitelist.`,
           `Requested redirect URL (${redirectTo}) is NOT in whitelist.`,
-          `whitelist=${whitelistOfHosts}`,
         );
         );
       } catch (err) {
       } catch (err) {
-        logger.warn(`Requested redirect URL (${redirectTo}) is invalid.`, err);
+        logger.warn(
+          { err },
+          `Requested redirect URL (${redirectTo}) is invalid.`,
+        );
       }
       }
 
 
       logger.warn(
       logger.warn(

+ 1 - 1
apps/app/src/server/models/activity.ts

@@ -100,7 +100,7 @@ activitySchema.index(
 activitySchema.plugin(mongoosePaginate);
 activitySchema.plugin(mongoosePaginate);
 
 
 activitySchema.post('save', function () {
 activitySchema.post('save', function () {
-  logger.debug('activity has been created', this);
+  logger.debug({ activity: this }, 'activity has been created');
 });
 });
 
 
 activitySchema.statics.createByParameters = async function (
 activitySchema.statics.createByParameters = async function (

+ 1 - 1
apps/app/src/server/models/external-account.ts

@@ -80,7 +80,7 @@ schema.statics.findOrRegister = function (
   return this.findOne({ providerType, accountId }).then((account) => {
   return this.findOne({ providerType, accountId }).then((account) => {
     // ExternalAccount is found
     // ExternalAccount is found
     if (account != null) {
     if (account != null) {
-      logger.debug(`ExternalAccount '${accountId}' is found `, account);
+      logger.debug({ account }, `ExternalAccount '${accountId}' is found`);
       return account;
       return account;
     }
     }
 
 

+ 2 - 2
apps/app/src/server/models/user-group-relation.ts

@@ -104,7 +104,7 @@ schema.statics.findAllRelation = function () {
  * @memberof UserGroupRelation
  * @memberof UserGroupRelation
  */
  */
 schema.statics.findAllRelationForUserGroup = function (userGroup) {
 schema.statics.findAllRelationForUserGroup = function (userGroup) {
-  logger.debug('findAllRelationForUserGroup is called', userGroup);
+  logger.debug({ userGroup }, 'findAllRelationForUserGroup is called');
   // biome-ignore lint/plugin: allow populate for backward compatibility
   // biome-ignore lint/plugin: allow populate for backward compatibility
   return this.find({ relatedGroup: userGroup }).populate('relatedUser').exec();
   return this.find({ relatedGroup: userGroup }).populate('relatedUser').exec();
 };
 };
@@ -236,7 +236,7 @@ schema.statics.findUserByNotRelatedGroup = function (userGroup, queryOptions) {
       $or: searthField,
       $or: searthField,
     };
     };
 
 
-    logger.debug('findUserByNotRelatedGroup ', query);
+    logger.debug({ query }, 'findUserByNotRelatedGroup');
     return User.find(query).exec();
     return User.find(query).exec();
   });
   });
 };
 };

+ 2 - 2
apps/app/src/server/routes/apiv3/bookmark-folder.ts

@@ -188,7 +188,7 @@ module.exports = (crowi: Crowi) => {
 
 
       try {
       try {
         const bookmarkFolder = await BookmarkFolder.createByParameters(params);
         const bookmarkFolder = await BookmarkFolder.createByParameters(params);
-        logger.debug('bookmark folder created', bookmarkFolder);
+        logger.debug({ bookmarkFolder }, 'bookmark folder created');
         return res.apiv3({ bookmarkFolder });
         return res.apiv3({ bookmarkFolder });
       } catch (err) {
       } catch (err) {
         logger.error(err);
         logger.error(err);
@@ -467,7 +467,7 @@ module.exports = (crowi: Crowi) => {
             userId,
             userId,
             folderId,
             folderId,
           );
           );
-        logger.debug('bookmark added to folder', bookmarkFolder);
+        logger.debug({ bookmarkFolder }, 'bookmark added to folder');
         return res.apiv3({ bookmarkFolder });
         return res.apiv3({ bookmarkFolder });
       } catch (err) {
       } catch (err) {
         logger.error(err);
         logger.error(err);

+ 10 - 10
apps/app/src/server/routes/apiv3/g2g-transfer.ts

@@ -464,7 +464,7 @@ module.exports = (crowi: Crowi): Router => {
           fileName.length === 0 ||
           fileName.length === 0 ||
           fileName.length > 256
           fileName.length > 256
         ) {
         ) {
-          logger.warn('Invalid fileName in attachment metadata.', { fileName });
+          logger.warn({ fileName }, 'Invalid fileName in attachment metadata.');
           return res.apiv3Err(
           return res.apiv3Err(
             new ErrorV3(
             new ErrorV3(
               'Invalid fileName in attachment metadata.',
               'Invalid fileName in attachment metadata.',
@@ -478,7 +478,7 @@ module.exports = (crowi: Crowi): Router => {
           !Number.isInteger(fileSize) ||
           !Number.isInteger(fileSize) ||
           fileSize < 0
           fileSize < 0
         ) {
         ) {
-          logger.warn('Invalid fileSize in attachment metadata.', { fileSize });
+          logger.warn({ fileSize }, 'Invalid fileSize in attachment metadata.');
           return res.apiv3Err(
           return res.apiv3Err(
             new ErrorV3(
             new ErrorV3(
               'Invalid fileSize in attachment metadata.',
               'Invalid fileSize in attachment metadata.',
@@ -489,10 +489,10 @@ module.exports = (crowi: Crowi): Router => {
         }
         }
         const count = await Attachment.countDocuments({ fileName, fileSize });
         const count = await Attachment.countDocuments({ fileName, fileSize });
         if (count === 0) {
         if (count === 0) {
-          logger.warn('Attachment not found in collection.', {
-            fileName,
-            fileSize,
-          });
+          logger.warn(
+            { fileName, fileSize },
+            'Attachment not found in collection.',
+          );
           return res.apiv3Err(
           return res.apiv3Err(
             new ErrorV3(
             new ErrorV3(
               'Attachment not found in collection.',
               'Attachment not found in collection.',
@@ -526,10 +526,10 @@ module.exports = (crowi: Crowi): Router => {
       // Normalize the path to prevent path traversal attacks
       // Normalize the path to prevent path traversal attacks
       const resolvedFilePath = path.resolve(file.path);
       const resolvedFilePath = path.resolve(file.path);
       if (!isPathWithinBase(resolvedFilePath, importService.baseDir)) {
       if (!isPathWithinBase(resolvedFilePath, importService.baseDir)) {
-        logger.error('Path traversal attack detected', {
-          filePath: resolvedFilePath,
-          baseDir: importService.baseDir,
-        });
+        logger.error(
+          { filePath: resolvedFilePath, baseDir: importService.baseDir },
+          'Path traversal attack detected',
+        );
         return res.apiv3Err(
         return res.apiv3Err(
           new ErrorV3('Invalid file path.', 'invalid_path'),
           new ErrorV3('Invalid file path.', 'invalid_path'),
           400,
           400,

+ 18 - 12
apps/app/src/server/routes/apiv3/page/update-page.ts

@@ -145,7 +145,7 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
         req.user,
         req.user,
       );
       );
     } catch (err) {
     } catch (err) {
-      logger.error('Edit notification failed', err);
+      logger.error({ err }, 'Edit notification failed');
     }
     }
 
 
     // user notification
     // user notification
@@ -163,11 +163,14 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
         );
         );
         for (const result of results) {
         for (const result of results) {
           if (result.status === 'rejected') {
           if (result.status === 'rejected') {
-            logger.error('Create user notification failed', result.reason);
+            logger.error(
+              { err: result.reason },
+              'Create user notification failed',
+            );
           }
           }
         }
         }
       } catch (err) {
       } catch (err) {
-        logger.error('Create user notification failed', err);
+        logger.error({ err }, 'Create user notification failed');
       }
       }
     }
     }
 
 
@@ -180,7 +183,7 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
         const openaiService = getOpenaiService();
         const openaiService = getOpenaiService();
         await openaiService?.updateVectorStoreFileOnPageUpdate(updatedPage);
         await openaiService?.updateVectorStoreFileOnPageUpdate(updatedPage);
       } catch (err) {
       } catch (err) {
-        logger.error('Rebuild vector store failed', err);
+        logger.error({ err }, 'Rebuild vector store failed');
       }
       }
     }
     }
   }
   }
@@ -305,11 +308,14 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
           try {
           try {
             previousRevision = await Revision.findById(sanitizeRevisionId);
             previousRevision = await Revision.findById(sanitizeRevisionId);
           } catch (error) {
           } catch (error) {
-            logger.error('Failed to fetch previousRevision by revisionId', {
-              revisionId: sanitizeRevisionId,
-              pageId: currentPage._id,
-              error,
-            });
+            logger.error(
+              {
+                revisionId: sanitizeRevisionId,
+                pageId: currentPage._id,
+                err: error,
+              },
+              'Failed to fetch previousRevision by revisionId',
+            );
           }
           }
         }
         }
 
 
@@ -319,12 +325,12 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
             previousRevision = await Revision.findById(currentPage.revision);
             previousRevision = await Revision.findById(currentPage.revision);
           } catch (error) {
           } catch (error) {
             logger.error(
             logger.error(
-              'Failed to fetch previousRevision by currentPage.revision',
               {
               {
                 pageId: currentPage._id,
                 pageId: currentPage._id,
                 revisionId: currentPage.revision,
                 revisionId: currentPage.revision,
-                error,
+                err: error,
               },
               },
+              'Failed to fetch previousRevision by currentPage.revision',
             );
             );
           }
           }
         }
         }
@@ -339,7 +345,7 @@ export const updatePageHandlersFactory = (crowi: Crowi): RequestHandler[] => {
           options,
           options,
         );
         );
       } catch (err) {
       } catch (err) {
-        logger.error('Error occurred while updating a page.', err);
+        logger.error({ err }, 'Error occurred while updating a page.');
         return res.apiv3Err(err);
         return res.apiv3Err(err);
       }
       }
 
 

+ 2 - 2
apps/app/src/server/service/config-manager/config-loader.ts

@@ -31,7 +31,7 @@ export class ConfigLoader implements IConfigLoader<ConfigKey, ConfigValues> {
       };
       };
     }
     }
 
 
-    logger.debug('loadFromEnv', envConfig);
+    logger.debug({ envConfig }, 'loadFromEnv');
 
 
     return envConfig;
     return envConfig;
   }
   }
@@ -62,7 +62,7 @@ export class ConfigLoader implements IConfigLoader<ConfigKey, ConfigValues> {
       };
       };
     }
     }
 
 
-    logger.debug('loadFromDB', dbConfig);
+    logger.debug({ dbConfig }, 'loadFromDB');
     return dbConfig;
     return dbConfig;
   }
   }
 
 

+ 1 - 1
apps/app/src/server/service/external-account.ts

@@ -57,7 +57,7 @@ class ExternalAccountService {
           );
           );
           return ExternalAccount.associate(providerId, userInfo.id, err.user);
           return ExternalAccount.associate(providerId, userInfo.id, err.user);
         }
         }
-        logger.error('provider-DuplicatedUsernameException', providerId);
+        logger.error({ providerId }, 'provider-DuplicatedUsernameException');
 
 
         throw new ErrorV3(
         throw new ErrorV3(
           'message.provider_duplicated_username_exception',
           'message.provider_duplicated_username_exception',

+ 1 - 1
apps/app/src/server/service/file-uploader/gridfs.ts

@@ -250,7 +250,7 @@ module.exports = (crowi: Crowi) => {
     try {
     try {
       // Add error handling to prevent resource leaks
       // Add error handling to prevent resource leaks
       readable.on('error', (err) => {
       readable.on('error', (err) => {
-        logger.error('Readable stream error:', err);
+        logger.error({ err }, 'Readable stream error');
         readable.destroy();
         readable.destroy();
         throw err;
         throw err;
       });
       });

+ 38 - 26
apps/app/src/server/service/mail/mail.ts

@@ -82,8 +82,8 @@ class MailService implements S2sMessageHandlable {
         await s2sMessagingService.publish(s2sMessage);
         await s2sMessagingService.publish(s2sMessage);
       } catch (e) {
       } catch (e) {
         logger.error(
         logger.error(
-          'Failed to publish update message with S2sMessagingService: ',
-          e.message,
+          { err: e },
+          'Failed to publish update message with S2sMessagingService',
         );
         );
       }
       }
     }
     }
@@ -161,14 +161,17 @@ class MailService implements S2sMessageHandlable {
     for (let attempt = 1; attempt <= maxRetries; attempt++) {
     for (let attempt = 1; attempt <= maxRetries; attempt++) {
       try {
       try {
         const result = await this.mailer.sendMail(config);
         const result = await this.mailer.sendMail(config);
-        logger.info('OAuth 2.0 email sent successfully', {
-          messageId: result.messageId,
-          from: config.from,
-          recipient: config.to,
-          attempt,
-          clientId: maskedClientId,
-          tag: 'oauth2_email_success',
-        });
+        logger.info(
+          {
+            messageId: result.messageId,
+            from: config.from,
+            recipient: config.to,
+            attempt,
+            clientId: maskedClientId,
+            tag: 'oauth2_email_success',
+          },
+          'OAuth 2.0 email sent successfully',
+        );
         return result;
         return result;
       } catch (error: unknown) {
       } catch (error: unknown) {
         const err = error as Error & { code?: string };
         const err = error as Error & { code?: string };
@@ -182,9 +185,8 @@ class MailService implements S2sMessageHandlable {
         }
         }
 
 
         logger.error(
         logger.error(
-          `OAuth 2.0 email send failed (attempt ${attempt}/${maxRetries})`,
           {
           {
-            error: err.message,
+            err,
             code: err.code,
             code: err.code,
             user: config.from,
             user: config.from,
             recipient: config.to,
             recipient: config.to,
@@ -193,6 +195,7 @@ class MailService implements S2sMessageHandlable {
             timestamp: new Date().toISOString(),
             timestamp: new Date().toISOString(),
             tag: monitoringTag,
             tag: monitoringTag,
           },
           },
+          `OAuth 2.0 email send failed (attempt ${attempt}/${maxRetries})`,
         );
         );
 
 
         if (attempt === maxRetries) {
         if (attempt === maxRetries) {
@@ -232,17 +235,23 @@ class MailService implements S2sMessageHandlable {
 
 
       await FailedEmail.create(failedEmail);
       await FailedEmail.create(failedEmail);
 
 
-      logger.error('Failed email stored for manual review', {
-        recipient: config.to,
-        errorMessage: error.message,
-        errorCode: error.code,
-      });
+      logger.error(
+        {
+          recipient: config.to,
+          errorMessage: error.message,
+          errorCode: error.code,
+        },
+        'Failed email stored for manual review',
+      );
     } catch (err: unknown) {
     } catch (err: unknown) {
       const storeError = err as Error;
       const storeError = err as Error;
-      logger.error('Failed to store failed email', {
-        error: storeError.message,
-        originalError: error.message,
-      });
+      logger.error(
+        {
+          err: storeError,
+          originalError: error.message,
+        },
+        'Failed to store failed email',
+      );
       throw new Error(`Failed to store failed email: ${storeError.message}`);
       throw new Error(`Failed to store failed email: ${storeError.message}`);
     }
     }
   }
   }
@@ -270,11 +279,14 @@ class MailService implements S2sMessageHandlable {
 
 
     // Use sendWithRetry for OAuth 2.0 to handle token refresh failures with exponential backoff
     // Use sendWithRetry for OAuth 2.0 to handle token refresh failures with exponential backoff
     if (transmissionMethod === 'oauth2') {
     if (transmissionMethod === 'oauth2') {
-      logger.debug('Sending email via OAuth2 with config:', {
-        from: mailConfig.from,
-        to: mailConfig.to,
-        subject: mailConfig.subject,
-      });
+      logger.debug(
+        {
+          from: mailConfig.from,
+          to: mailConfig.to,
+          subject: mailConfig.subject,
+        },
+        'Sending email via OAuth2 with config',
+      );
       return this.sendWithRetry(mailConfig as EmailConfig);
       return this.sendWithRetry(mailConfig as EmailConfig);
     }
     }
 
 

+ 1 - 1
apps/app/src/server/service/mail/oauth2.ts

@@ -71,7 +71,7 @@ export function createOAuth2Client(
 
 
   const client = nodemailer.createTransport(option);
   const client = nodemailer.createTransport(option);
 
 
-  logger.debug('mailer set up for OAuth2', client);
+  logger.debug('mailer set up for OAuth2');
 
 
   return client;
   return client;
 }
 }

+ 1 - 1
apps/app/src/server/service/mail/ses.ts

@@ -39,7 +39,7 @@ export function createSESClient(
 
 
   const client = nodemailer.createTransport(ses(option));
   const client = nodemailer.createTransport(ses(option));
 
 
-  logger.debug('mailer set up for SES', client);
+  logger.debug('mailer set up for SES');
 
 
   return client;
   return client;
 }
 }

+ 2 - 2
apps/app/src/server/service/mail/smtp.ts

@@ -23,7 +23,7 @@ export function createSMTPClient(
   configManager: IConfigManagerForApp,
   configManager: IConfigManagerForApp,
   option?: SMTPTransport.Options,
   option?: SMTPTransport.Options,
 ): Transporter | null {
 ): Transporter | null {
-  logger.debug('createSMTPClient option', option);
+  logger.debug('createSMTPClient called');
 
 
   let smtpOption: SMTPTransport.Options;
   let smtpOption: SMTPTransport.Options;
 
 
@@ -58,7 +58,7 @@ export function createSMTPClient(
 
 
   const client = nodemailer.createTransport(smtpOption);
   const client = nodemailer.createTransport(smtpOption);
 
 
-  logger.debug('mailer set up for SMTP', client);
+  logger.debug('mailer set up for SMTP');
 
 
   return client;
   return client;
 }
 }

+ 3 - 3
apps/app/src/server/service/page/events/seen.ts

@@ -24,13 +24,13 @@ export const onSeen = async (
     const page = await Page.findById(pageId);
     const page = await Page.findById(pageId);
 
 
     if (page == null) {
     if (page == null) {
-      logger.warn('onSeen: page not found', { pageId });
+      logger.warn({ pageId }, 'onSeen: page not found');
       return;
       return;
     }
     }
 
 
     await page.seen(user);
     await page.seen(user);
-    logger.debug('onSeen: successfully marked page as seen', { pageId });
+    logger.debug({ pageId }, 'onSeen: successfully marked page as seen');
   } catch (err) {
   } catch (err) {
-    logger.error('onSeen: failed to mark page as seen', err);
+    logger.error({ err }, 'onSeen: failed to mark page as seen');
   }
   }
 };
 };

+ 6 - 9
apps/app/src/server/service/page/index.ts

@@ -2406,7 +2406,7 @@ class PageService implements IPageService {
     const ids = pages.map((page) => page._id);
     const ids = pages.map((page) => page._id);
     const paths = pages.map((page) => page.path);
     const paths = pages.map((page) => page.path);
 
 
-    logger.debug('Deleting completely', paths);
+    logger.debug({ paths }, 'Deleting completely');
 
 
     await this.deleteCompletelyOperation(ids, paths);
     await this.deleteCompletelyOperation(ids, paths);
 
 
@@ -2461,7 +2461,7 @@ class PageService implements IPageService {
     const ids = [page._id];
     const ids = [page._id];
     const paths = [page.path];
     const paths = [page.path];
 
 
-    logger.debug('Deleting completely', paths);
+    logger.debug({ paths }, 'Deleting completely');
 
 
     const parameters = {
     const parameters = {
       ip: activityParameters.ip,
       ip: activityParameters.ip,
@@ -2598,7 +2598,7 @@ class PageService implements IPageService {
     const ids = [page._id];
     const ids = [page._id];
     const paths = [page.path];
     const paths = [page.path];
 
 
-    logger.debug('Deleting completely', paths);
+    logger.debug({ paths }, 'Deleting completely');
 
 
     await this.deleteCompletelyOperation(ids, paths);
     await this.deleteCompletelyOperation(ids, paths);
 
 
@@ -3692,8 +3692,8 @@ class PageService implements IPageService {
         paths: nonNormalizablePagePaths,
         paths: nonNormalizablePagePaths,
       });
       });
       logger.debug(
       logger.debug(
+        { paths: nonNormalizablePagePaths },
         'Some pages could not be converted.',
         'Some pages could not be converted.',
-        nonNormalizablePagePaths,
       );
       );
     }
     }
 
 
@@ -4219,8 +4219,8 @@ class PageService implements IPageService {
           // Throw if any error is found
           // Throw if any error is found
           if (res.result.writeErrors.length > 0) {
           if (res.result.writeErrors.length > 0) {
             logger.error(
             logger.error(
+              { writeErrors: res.result.writeErrors },
               'Failed to migrate some pages',
               'Failed to migrate some pages',
-              res.result.writeErrors,
             );
             );
             socket?.emit(SocketEventName.PMEnded, { isSucceeded: false });
             socket?.emit(SocketEventName.PMEnded, { isSucceeded: false });
             throw Error('Failed to migrate some pages');
             throw Error('Failed to migrate some pages');
@@ -4230,11 +4230,8 @@ class PageService implements IPageService {
           if (res.result.nModified === 0 && res.result.nMatched === 0) {
           if (res.result.nModified === 0 && res.result.nMatched === 0) {
             shouldContinue = false;
             shouldContinue = false;
             logger.error(
             logger.error(
+              { parentPaths, bulkWriteResult: res },
               'Migration is unable to continue',
               'Migration is unable to continue',
-              'parentPaths:',
-              parentPaths,
-              'bulkWriteResult:',
-              res,
             );
             );
             socket?.emit(SocketEventName.PMEnded, { isSucceeded: false });
             socket?.emit(SocketEventName.PMEnded, { isSucceeded: false });
           }
           }

+ 7 - 8
apps/app/src/server/service/s2s-messaging/nchan.ts

@@ -67,7 +67,7 @@ class NchanDelegator extends AbstractS2sMessagingService {
 
 
     const url = this.constructUrl(this.publishPath).toString();
     const url = this.constructUrl(this.publishPath).toString();
 
 
-    logger.debug('Publish message', s2sMessage, `to ${url}`);
+    logger.debug({ s2sMessage, url }, 'Publish message');
 
 
     return axios.post(url, s2sMessage);
     return axios.post(url, s2sMessage);
   }
   }
@@ -134,7 +134,7 @@ class NchanDelegator extends AbstractS2sMessagingService {
       logger.info('WebSocket client disconnected');
       logger.info('WebSocket client disconnected');
     });
     });
     socket.addEventListener('error', (error) => {
     socket.addEventListener('error', (error) => {
-      logger.error('WebSocket error occured:', error.message);
+      logger.error({ err: error }, 'WebSocket error occured');
     });
     });
 
 
     socket.addEventListener('open', () => {
     socket.addEventListener('open', () => {
@@ -163,8 +163,8 @@ class NchanDelegator extends AbstractS2sMessagingService {
       // check uid
       // check uid
       if (s2sMessage.publisherUid === this.uid) {
       if (s2sMessage.publisherUid === this.uid) {
         logger.debug(
         logger.debug(
-          `Skip processing by ${handlable.constructor.name} because this message is sent by the publisher itself:`,
-          `from ${this.uid}`,
+          { publisherUid: this.uid },
+          `Skip processing by ${handlable.constructor.name} because this message is sent by the publisher itself`,
         );
         );
         return;
         return;
       }
       }
@@ -172,16 +172,15 @@ class NchanDelegator extends AbstractS2sMessagingService {
       // check shouldHandleS2sMessage
       // check shouldHandleS2sMessage
       const shouldHandle = handlable.shouldHandleS2sMessage(s2sMessage);
       const shouldHandle = handlable.shouldHandleS2sMessage(s2sMessage);
       logger.debug(
       logger.debug(
-        `${handlable.constructor.name}.shouldHandleS2sMessage(`,
-        s2sMessage,
-        `) => ${shouldHandle}`,
+        { s2sMessage, shouldHandle },
+        `${handlable.constructor.name}.shouldHandleS2sMessage`,
       );
       );
 
 
       if (shouldHandle) {
       if (shouldHandle) {
         handlable.handleS2sMessage(s2sMessage);
         handlable.handleS2sMessage(s2sMessage);
       }
       }
     } catch (err) {
     } catch (err) {
-      logger.warn('Could not handle a message: ', err.message);
+      logger.warn({ err }, 'Could not handle a message');
     }
     }
   }
   }
 }
 }

+ 4 - 4
apps/app/src/server/service/search-delegator/elasticsearch.ts

@@ -646,7 +646,7 @@ class ElasticsearchDelegator
       this.prepareBodyForDelete(body, page);
       this.prepareBodyForDelete(body, page);
     });
     });
 
 
-    logger.debug('deletePages(): Sending Request to ES', body);
+    logger.debug({ body }, 'deletePages(): Sending Request to ES');
     return this.client.bulk({
     return this.client.bulk({
       body,
       body,
     });
     });
@@ -664,7 +664,7 @@ class ElasticsearchDelegator
   ): Promise<ISearchResult<ISearchResultData>> {
   ): Promise<ISearchResult<ISearchResultData>> {
     // for debug
     // for debug
     if (process.env.NODE_ENV === 'development') {
     if (process.env.NODE_ENV === 'development') {
-      logger.debug('query: ', JSON.stringify(query, null, 2));
+      logger.debug({ query }, 'query');
 
 
       const validateQueryResponse = await (async () => {
       const validateQueryResponse = await (async () => {
         if (isES7ClientDelegator(this.client)) {
         if (isES7ClientDelegator(this.client)) {
@@ -700,7 +700,7 @@ class ElasticsearchDelegator
       })();
       })();
 
 
       // for debug
       // for debug
-      logger.debug('ES result: ', validateQueryResponse);
+      logger.debug({ validateQueryResponse }, 'ES result');
     }
     }
 
 
     const searchResponse = await (async () => {
     const searchResponse = await (async () => {
@@ -1034,7 +1034,7 @@ class ElasticsearchDelegator
     const count = (await User.count({})) || 1;
     const count = (await User.count({})) || 1;
 
 
     const minScore = queryString.length * 0.1 - 1; // increase with length
     const minScore = queryString.length * 0.1 - 1; // increase with length
-    logger.debug('min_score: ', minScore);
+    logger.debug({ minScore }, 'min_score');
 
 
     query.body.query = {
     query.body.query = {
       function_score: {
       function_score: {

+ 4 - 4
apps/app/src/server/service/slack-integration.ts

@@ -250,8 +250,8 @@ export class SlackIntegrationService implements S2sMessageHandlable {
     try {
     try {
       await client.chat.postMessage(messageArgs);
       await client.chat.postMessage(messageArgs);
     } catch (error) {
     } catch (error) {
-      logger.debug('Post error', error);
-      logger.debug('Sent data to slack is:', messageArgs);
+      logger.debug({ err: error }, 'Post error');
+      logger.debug({ messageArgs }, 'Sent data to slack');
       throw error;
       throw error;
     }
     }
   }
   }
@@ -264,8 +264,8 @@ export class SlackIntegrationService implements S2sMessageHandlable {
     try {
     try {
       await slackLegacyUtil.postMessage(messageArgs);
       await slackLegacyUtil.postMessage(messageArgs);
     } catch (error) {
     } catch (error) {
-      logger.debug('Post error', error);
-      logger.debug('Sent data to slack is:', messageArgs);
+      logger.debug({ err: error }, 'Post error');
+      logger.debug({ messageArgs }, 'Sent data to slack');
       throw error;
       throw error;
     }
     }
   }
   }

+ 3 - 3
apps/app/src/server/service/socket-io/socket-io.ts

@@ -178,7 +178,7 @@ export class SocketIoService {
       const clients = await this.getAdminSocket().fetchSockets();
       const clients = await this.getAdminSocket().fetchSockets();
       const clientsCount = clients.length;
       const clientsCount = clients.length;
 
 
-      logger.debug("Current count of clients for '/admin':", clientsCount);
+      logger.debug({ clientsCount }, "Current count of clients for '/admin'");
 
 
       const limit = configManager.getConfig(
       const limit = configManager.getConfig(
         's2cMessagingPubsub:connectionsLimitForAdmin',
         's2cMessagingPubsub:connectionsLimitForAdmin',
@@ -198,7 +198,7 @@ export class SocketIoService {
     if (socket.request.user == null) {
     if (socket.request.user == null) {
       const clientsCount = this.guestClients.size;
       const clientsCount = this.guestClients.size;
 
 
-      logger.debug('Current count of clients for guests:', clientsCount);
+      logger.debug({ clientsCount }, 'Current count of clients for guests');
 
 
       const limit = configManager.getConfig(
       const limit = configManager.getConfig(
         's2cMessagingPubsub:connectionsLimitForGuest',
         's2cMessagingPubsub:connectionsLimitForGuest',
@@ -227,7 +227,7 @@ export class SocketIoService {
     const clients = await this.getDefaultSocket().fetchSockets();
     const clients = await this.getDefaultSocket().fetchSockets();
     const clientsCount = clients.length;
     const clientsCount = clients.length;
 
 
-    logger.debug("Current count of clients for '/':", clientsCount);
+    logger.debug({ clientsCount }, "Current count of clients for '/'");
 
 
     const limit = configManager.getConfig(
     const limit = configManager.getConfig(
       's2cMessagingPubsub:connectionsLimit',
       's2cMessagingPubsub:connectionsLimit',

+ 2 - 2
apps/app/src/server/service/yjs/create-mongodb-persistence.ts

@@ -33,7 +33,7 @@ export const createMongoDBPersistence = (
   const persistence: YWebsocketPersistence = {
   const persistence: YWebsocketPersistence = {
     provider: mdb,
     provider: mdb,
     bindState: async (docName: string, ydoc: WSSharedDoc) => {
     bindState: async (docName: string, ydoc: WSSharedDoc) => {
-      logger.debug('bindState', { docName });
+      logger.debug({ docName }, 'bindState');
 
 
       const persistedYdoc = await mdb.getYDoc(docName);
       const persistedYdoc = await mdb.getYDoc(docName);
 
 
@@ -93,7 +93,7 @@ export const createMongoDBPersistence = (
       });
       });
     },
     },
     writeState: async (docName: string) => {
     writeState: async (docName: string) => {
-      logger.debug('writeState', { docName });
+      logger.debug({ docName }, 'writeState');
       // flush document on close to have the smallest possible database
       // flush document on close to have the smallest possible database
       await mdb.flushDocument(docName);
       await mdb.flushDocument(docName);
     },
     },

+ 9 - 6
apps/app/src/server/service/yjs/upgrade-handler.ts

@@ -89,7 +89,7 @@ export const createUpgradeHandler = (sessionConfig: SessionConfig) => {
   ): Promise<UpgradeResult> => {
   ): Promise<UpgradeResult> => {
     const pageId = extractPageId(request.url);
     const pageId = extractPageId(request.url);
     if (pageId == null) {
     if (pageId == null) {
-      logger.warn('Invalid URL path for Yjs upgrade', { url: request.url });
+      logger.warn({ url: request.url }, 'Invalid URL path for Yjs upgrade');
       writeErrorResponse(socket, 400, 'Bad Request');
       writeErrorResponse(socket, 400, 'Bad Request');
       return { authorized: false, statusCode: 400 };
       return { authorized: false, statusCode: 400 };
     }
     }
@@ -100,7 +100,7 @@ export const createUpgradeHandler = (sessionConfig: SessionConfig) => {
       await runMiddleware(passportInit as ConnectMiddleware, request);
       await runMiddleware(passportInit as ConnectMiddleware, request);
       await runMiddleware(passportSession as ConnectMiddleware, request);
       await runMiddleware(passportSession as ConnectMiddleware, request);
     } catch (err) {
     } catch (err) {
-      logger.warn('Session/passport middleware failed on upgrade', { err });
+      logger.warn({ err }, 'Session/passport middleware failed on upgrade');
       writeErrorResponse(socket, 401, 'Unauthorized');
       writeErrorResponse(socket, 401, 'Unauthorized');
       return { authorized: false, statusCode: 401 };
       return { authorized: false, statusCode: 401 };
     }
     }
@@ -114,10 +114,13 @@ export const createUpgradeHandler = (sessionConfig: SessionConfig) => {
     if (!isAccessible) {
     if (!isAccessible) {
       const statusCode = user == null ? 401 : 403;
       const statusCode = user == null ? 401 : 403;
       const message = user == null ? 'Unauthorized' : 'Forbidden';
       const message = user == null ? 'Unauthorized' : 'Forbidden';
-      logger.warn(`Yjs upgrade rejected: ${message}`, {
-        pageId,
-        userId: user?._id,
-      });
+      logger.warn(
+        {
+          pageId,
+          userId: user?._id,
+        },
+        `Yjs upgrade rejected: ${message}`,
+      );
       writeErrorResponse(socket, statusCode, message);
       writeErrorResponse(socket, statusCode, message);
       return { authorized: false, statusCode };
       return { authorized: false, statusCode };
     }
     }

+ 2 - 2
apps/app/src/server/service/yjs/yjs.ts

@@ -102,7 +102,7 @@ class YjsService implements IYjsService {
       } catch (err) {
       } catch (err) {
         guard.restore();
         guard.restore();
 
 
-        logger.error('Yjs upgrade handler failed unexpectedly', { url, err });
+        logger.error({ url, err }, 'Yjs upgrade handler failed unexpectedly');
         if (socket.writable) {
         if (socket.writable) {
           socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n');
           socket.write('HTTP/1.1 500 Internal Server Error\r\n\r\n');
         }
         }
@@ -116,8 +116,8 @@ class YjsService implements IYjsService {
   public async getYDocStatus(pageId: string): Promise<YDocStatus> {
   public async getYDocStatus(pageId: string): Promise<YDocStatus> {
     const dumpLog = (status: YDocStatus, args?: { [key: string]: unknown }) => {
     const dumpLog = (status: YDocStatus, args?: { [key: string]: unknown }) => {
       logger.debug(
       logger.debug(
-        `getYDocStatus('${pageId}') detected '${status}'`,
         args ?? {},
         args ?? {},
+        `getYDocStatus('${pageId}') detected '${status}'`,
       );
       );
     };
     };
 
 

+ 4 - 4
apps/app/src/server/util/slack-legacy.ts

@@ -27,8 +27,8 @@ export const slackLegacyUtilFactory = (
     try {
     try {
       await webhook.send(messageObj);
       await webhook.send(messageObj);
     } catch (error) {
     } catch (error) {
-      logger.debug('Post error', error);
-      logger.debug('Sent data to slack is:', messageObj);
+      logger.debug({ err: error }, 'Post error');
+      logger.debug({ messageObj }, 'Sent data to slack');
       throw error;
       throw error;
     }
     }
   };
   };
@@ -38,8 +38,8 @@ export const slackLegacyUtilFactory = (
     try {
     try {
       await client.chat.postMessage(messageObj);
       await client.chat.postMessage(messageObj);
     } catch (error) {
     } catch (error) {
-      logger.debug('Post error', error);
-      logger.debug('Sent data to slack is:', messageObj);
+      logger.debug({ err: error }, 'Post error');
+      logger.debug({ messageObj }, 'Sent data to slack');
       throw error;
       throw error;
     }
     }
   };
   };

+ 3 - 3
apps/app/src/states/socket-io/global-socket.ts

@@ -42,16 +42,16 @@ export const useSetupGlobalSocket = (): void => {
 
 
       // Error handling
       // Error handling
       newSocket.on('error', (err) => {
       newSocket.on('error', (err) => {
-        logger.error(err);
+        logger.error({ err }, 'Socket error');
       });
       });
       newSocket.on('connect_error', (err) => {
       newSocket.on('connect_error', (err) => {
-        logger.error('Failed to connect with websocket.', err);
+        logger.error({ err }, 'Failed to connect with websocket.');
       });
       });
 
 
       // Store connection in atom
       // Store connection in atom
       setSocket(newSocket);
       setSocket(newSocket);
     } catch (error) {
     } catch (error) {
-      logger.error('Failed to initialize WebSocket:', error);
+      logger.error({ err: error }, 'Failed to initialize WebSocket');
     }
     }
   }, [setSocket]);
   }, [setSocket]);