diff --git a/src/features/auth/lib/Auth.service.ts b/src/features/auth/lib/Auth.service.ts index c9c5239..6d72b91 100644 --- a/src/features/auth/lib/Auth.service.ts +++ b/src/features/auth/lib/Auth.service.ts @@ -18,10 +18,10 @@ class AuthService extends BaseService { tokenSet = await dbx.handleDropboxCallback(urlParams) // Need to get the user's account info to get the Team Root Namespace ID. This step makes sure we are accessing the root folder that includes both personal and team folder - console.info('AuthService#handleDropboxCallback :: Getting account info') + logger.log('AuthService#handleDropboxCallback :: Getting account info') const dbxClient = new DropboxClient(tokenSet.refreshToken).getDropboxClient() const accountInfo = await dbxClient.usersGetCurrentAccount() - console.info('AuthService#handleDropboxCallback :: Account info', accountInfo) + logger.log('AuthService#handleDropboxCallback :: Account info', accountInfo) // The root_namespace_id is strictly available on root_info rootNamespaceId = accountInfo.result.root_info?.root_namespace_id diff --git a/src/features/dropbox/lib/Dropbox.service.ts b/src/features/dropbox/lib/Dropbox.service.ts index 5f9802b..0abfd71 100644 --- a/src/features/dropbox/lib/Dropbox.service.ts +++ b/src/features/dropbox/lib/Dropbox.service.ts @@ -54,7 +54,7 @@ export class DropboxService extends AuthenticatedDropboxService { path: string, dbxClient: Dropbox, ) { - console.info('DropboxService#searchChildrenFolders :: Searching children folders') + logger.log('DropboxService#searchChildrenFolders :: Searching children folders') const isSharedFolder = !!folderResult.sharing_info?.shared_folder_id const prefixPath = isSharedFolder ? path : undefined @@ -81,12 +81,12 @@ export class DropboxService extends AuthenticatedDropboxService { * To get the subfolder of the result folder, we use filesListFolder with the namespace_id of the result folder. */ async _searchForFolder({ dbxClient, search }: { dbxClient: Dropbox; search: string }) { - logger.info('DropboxService#getFolderTree :: Searching folder in Dropbox... Query: ', search) + logger.log('DropboxService#getFolderTree :: Searching folder in Dropbox... Query: ', search) const sanitizedSearch = replaceSpecialCharactersWithSpace(search) if (!sanitizedSearch) return [] const { path, folder: query } = splitPathAndFolder(sanitizedSearch) - console.info({ path, query }) + logger.log({ path, query }) let tempPath = path let folderResult: files.FolderMetadataReference | undefined @@ -94,7 +94,7 @@ export class DropboxService extends AuthenticatedDropboxService { const formattedFolders: Partial[] = [] if (query !== '') { - console.info('DropboxService#searchForFolder :: Query is available. Searching for folder') + logger.log('DropboxService#searchForFolder :: Query is available. Searching for folder') const searchResponse = await dbxClient.filesSearchV2({ query, options: { @@ -117,7 +117,7 @@ export class DropboxService extends AuthenticatedDropboxService { const metadata = matchMetadata.metadata folderResult = metadata['.tag'] === ObjectType.FOLDER ? metadata : undefined } else { - console.info('DropboxService#searchForFolder :: Query is empty. Getting folder metadata') + logger.log('DropboxService#searchForFolder :: Query is empty. Getting folder metadata') const metaDataResp = await dbxClient.filesGetMetadata({ path: tempPath }) if (metaDataResp.status !== httpStatus.OK) { diff --git a/src/features/sync/lib/MapFiles.service.ts b/src/features/sync/lib/MapFiles.service.ts index 62ccadb..5f2ba6f 100644 --- a/src/features/sync/lib/MapFiles.service.ts +++ b/src/features/sync/lib/MapFiles.service.ts @@ -52,7 +52,7 @@ type MarkUpdatedPayload = Omit< export class MapFilesService extends AuthenticatedDropboxService { async getSingleFileMap(where: WhereClause): Promise { - logger.info('MapFilesService#getSingleFileMap :: Getting single file map where', where.getSQL()) + logger.info('MapFilesService#getSingleFileMap :: Getting single file map') const results = await db.query.fileFolderSync.findFirst({ where, @@ -62,7 +62,7 @@ export class MapFilesService extends AuthenticatedDropboxService { } async getAllFileMaps(where: WhereClause): Promise { - logger.info('MapFilesService#getAllFileMaps :: Getting all file maps where', where.getSQL()) + logger.info('MapFilesService#getAllFileMaps :: Getting all file maps') const results = await db.query.fileFolderSync.findMany({ where: (fileFolderSync, { eq }) => @@ -178,7 +178,7 @@ export class MapFilesService extends AuthenticatedDropboxService { payload: FileSyncUpdatePayload, condition: WhereClause, ): Promise { - logger.info('MapFilesService#updateFileMap :: Updating file map', payload, condition.getSQL()) + logger.info('MapFilesService#updateFileMap :: Updating file map', payload) const [connection] = await db .update(fileFolderSync) @@ -528,7 +528,7 @@ export class MapFilesService extends AuthenticatedDropboxService { } async getAllChannelMaps(where?: WhereClause): Promise { - logger.info('MapFilesService#getAllChannelMaps :: Getting all channel maps', where?.getSQL()) + logger.info('MapFilesService#getAllChannelMaps :: Getting all channel maps') const results = await db.query.channelSync.findMany({ where: (channelSync, { eq }) => @@ -684,7 +684,7 @@ export class MapFilesService extends AuthenticatedDropboxService { // Bulk soft-delete stale channel maps if (staleChannelMapIds.length > 0) { - console.info('Soft delete channel maps and make them inactive: ', staleChannelMapIds) + logger.info('Soft delete channel maps and make them inactive: ', staleChannelMapIds) await this.deleteChannelMapsByIds(staleChannelMapIds) } @@ -719,23 +719,23 @@ export class MapFilesService extends AuthenticatedDropboxService { if (fileChannel.membershipType === FileChannelMembership.COMPANY) { if (!fileChannel.companyId) { - console.error('Company id not found') + logger.error('Company id not found') return null } const company = companyMap.get(fileChannel.companyId) if (!company) { - console.error('Company not found in batch response', fileChannel.companyId) + logger.error('Company not found in batch response', fileChannel.companyId) return null } fileChannelValue = [{ id: company.id, companyId: company.id, object: 'company' as const }] } else { if (!fileChannel.clientId) { - console.error('Client id not found') + logger.error('Client id not found') return null } const client = clientMap.get(fileChannel.clientId) if (!client) { - console.error('Client not found in batch response', fileChannel.clientId) + logger.error('Client not found in batch response', fileChannel.clientId) return null } fileChannelValue = [ diff --git a/src/features/sync/lib/Sync.service.ts b/src/features/sync/lib/Sync.service.ts index 7599f43..34242dd 100644 --- a/src/features/sync/lib/Sync.service.ts +++ b/src/features/sync/lib/Sync.service.ts @@ -515,7 +515,7 @@ export class SyncService extends AuthenticatedDropboxService { error.status === 400 && error.body.message === 'Folder already exists' ) { - console.info({ message: error.body.message, path: itemPath }) + logger.info({ message: error.body.message, path: itemPath }) // Row exists (concurrent winner) → just stamp dbxFileId. Otherwise recover the // folder's id + path from Assembly so children resolve under it, not a duplicate. const existing = await this.mapFilesService.getDbxMappedFileFromPath( @@ -543,7 +543,7 @@ export class SyncService extends AuthenticatedDropboxService { } return } - console.error( + logger.error( `SyncService#createFolderInAssembly. Upload failed. Channel ID: ${assemblyChannelId}. Path: ${itemPath}`, ) throw error @@ -773,7 +773,7 @@ export class SyncService extends AuthenticatedDropboxService { logger.info('SyncService#uploadFileInAssembly :: File uploaded to Assembly', dbxPath) if (fileUploadResp.status !== httpStatus.OK) { - console.error({ error: await fileUploadResp.json() }) + logger.error({ error: await fileUploadResp.json() }) throw new Error('SyncService#uploadFileInAssemnly. Failed to upload file to assembly') } } @@ -795,11 +795,7 @@ export class SyncService extends AuthenticatedDropboxService { eq(fileFolderSync.itemPath, basePath), ) as WhereClause try { - logger.info( - 'SyncService#handleFolderCreatedCase :: Updating dbxFileId', - entryId, - fileMapCondition.getSQL(), - ) + logger.info('SyncService#handleFolderCreatedCase :: Updating dbxFileId', entryId) } catch (e) { logger.info(e) } @@ -901,7 +897,7 @@ export class SyncService extends AuthenticatedDropboxService { fileType: ObjectTypeValue, file: CopilotFileRetrieve, ): Promise<{ dbxFileId: string; contentHash?: string } | undefined> { - console.info(`SyncService#createAndUploadFileInDropbox. Channel ID: ${file.channelId}`) + logger.log(`SyncService#createAndUploadFileInDropbox. Channel ID: ${file.channelId}`) const dbxClient = this.dbxClient.getDropboxClient() const dbxFilePath = `${dbxRootPath}/${file.path}` @@ -931,7 +927,7 @@ export class SyncService extends AuthenticatedDropboxService { error.status === 409 && dbxError?.error?.path?.['.tag'] === 'not_found' if (!isNotFound) { - console.error(`SyncService#createAndUploadFileInDropbox. Channel ID: ${file.channelId}`) + logger.error(`SyncService#createAndUploadFileInDropbox. Channel ID: ${file.channelId}`) throw error } } @@ -960,7 +956,7 @@ export class SyncService extends AuthenticatedDropboxService { } if (existing) { - console.info( + logger.info( `SyncService#createAndUploadFileInDropbox. File exists but didn't received required file tag. Type: ${existing['.tag']}. Channel ID: ${file.channelId}`, ) return @@ -979,7 +975,7 @@ export class SyncService extends AuthenticatedDropboxService { logger.info('SyncService#createAndUploadFileInDropbox :: File created', dbxFilePath) return await this.uploadFileInDropbox(file, dbxFilePath) } - console.info( + logger.info( `SyncService#createAndUploadFileInDropbox. File type out of bound. Type: ${fileType}. Channel ID: ${file.channelId}`, ) } @@ -1003,7 +999,7 @@ export class SyncService extends AuthenticatedDropboxService { contentHash: dbxResponse.contentHash, } } - console.error( + logger.error( `SyncService#uploadFileInDropbox. Assembly file with Id: ${file.id} has no download url. Channel ID: ${file.channelId}`, ) throw new Error('File not found') diff --git a/src/features/webhook/assembly/api/webhook.controller.ts b/src/features/webhook/assembly/api/webhook.controller.ts index 9bbb322..72bac2d 100644 --- a/src/features/webhook/assembly/api/webhook.controller.ts +++ b/src/features/webhook/assembly/api/webhook.controller.ts @@ -24,7 +24,7 @@ export const handleWebhookEvent = async (req: NextRequest) => { const connection = await dropboxConnectionService.getConnectionForWorkspace() if (!connection.status) { - console.info(`Sync is not enabled for this workspace. Skipping webhook event`) + logger.info(`Sync is not enabled for this workspace. Skipping webhook event`) return NextResponse.json({}) } @@ -37,7 +37,7 @@ export const handleWebhookEvent = async (req: NextRequest) => { rootNamespaceId: connection.rootNamespaceId, }) const webhookEvent = assemblyWebhookService.parseWebhook(rawBody) - logger.info(`Event triggered. ${JSON.stringify(webhookEvent)}`) + logger.info('Event triggered.', webhookEvent) const eventType = assemblyWebhookService.validateHandleableEvent(webhookEvent) if (!eventType) { diff --git a/src/features/webhook/dropbox/api/webhook.controller.ts b/src/features/webhook/dropbox/api/webhook.controller.ts index 320905d..475638b 100644 --- a/src/features/webhook/dropbox/api/webhook.controller.ts +++ b/src/features/webhook/dropbox/api/webhook.controller.ts @@ -3,6 +3,7 @@ import status from 'http-status' import { type NextRequest, NextResponse } from 'next/server' import env from '@/config/server.env' import { DropboxWebhook } from '@/features/webhook/dropbox/lib/webhook.service' +import logger from '@/lib/logger' import { sleep } from '@/utils/sleep' export const handleWebhookUrlVerification = (req: NextRequest) => { @@ -16,7 +17,7 @@ export const handleWebhookUrlVerification = (req: NextRequest) => { }, }) } catch (error: unknown) { - console.error('Webhook verification error:', error) + logger.error('Webhook verification error:', error) return NextResponse.json( { error: 'Something went wrong' }, { status: status.INTERNAL_SERVER_ERROR }, diff --git a/src/features/webhook/dropbox/lib/webhook.service.ts b/src/features/webhook/dropbox/lib/webhook.service.ts index ae5dea1..29dff13 100644 --- a/src/features/webhook/dropbox/lib/webhook.service.ts +++ b/src/features/webhook/dropbox/lib/webhook.service.ts @@ -34,7 +34,7 @@ export class DropboxWebhook { // Skip if already pending — cron will handle it if (connection.pendingWebhook) { - console.info(`Webhook skipped for account ${account}, already has pending webhook`) + logger.info(`Webhook skipped for account ${account}, already has pending webhook`) continue } @@ -49,7 +49,7 @@ export class DropboxWebhook { .update(dropboxConnections) .set({ pendingWebhook: true }) .where(eq(dropboxConnections.id, connection.id)) - console.info(`Webhook debounced for account ${account}, marked as pending`) + logger.info(`Webhook debounced for account ${account}, marked as pending`) } else { await processDropboxChanges.trigger(account, { concurrencyKey: account }) } @@ -60,7 +60,7 @@ export class DropboxWebhook { const connection = await this.getActiveConnection(accountId) if (!connection || !connection.refreshToken) { - console.error( + logger.error( `DropboxWebhook#fetchDropboxChanges :: Connection is not valid for Dropbox accountId: ${accountId}`, ) return @@ -127,7 +127,7 @@ export class DropboxWebhook { dbxClient: Dropbox, ): Promise { try { - console.info( + logger.info( `WebhookService#handleDbxRootPathMove. Root path: ${channel.dbxRootPath}. Assembly file channel ID: ${channel.assemblyChannelId} Checking if the root path exists...`, ) const response = await this.getDropboxFileMetadata(channel.dbxRootPath, dbxClient) @@ -139,7 +139,7 @@ export class DropboxWebhook { error, ) if (error instanceof DropboxResponseError && error.status === 409) { - console.info( + logger.info( 'WebhookService#handleDbxRootPathMove :: Root path not found', channel.dbxRootPath, ) @@ -197,9 +197,7 @@ export class DropboxWebhook { user: User, connectionToken: DropboxConnectionTokens, ) { - console.info( - `webhookService#processChannelChanges. ChannelId: ${channel.id} ${JSON.stringify(channel)}`, - ) + logger.log(`WebhookService#processChannelChanges. ChannelId: ${channel.id}`) const { id: channelSyncId, dbxRootPath, assemblyChannelId, dbxCursor } = channel let hasMore = true let currentCursor = dbxCursor ?? '' diff --git a/src/features/webhook/dropbox/utils/getDropboxChanges.ts b/src/features/webhook/dropbox/utils/getDropboxChanges.ts index a0a8417..a8f8f17 100644 --- a/src/features/webhook/dropbox/utils/getDropboxChanges.ts +++ b/src/features/webhook/dropbox/utils/getDropboxChanges.ts @@ -1,6 +1,7 @@ import type { Dropbox } from 'dropbox' import type { MapFilesService } from '@/features/sync/lib/MapFiles.service' import { DropboxFileListFolderResultEntriesSchema } from '@/features/sync/types' +import logger from '@/lib/logger' export async function getDropboxChanges( cursor: string, @@ -34,8 +35,7 @@ export async function getDropboxChanges( const parsed = DropboxFileListFolderResultEntriesSchema.safeParse(entriesWithId) if (!parsed.success) { - console.info(`Entries payload: ${JSON.stringify(entriesWithId)}`) - console.error('Invalid Dropbox response entries:', parsed.error) + logger.error('Invalid Dropbox response entries:', parsed.error) // return throw new Error('Invalid Dropbox entries format') } @@ -50,7 +50,7 @@ export async function getDropboxChanges( hasMore: response.result.has_more, } } catch (error) { - console.error('Error fetching Dropbox changes:', error) + logger.error('Error fetching Dropbox changes:', error) throw error } } diff --git a/src/lib/__tests__/logger.test.ts b/src/lib/__tests__/logger.test.ts new file mode 100644 index 0000000..17b851b --- /dev/null +++ b/src/lib/__tests__/logger.test.ts @@ -0,0 +1,64 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { logger } from '@/lib/logger' + +afterEach(() => { + vi.restoreAllMocks() + delete process.env.LOG_LEVEL +}) + +describe('logger', () => { + it('caps object depth so nested payloads are not fully dumped', () => { + const spy = vi.spyOn(console, 'info').mockImplementation(() => undefined) + logger.info({ a: { b: { c: { secret: 'deep-value' } } } }) + const out = spy.mock.calls[0][0] as string + expect(out).not.toContain('deep-value') + expect(out).toContain('[Object]') + }) + + it('truncates long arrays instead of dumping every element', () => { + const spy = vi.spyOn(console, 'info').mockImplementation(() => undefined) + logger.info(Array.from({ length: 100 }, (_, i) => i)) + expect(spy.mock.calls[0][0] as string).toContain('more items') + }) + + it('passes string args through unchanged', () => { + const spy = vi.spyOn(console, 'info').mockImplementation(() => undefined) + logger.info('hello', 'world') + expect(spy).toHaveBeenCalledWith('hello world') + }) + + it('suppresses levels below LOG_LEVEL', () => { + process.env.LOG_LEVEL = 'warn' + const info = vi.spyOn(console, 'info').mockImplementation(() => undefined) + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + logger.info('quiet') + logger.warn('loud') + expect(info).not.toHaveBeenCalled() + expect(warn).toHaveBeenCalled() + }) + + it('defaults to info, suppressing the verbose log level', () => { + const log = vi.spyOn(console, 'log').mockImplementation(() => undefined) + const info = vi.spyOn(console, 'info').mockImplementation(() => undefined) + logger.log('debug detail') + logger.info('operational') + expect(log).not.toHaveBeenCalled() + expect(info).toHaveBeenCalled() + }) + + it('truncates long strings nested in an object', () => { + const spy = vi.spyOn(console, 'info').mockImplementation(() => undefined) + logger.info({ message: 'x'.repeat(1000) }) + expect(spy.mock.calls[0][0] as string).toContain('more characters') + }) + + it('always prints error, and treats an invalid LOG_LEVEL as the default', () => { + process.env.LOG_LEVEL = 'BOGUS' // unrecognized → falls back to info + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined) + const error = vi.spyOn(console, 'error').mockImplementation(() => undefined) + logger.warn('kept at info default') + logger.error('always') + expect(warn).toHaveBeenCalled() + expect(error).toHaveBeenCalled() + }) +}) diff --git a/src/lib/copilot/CopilotAPI.ts b/src/lib/copilot/CopilotAPI.ts index 539443a..5241a9a 100644 --- a/src/lib/copilot/CopilotAPI.ts +++ b/src/lib/copilot/CopilotAPI.ts @@ -113,7 +113,7 @@ export class CopilotAPI { fileType: ObjectTypeValue, ): Promise { // Names are validated upstream (SyncService); path passes through unchanged. - console.info(`CopilotAPI#_createFile. Path: ${path}`) + logger.log(`CopilotAPI#_createFile. Path: ${path}`) const sdk = await this.assemblySdk const createFileResponse = await sdk.createFile({ fileType, diff --git a/src/lib/copilot/utils.ts b/src/lib/copilot/utils.ts index ab5f25d..3b89ac0 100644 --- a/src/lib/copilot/utils.ts +++ b/src/lib/copilot/utils.ts @@ -1,6 +1,7 @@ import { assemblyApi } from '@assembly-js/node-sdk' import env from '@/config/server.env' import { TokenSchema } from '@/lib/copilot/types' +import logger from '@/lib/logger' export const buildClientName = (client: { givenName: string; familyName: string }) => `${client.givenName} ${client.familyName}` @@ -10,7 +11,7 @@ export async function getAssemblyTokenPayload(token: string) { const sdk = await assemblyApi({ apiKey: env.COPILOT_API_KEY, token }) if (!sdk.getTokenPayload) { // Never log the raw token — it is a credential. - console.error('getAssemblyTokenPayload | cannot decode token') + logger.error('getAssemblyTokenPayload | cannot decode token') return null } return TokenSchema.parse(await sdk.getTokenPayload()) diff --git a/src/lib/dropbox/DropboxClient.ts b/src/lib/dropbox/DropboxClient.ts index 81053c1..de90176 100644 --- a/src/lib/dropbox/DropboxClient.ts +++ b/src/lib/dropbox/DropboxClient.ts @@ -7,6 +7,7 @@ import { MAX_FETCH_DBX_RESOURCES } from '@/constants/limits' import { DropboxClientType, type DropboxClientTypeValue } from '@/db/constants' import { DropboxAuthClient } from '@/lib/dropbox/DropboxAuthClient' import { type DropboxFileMetadata, DropboxFileMetadataSchema } from '@/lib/dropbox/type' +import logger from '@/lib/logger' import { withRetry } from '@/lib/withRetry' import { dropboxArgHeader } from '@/utils/header' @@ -115,7 +116,7 @@ export class DropboxClient { fetchAll: boolean = false, limit: number = MAX_FETCH_DBX_RESOURCES, ) { - console.info( + logger.log( 'DropboxClient#getAllFilesFolders :: Fetching all files and folders. Root path: ', rootPath, ) @@ -138,7 +139,7 @@ export class DropboxClient { }) entries.push(...filesFolders.result.entries) } - console.info('DropboxClient#getAllFilesFolders :: Total entries', entries.length) + logger.info('DropboxClient#getAllFilesFolders :: Total entries', entries.length) return entries } diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 56226cd..86e679e 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -2,6 +2,8 @@ import util from 'node:util' type LogLevel = 'log' | 'info' | 'warn' | 'error' +// Only object args get the depth/length caps; string args are logged as-is. Pass the +// raw object (not JSON.stringify(obj)) if you want a large value to be bounded. export interface Logger { log: (...args: unknown[]) => void info: (...args: unknown[]) => void @@ -9,9 +11,28 @@ export interface Logger { error: (...args: unknown[]) => void } +// Lower rank = more verbose. LOG_LEVEL sets the minimum level that prints. +const LEVEL_RANK: Record = { log: 0, info: 1, warn: 2, error: 3 } + +const parseLevel = (value: string | undefined): LogLevel => { + const level = value?.toLowerCase() + return level === 'log' || level === 'info' || level === 'warn' || level === 'error' + ? level + : 'info' +} + +// Read per call so the level can be set without a rebuild (and stays testable). +const minRank = (): number => LEVEL_RANK[parseLevel(process.env.LOG_LEVEL)] + +// Bound object output so one log line can't dump a whole payload / file list. +// breakLength: Infinity keeps each log on one physical line so line-based log +// collectors don't split a wrapped object across entries. const inspectOptions: util.InspectOptions = { - depth: null, + depth: 2, colors: Boolean(process.stdout.isTTY), + maxArrayLength: 10, + maxStringLength: 512, + breakLength: Infinity, } function formatArg(arg: unknown): string { @@ -20,8 +41,9 @@ function formatArg(arg: unknown): string { function loggerFactory(level: LogLevel): (...args: unknown[]) => void { return (...args: unknown[]) => { + if (LEVEL_RANK[level] < minRank()) return const line = args.map(formatArg).join(' ') - // biome-ignore lint/suspicious/noConsole: only 'log' level will be warned + // biome-ignore lint/suspicious/noConsole: this is the single console entry point console[level](line) } } diff --git a/src/lib/withRetry.ts b/src/lib/withRetry.ts index f707cd0..318f41c 100644 --- a/src/lib/withRetry.ts +++ b/src/lib/withRetry.ts @@ -5,6 +5,7 @@ import { DropboxResponseError } from 'dropbox' import httpStatus from 'http-status' import pRetry from 'p-retry' import type { StatusableError } from '@/errors/BaseServerError' +import logger from '@/lib/logger' import { sleep } from '@/utils/sleep' const RETRYABLE_STATUS_CODES = new Set([ @@ -39,7 +40,7 @@ export const withRetry = async ( if (error.status === httpStatus.TOO_MANY_REQUESTS && retryAfter) { // If rate limit happens with retryAfter value from dropbox api. Wait const waitMs = retryAfter * 1000 - console.warn(`Rate limited. Waiting for ${retryAfter} seconds before retry...`) + logger.warn(`Rate limited. Waiting for ${retryAfter} seconds before retry...`) await sleep(waitMs) } } @@ -85,7 +86,7 @@ export const withRetry = async ( await sleep(1000) } - console.warn( + logger.warn( `CopilotAPI#withRetry | Attempt ${error.attemptNumber} failed. There are ${error.retriesLeft} retries left. Error:`, error, ) diff --git a/src/trigger/processFileSync.ts b/src/trigger/processFileSync.ts index ebec996..847e75a 100644 --- a/src/trigger/processFileSync.ts +++ b/src/trigger/processFileSync.ts @@ -72,9 +72,7 @@ export const processDropboxChanges = task({ }, run: async (accountId: string) => { const dropboxWebhook = new DropboxWebhook() - console.info( - `processFileSync#processDropboxChanges, Process start for account ID: ${accountId}`, - ) + logger.info(`processFileSync#processDropboxChanges, Process start for account ID: ${accountId}`) await dropboxWebhook.fetchDropBoxChanges(accountId) }, }) diff --git a/src/trigger/scheduledTasks.ts b/src/trigger/scheduledTasks.ts index d0531df..a1a38a1 100644 --- a/src/trigger/scheduledTasks.ts +++ b/src/trigger/scheduledTasks.ts @@ -3,13 +3,14 @@ import { and, eq } from 'drizzle-orm' import env from '@/config/server.env' import db from '@/db' import { dropboxConnections } from '@/db/schema/dropboxConnections.schema' +import logger from '@/lib/logger' import { processDropboxChanges } from '@/trigger/processFileSync' export const pendingWebhookCatchUp = schedules.task({ id: 'pending-webhook-catch-up', cron: env.WEBHOOK_CATCHUP_CRON, run: async () => { - console.info('Catch-up cron: triggering sync for all pending webhooks') + logger.info('Catch-up cron: triggering sync for all pending webhooks') const pendingConnections = await db .select({ accountId: dropboxConnections.accountId }) @@ -18,7 +19,7 @@ export const pendingWebhookCatchUp = schedules.task({ if (pendingConnections.length === 0) return - console.info( + logger.info( `Catch-up cron: triggering sync for ${pendingConnections.length} account(s) with pending webhooks`, )