From b6f1432db4a14e5b1f8e58fc10efae5b9ce0ec72 Mon Sep 17 00:00:00 2001 From: jrea Date: Tue, 8 Oct 2024 09:28:15 -0400 Subject: [PATCH] fix(server): support external logging --- .../server/src/api/routes/auth/callback.ts | 3 +- packages/server/src/api/routes/me/index.ts | 10 +--- packages/server/src/api/routes/tenants/GET.ts | 4 +- .../server/src/api/routes/tenants/POST.ts | 4 +- .../api/routes/tenants/[tenantId]/DELETE.ts | 4 +- .../src/api/routes/tenants/[tenantId]/GET.ts | 8 +-- .../src/api/routes/tenants/[tenantId]/PUT.ts | 4 +- .../routes/tenants/[tenantId]/users/GET.ts | 7 +-- .../routes/tenants/[tenantId]/users/POST.ts | 4 +- .../routes/tenants/[tenantId]/users/PUT.ts | 7 +-- .../[tenantId]/users/[userId]/DELETE.ts | 7 +-- .../routes/tenants/[tenantId]/users/index.ts | 18 ++++-- .../server/src/api/routes/tenants/index.ts | 11 ++-- packages/server/src/api/routes/users/GET.ts | 5 +- packages/server/src/api/routes/users/POST.ts | 4 +- .../src/api/routes/users/[userId]/PUT.ts | 5 +- packages/server/src/api/routes/users/index.ts | 7 +-- packages/server/src/api/utils/auth.ts | 2 +- packages/server/src/api/utils/request.ts | 12 +++- packages/server/src/auth/index.ts | 12 ++-- packages/server/src/db/DBManager.ts | 16 +++--- packages/server/src/db/NileInstance.ts | 33 ++++++----- packages/server/src/db/PoolProxy.ts | 2 +- packages/server/src/types.ts | 5 ++ packages/server/src/utils/Config/envVars.ts | 53 +++++++++--------- packages/server/src/utils/Config/index.ts | 6 +- packages/server/src/utils/Logger.ts | 55 ++++++++++++------- packages/server/src/utils/fetch.ts | 37 +++++-------- 28 files changed, 166 insertions(+), 179 deletions(-) diff --git a/packages/server/src/api/routes/auth/callback.ts b/packages/server/src/api/routes/auth/callback.ts index 62c1326f..fc81f507 100644 --- a/packages/server/src/api/routes/auth/callback.ts +++ b/packages/server/src/api/routes/auth/callback.ts @@ -10,8 +10,7 @@ const key = 'CALLBACK'; export default async function route(request: Request, config: Config) { const { error } = Logger( { ...config, debug: config.debug } as Config, - '[ROUTES]', - `[${key}]` + `[ROUTES][${key}]` ); const [provider] = new URL(request.url).pathname.split('/').reverse(); const passThroughUrl = new URL(request.url); diff --git a/packages/server/src/api/routes/me/index.ts b/packages/server/src/api/routes/me/index.ts index 2fb2ab77..60ab7fdf 100644 --- a/packages/server/src/api/routes/me/index.ts +++ b/packages/server/src/api/routes/me/index.ts @@ -1,4 +1,3 @@ -import Logger from '../../../utils/Logger'; import { Routes } from '../../types'; import { apiRoutes } from '../../utils/routes/apiRoutes'; import urlMatches from '../../utils/routes/urlMatches'; @@ -39,25 +38,18 @@ const key = 'ME'; async function GET( url: string, init: RequestInit & { request: Request }, - log: (...args: string[]) => void, config: Config ) { - log('[GET]', url); const res = await request(url, init, config); return res; } export default async function route(request: Request, config: Config) { const url = apiRoutes(config)[key]; - const { info } = Logger( - { ...config, debug: config.debug } as Config, - '[ROUTES]', - `[${key}]` - ); switch (request.method) { case 'GET': - return await GET(url, { request }, info, config); + return await GET(url, { request }, config); default: return new Response('method not allowed', { status: 405 }); diff --git a/packages/server/src/api/routes/tenants/GET.ts b/packages/server/src/api/routes/tenants/GET.ts index a8c18171..a22669e7 100644 --- a/packages/server/src/api/routes/tenants/GET.ts +++ b/packages/server/src/api/routes/tenants/GET.ts @@ -34,14 +34,12 @@ import { apiRoutes } from '../../utils/routes/apiRoutes'; export async function GET( config: Config, session: ActiveSession, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { let url = `${apiRoutes(config).USER_TENANTS(session.id)}`; if (typeof session === 'object' && 'user' in session && session.user) { url = `${apiRoutes(config).USER_TENANTS(session.user.id)}`; } - log('[GET]', url); const res = await request(url, init, config); return res; diff --git a/packages/server/src/api/routes/tenants/POST.ts b/packages/server/src/api/routes/tenants/POST.ts index 4d8024ff..b2083bbd 100644 --- a/packages/server/src/api/routes/tenants/POST.ts +++ b/packages/server/src/api/routes/tenants/POST.ts @@ -45,13 +45,11 @@ import { apiRoutes } from '../../utils/routes/apiRoutes'; */ export async function POST( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { init.body = init.request.body; init.method = 'POST'; const url = `${apiRoutes(config).TENANTS}`; - log('[POST]', url); return await request(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/DELETE.ts b/packages/server/src/api/routes/tenants/[tenantId]/DELETE.ts index 8528c8d5..f72d2bfb 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/DELETE.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/DELETE.ts @@ -34,8 +34,7 @@ import { apiRoutes } from '../../../utils/routes/apiRoutes'; */ export async function DELETE( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); const [tenantId] = yurl.pathname.split('/').reverse(); @@ -45,7 +44,6 @@ export async function DELETE( init.method = 'DELETE'; const url = `${apiRoutes(config).TENANT(tenantId)}`; - log('[DELETE]', url); return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/GET.ts b/packages/server/src/api/routes/tenants/[tenantId]/GET.ts index fd14d66e..131252d3 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/GET.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/GET.ts @@ -1,5 +1,5 @@ import { Config } from '../../../../utils/Config'; -import fetch from '../../../utils/request'; +import request from '../../../utils/request'; import { apiRoutes } from '../../../utils/routes/apiRoutes'; /** @@ -38,17 +38,17 @@ import { apiRoutes } from '../../../utils/routes/apiRoutes'; export async function GET( config: Config, init: RequestInit & { request: Request }, - log: (...args: string[]) => void + log: (message: string | unknown, meta?: Record) => void ) { const yurl = new URL(init.request.url); const [tenantId] = yurl.pathname.split('/').reverse(); if (!tenantId) { + log('[GET] No tenant id provided.'); return new Response(null, { status: 404 }); } init.method = 'GET'; const url = `${apiRoutes(config).TENANT(tenantId)}`; - log('[GET]', url); - return await fetch(url, init, config); + return await request(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/PUT.ts b/packages/server/src/api/routes/tenants/[tenantId]/PUT.ts index 3cc459bd..44a5c2ec 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/PUT.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/PUT.ts @@ -38,8 +38,7 @@ import { apiRoutes } from '../../../utils/routes/apiRoutes'; */ export async function PUT( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); const [tenantId] = yurl.pathname.split('/').reverse(); @@ -49,7 +48,6 @@ export async function PUT( init.body = init.request.body; init.method = 'PUT'; const url = `${apiRoutes(config).TENANT(tenantId)}`; - log('[PUT]', url); return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/users/GET.ts b/packages/server/src/api/routes/tenants/[tenantId]/users/GET.ts index 16e407aa..11b1e1b3 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/users/GET.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/users/GET.ts @@ -35,16 +35,11 @@ import request from '../../../../utils/request'; */ export async function GET( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); const [, tenantId] = yurl.pathname.split('/').reverse(); - if (!tenantId) { - return new Response(null, { status: 404 }); - } const url = `${apiRoutes(config).TENANT_USERS(tenantId)}`; - log('[GET]', '[TENANT_USERS]', url); return await request(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/users/POST.ts b/packages/server/src/api/routes/tenants/[tenantId]/users/POST.ts index 4179fe97..5c31818d 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/users/POST.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/users/POST.ts @@ -46,8 +46,7 @@ import { Config } from '../../../../../utils/Config'; export async function POST( config: Config, session: ActiveSession, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); const [, tenantId] = yurl.pathname.split('/').reverse(); @@ -56,7 +55,6 @@ export async function POST( init.body = JSON.stringify({ email: session.email }); init.method = 'PUT'; const url = apiRoutes(config).TENANT_USERS(tenantId); - log('[PUT]', url); return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/users/PUT.ts b/packages/server/src/api/routes/tenants/[tenantId]/users/PUT.ts index 77ccc695..2e7cd0e0 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/users/PUT.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/users/PUT.ts @@ -29,18 +29,13 @@ import { Config } from '../../../../../utils/Config'; export async function PUT( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); const [, tenantId] = yurl.pathname.split('/').reverse(); - if (!tenantId) { - return new Response(null, { status: 404 }); - } init.method = 'PUT'; const url = `${apiRoutes(config).TENANT_USERS(tenantId)}`; - log('[PUT]', url); return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/users/[userId]/DELETE.ts b/packages/server/src/api/routes/tenants/[tenantId]/users/[userId]/DELETE.ts index fd550061..1854471f 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/users/[userId]/DELETE.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/users/[userId]/DELETE.ts @@ -31,20 +31,15 @@ import { Config } from '../../../../../../utils/Config'; export async function DELETE( config: Config, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { const yurl = new URL(init.request.url); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [userId, _, tenantId] = yurl.pathname.split('/').reverse(); - if (!tenantId) { - return new Response(null, { status: 404 }); - } init.method = 'DELETE'; init.body = JSON.stringify({ email: userId }); const url = `${apiRoutes(config).TENANT_USER(tenantId, userId)}`; - log('[DELETE]', url); return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/tenants/[tenantId]/users/index.ts b/packages/server/src/api/routes/tenants/[tenantId]/users/index.ts index fd14383a..9226f12a 100644 --- a/packages/server/src/api/routes/tenants/[tenantId]/users/index.ts +++ b/packages/server/src/api/routes/tenants/[tenantId]/users/index.ts @@ -14,8 +14,7 @@ const key = 'TENANT_USERS'; export default async function route(request: Request, config: Config) { const { info } = Logger( { ...config, debug: config.debug } as Config, - '[ROUTES]', - `[${key}]` + `[ROUTES][${key}]` ); const session = await auth(request, config); @@ -23,16 +22,23 @@ export default async function route(request: Request, config: Config) { info('401'); return new Response(null, { status: 401 }); } + const yurl = new URL(request.url); + const [, tenantId] = yurl.pathname.split('/').reverse(); + + if (!tenantId) { + info('No tenant id found in path'); + return new Response(null, { status: 404 }); + } switch (request.method) { case 'GET': - return await GET(config, { request }, info); + return await GET(config, { request }); case 'POST': - return await POST(config, session, { request }, info); + return await POST(config, session, { request }); case 'PUT': - return await PUT(config, { request }, info); + return await PUT(config, { request }); case 'DELETE': - return await DELETE(config, { request }, info); + return await DELETE(config, { request }); default: return new Response('method not allowed', { status: 405 }); diff --git a/packages/server/src/api/routes/tenants/index.ts b/packages/server/src/api/routes/tenants/index.ts index a7f909f7..cc1b143c 100644 --- a/packages/server/src/api/routes/tenants/index.ts +++ b/packages/server/src/api/routes/tenants/index.ts @@ -26,8 +26,7 @@ const key = 'TENANTS'; export default async function route(request: Request, config: Config) { const { info } = Logger( { ...config, debug: config.debug } as Config, - '[ROUTES]', - `[${key}]` + `[ROUTES][${key}]` ); const session = await auth(request, config); @@ -42,13 +41,13 @@ export default async function route(request: Request, config: Config) { if (isUUID(possibleTenantId)) { return await TENANT_GET(config, { request }, info); } - return await GET(config, session, { request }, info); + return await GET(config, session, { request }); case 'POST': - return await POST(config, { request }, info); + return await POST(config, { request }); case 'DELETE': - return await DELETE(config, { request }, info); + return await DELETE(config, { request }); case 'PUT': - return await PUT(config, { request }, info); + return await PUT(config, { request }); default: return new Response('method not allowed', { status: 405 }); diff --git a/packages/server/src/api/routes/users/GET.ts b/packages/server/src/api/routes/users/GET.ts index 712070b2..a542056a 100644 --- a/packages/server/src/api/routes/users/GET.ts +++ b/packages/server/src/api/routes/users/GET.ts @@ -37,18 +37,17 @@ import { Config } from '../../../utils/Config'; export async function GET( config: Config, init: RequestInit & { request: Request }, - log: (...args: string[]) => void + log: (message: string | unknown, meta?: Record) => void ) { const yurl = new URL(init.request.url); const tenantId = yurl.searchParams.get('tenantId'); const tenant = tenantId ?? getTenantFromHttp(init.request.headers); if (!tenant) { - log('[GET]', '[ERROR]', 'No tenant id provided.'); + log('[GET] No tenant id provided.'); return new Response(null, { status: 404 }); } const url = apiRoutes(config).TENANT_USERS(tenant); - log('[GET]', url); init.method = 'GET'; return await request(url, init, config); } diff --git a/packages/server/src/api/routes/users/POST.ts b/packages/server/src/api/routes/users/POST.ts index bb55c33e..139ce140 100644 --- a/packages/server/src/api/routes/users/POST.ts +++ b/packages/server/src/api/routes/users/POST.ts @@ -67,8 +67,7 @@ import { Config } from '../../../utils/Config'; export async function POST( config: Config, - init: RequestInit & { request: Request }, - log?: (...args: string[]) => void + init: RequestInit & { request: Request } ) { init.body = init.request.body; init.method = 'POST'; @@ -77,7 +76,6 @@ export async function POST( const tenant = tenantId ?? getTenantFromHttp(init.request.headers); const url = apiRoutes(config).USERS(tenant ? tenant : undefined); - log && log('[POST]', url); return await request(url, init, config); } diff --git a/packages/server/src/api/routes/users/[userId]/PUT.ts b/packages/server/src/api/routes/users/[userId]/PUT.ts index e89ab96b..970a1882 100644 --- a/packages/server/src/api/routes/users/[userId]/PUT.ts +++ b/packages/server/src/api/routes/users/[userId]/PUT.ts @@ -43,8 +43,7 @@ import { Config } from '../../../../utils/Config'; export async function PUT( config: Config, session: void | ActiveSession, - init: RequestInit & { request: Request }, - log: (...args: string[]) => void + init: RequestInit & { request: Request } ) { if (!session) { return new Response(null, { status: 401 }); @@ -58,7 +57,5 @@ export async function PUT( const url = apiRoutes(config).USER(userId); - log('[PUT]', url); - return await fetch(url, init, config); } diff --git a/packages/server/src/api/routes/users/index.ts b/packages/server/src/api/routes/users/index.ts index c9e33253..bf3b75b1 100644 --- a/packages/server/src/api/routes/users/index.ts +++ b/packages/server/src/api/routes/users/index.ts @@ -13,8 +13,7 @@ const key = 'USERS'; export default async function route(request: Request, config: Config) { const { info } = Logger( { ...config, debug: config.debug } as Config, - '[ROUTES]', - `[${key}]` + `[ROUTES][${key}]` ); const session = await auth(request, config); @@ -22,9 +21,9 @@ export default async function route(request: Request, config: Config) { case 'GET': return await GET(config, { request }, info); case 'POST': - return await POST(config, { request }, info); + return await POST(config, { request }); case 'PUT': - return await PUT(config, session, { request }, info); + return await PUT(config, session, { request }); default: return new Response('method not allowed', { status: 405 }); diff --git a/packages/server/src/api/utils/auth.ts b/packages/server/src/api/utils/auth.ts index 72be69c9..4c7f6c61 100644 --- a/packages/server/src/api/utils/auth.ts +++ b/packages/server/src/api/utils/auth.ts @@ -23,7 +23,7 @@ export default async function auth( info('checking auth'); const sessionUrl = `${config.api.basePath}/auth/session`; - info('using session', sessionUrl); + info(`using session${sessionUrl}`); // handle the pass through with posts req.headers.delete('content-length'); diff --git a/packages/server/src/api/utils/request.ts b/packages/server/src/api/utils/request.ts index dbf30e75..5bf4bf8c 100644 --- a/packages/server/src/api/utils/request.ts +++ b/packages/server/src/api/utils/request.ts @@ -20,15 +20,21 @@ export default async function request( params.duplex = 'half'; } - info(`[${params.method ?? 'GET'}]`, url); const res = await fetch(url, { ...params }).catch((e) => { - error('An error has occurred in the fetch', e); + error('An error has occurred in the fetch', { + message: e.message, + stack: e.stack, + }); return new Response( 'An unexpected (most likely configuration) problem has occurred', { status: 500 } ); }); const loggingRes = typeof res?.clone === 'function' ? res?.clone() : null; - info('[Response]', res?.status, res?.statusText, await loggingRes?.text()); + info(`[${params.method ?? 'GET'}] ${url}`, { + status: res?.status, + statusText: res?.statusText, + text: await loggingRes?.text(), + }); return res; } diff --git a/packages/server/src/auth/index.ts b/packages/server/src/auth/index.ts index 90fb8499..6e660162 100644 --- a/packages/server/src/auth/index.ts +++ b/packages/server/src/auth/index.ts @@ -29,7 +29,7 @@ export default function serverAuth( throw new Error('Server side login requires a user email and password.'); } - info('Obtaining providers for', email); + info(`Obtaining providers for ${email}`); const sessionUrl = new URL(`${ORIGIN}${routes.PROVIDERS}`); const sessionReq = new Request(sessionUrl, { method: 'GET', @@ -47,7 +47,7 @@ export default function serverAuth( try { providers = await sessionRes?.json(); } catch (e) { - info(sessionUrl, sessionRes); + info(sessionUrl, { sessionRes }); error(e); } @@ -65,8 +65,8 @@ export default function serverAuth( const json = (await csrfRes?.json()) ?? {}; csrfToken = json?.csrfToken; } catch (e) { - info(sessionUrl, csrfRes); - error(e, csrfRes); + info(sessionUrl, { csrfRes }); + error(e, { csrfRes }); } const { credentials } = providers ?? {}; @@ -83,7 +83,7 @@ export default function serverAuth( if (!csrfCookie) { throw new Error('Unable to authenticate REST'); } - info('Attempting sign in with email', email); + info(`Attempting sign in with email ${email}`); const postReq = new Request(signInUrl, { method: 'POST', headers: new Headers({ @@ -106,7 +106,7 @@ export default function serverAuth( if (!token) { throw new Error('Server login failed'); } - info('Server login successful', authCookie, csrfCookie); + info('Server login successful', { authCookie, csrfCookie }); return new Headers({ cookie: [token, csrfCookie].join('; '), }); diff --git a/packages/server/src/db/DBManager.ts b/packages/server/src/db/DBManager.ts index 0f5b5ac2..9e832d61 100644 --- a/packages/server/src/db/DBManager.ts +++ b/packages/server/src/db/DBManager.ts @@ -31,12 +31,12 @@ export default class DBManager { watchEvictPool(this.poolWatcherFn); } poolWatcher = (config: ServerConfig) => (id: undefined | null | string) => { - const { info } = Logger(config, '[DBManager]'); + const { info, warn } = Logger(config, '[DBManager]'); if (id && this.connections.has(id)) { - info('Removing', id, 'from db connection pool.'); + info(`Removing ${id} from db connection pool.`); this.connections.delete(id); } else { - info('missed eviction of', id); + warn(`missed eviction of ${id}`); } }; @@ -45,16 +45,16 @@ export default class DBManager { const id = this.makeId(config.tenantId, config.userId); const existing = this.connections.get(id); - info('# of instances:', this.connections.size); + info(`# of instances: ${this.connections.size}`); if (existing) { - info('returning existing', id); + info(`returning existing ${id}`); existing.startTimeout(); return existing.pool; } const newOne = new NileDatabase(new Config(config), id); this.connections.set(id, newOne); - info('created new', id); - info('# of instances:', this.connections.size); + info(`created new ${id}`); + info(`# of instances: ${this.connections.size}`); if (this.cleared) { this.cleared = false; } @@ -63,7 +63,7 @@ export default class DBManager { clear = (config: ServerConfig) => { const { info } = Logger(config, '[DBManager]'); - info('Clearing all connections', this.connections.size); + info(`Clearing all connections ${this.connections.size}`); this.cleared = true; this.connections.clear(); }; diff --git a/packages/server/src/db/NileInstance.ts b/packages/server/src/db/NileInstance.ts index 0cf84ec9..f313140a 100644 --- a/packages/server/src/db/NileInstance.ts +++ b/packages/server/src/db/NileInstance.ts @@ -29,7 +29,7 @@ class NileDatabase { config.db = poolConfig; this.config = config; - info(this.config.db); + info(JSON.stringify(this.config.db)); this.pool = createProxyForPool(new Pool(remaining), this.config); @@ -47,15 +47,21 @@ class NileDatabase { afterCreate(client, (err) => { const { error } = Logger(config, '[after create callback]'); if (err) { - error('after create failed', err); + error('after create failed', { + message: err.message, + stack: err.stack, + }); evictPool(this.id); } }); this.startTimeout(); }); - this.pool.on('error', async (e) => { - info('pool failed', e); + this.pool.on('error', async (err) => { + info('pool failed', { + message: err.message, + stack: err.stack, + }); if (this.timer) { clearTimeout(this.timer); } @@ -70,15 +76,13 @@ class NileDatabase { } this.timer = setTimeout(() => { info( - 'Pool reached idleTimeoutMillis.', - this.id, - 'evicted after', - Number(this.config.db.idleTimeoutMillis) ?? 30000, - 'ms' + `Pool reached idleTimeoutMillis. ${this.id} evicted after ${ + Number(this.config.db.idleTimeoutMillis) ?? 30000 + }ms` ); this.pool.end(() => { evictPool(this.id); - info('Pool end called for', this.id); + info(`Pool end called for ${this.id}`); // something odd going on here. Without the callback, pool.end() is flakey }); }, Number(this.config.db.idleTimeoutMillis) ?? 30000); @@ -91,7 +95,10 @@ function makeAfterCreate(config: Config): AfterCreate { const { warn, info } = Logger(config, '[afterCreate]'); return (conn, done) => { conn.on('error', function errorHandler(error: Error) { - warn('Connection was terminated by server', error); + warn('Connection was terminated by server', { + message: error.message, + stack: error.stack, + }); done(error, conn); }); @@ -107,10 +114,10 @@ function makeAfterCreate(config: Config): AfterCreate { // in this example we use pg driver's connection API conn.query(query.join(';'), function (err: Error) { if (query.length === 1) { - info('[tenant id]', config.tenantId); + info(`[tenant id] ${config.tenantId}`); } if (query.length === 2) { - info('[user id]', config.userId); + info(`[user id] ${config.userId}`); } done(err, conn); }); diff --git a/packages/server/src/db/PoolProxy.ts b/packages/server/src/db/PoolProxy.ts index 8e4ee9a6..2e81040d 100644 --- a/packages/server/src/db/PoolProxy.ts +++ b/packages/server/src/db/PoolProxy.ts @@ -25,7 +25,7 @@ export function createProxyForPool(pool: Pool, config: Config): Pool { } const caller = target[property]; return function query(...args: AllowAny) { - info(...args); + info('query', ...args); // @ts-expect-error - not mine const called = caller.apply(this, args); return called; diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index 5ce8ab53..7d84e8e2 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -22,6 +22,11 @@ export type ServerConfig = { cookieKey?: string; token?: string; // process.env.NILEDB_TOKEN }; + logger?: { + info?: (args: unknown[]) => void; + warn?: (args: unknown[]) => void; + error?: (args: unknown[]) => void; + }; }; export type NileDb = NilePoolConfig & { tenantId?: string }; diff --git a/packages/server/src/utils/Config/envVars.ts b/packages/server/src/utils/Config/envVars.ts index 0cad9716..bb5293ba 100644 --- a/packages/server/src/utils/Config/envVars.ts +++ b/packages/server/src/utils/Config/envVars.ts @@ -12,14 +12,14 @@ export const getDatabaseId = (cfg: EnvConfig) => { const { info } = Logger(config, '[databaseId]'); if (config?.databaseId) { - logger && info(logger, 'config', config.databaseId); + logger && info(`${logger}[config] ${config.databaseId}`); return String(config?.databaseId); } if (process.env.NILEDB_POSTGRES_URL) { const pgUrl = new URL(process.env.NILEDB_POSTGRES_URL); return pgUrl.pathname.substring(1); } - logger && info(logger, 'env', process.env.NILEDB_ID); + logger && info(`${logger}[NILEDB_ID] ${process.env.NILEDB_ID}`); return process.env.NILEDB_ID; }; export const getUsername = (cfg: EnvConfig) => { @@ -27,10 +27,10 @@ export const getUsername = (cfg: EnvConfig) => { const { info } = Logger(config, '[username]'); if (config?.user) { - logger && info(logger, 'config', config.user); + logger && info(`${logger}[config] ${config.user}`); return String(config?.user); } - logger && info(logger, 'NILEDB_USER', process.env.NILEDB_USER); + logger && info(`${logger}[NILEDB_USER] ${process.env.NILEDB_USER}`); return process.env.NILEDB_USER; }; @@ -39,12 +39,12 @@ export const getPassword = (cfg: EnvConfig) => { const log = logProtector(logger); const { info } = Logger(config, '[password]'); if (config?.password) { - log && info(logger, 'config', config.password); + log && info(`${logger}[config] ${config.password}`); return String(config.password); } - log && info(logger, 'NILEDB_PASSWORD', process.env.NILEDB_PASSWORD); + logger && info(`${logger}[NILEDB_PASSWORD] ${process.env.NILEDB_PASSWORD}`); return process.env.NILEDB_PASSWORD; }; @@ -56,11 +56,11 @@ export const getToken = (cfg: EnvConfig) => { const { config, logger } = cfg; const { info } = Logger(config, '[token]'); if (config?.api?.token) { - logger && info(logger, 'config', config.api?.token); + logger && info(`${logger}[config] ${config.api?.token}`); return String(config.api?.token); } if (process.env.NILEDB_TOKEN) { - logger && info(logger, 'NILEDB_TOKEN', process.env.NILEDB_TOKEN); + logger && info(`${logger}[NILEDB_TOKEN] ${process.env.NILEDB_TOKEN}`); return process.env.NILEDB_TOKEN; } return undefined; @@ -70,11 +70,11 @@ export const getDatabaseName = (cfg: EnvConfig) => { const { config, logger } = cfg; const { info } = Logger(config, '[databaseName]'); if (config?.databaseName) { - logger && info(logger, 'config', config.databaseName); + logger && info(`${logger}[config] ${config.databaseName}`); return String(config.databaseName); } if (process.env.NILEDB_NAME) { - logger && info(logger, 'NILEDB_NAME', process.env.NILEDB_NAME); + logger && info(`${logger}[NILEDB_NAME] ${process.env.NILEDB_NAME}`); return process.env.NILEDB_NAME; } return null; @@ -84,12 +84,12 @@ export const getTenantId = (cfg: EnvConfig): string | null => { const { config, logger } = cfg; const { info } = Logger(config, '[tenantId]'); if (config?.tenantId) { - logger && info(logger, 'config', config.tenantId); + logger && info(`${logger}[config] ${config.tenantId}`); return config.tenantId; } if (process.env.NILEDB_TENANT) { - logger && info(logger, 'NILEDB_TENANT', process.env.NILEDB_TENANT); + logger && info(`${logger}[NILEDB_TENANT] ${process.env.NILEDB_TENANT}`); return process.env.NILEDB_TENANT; } @@ -102,20 +102,20 @@ export const getTenantId = (cfg: EnvConfig): string | null => { */ export const getBasePath = (cfg: EnvConfig): undefined | string => { const { config, logger } = cfg; - const { info } = Logger(config, '[basePath]'); + const { warn, info } = Logger(config, '[basePath]'); const basePath = config?.api?.basePath; if (basePath) { - logger && info(logger, 'config', basePath); + logger && info(`${logger}[config] ${basePath}`); return basePath; } if (process.env.NILEDB_API_URL) { - logger && info(logger, 'NILEDB_API_URL', process.env.NILEDB_API_URL); + logger && info(`${logger}[NILEDB_API_URL] ${process.env.NILEDB_API_URL}`); const apiUrl = new URL(process.env.NILEDB_API_URL); return apiUrl.href; } - info('not set. Must run auto-configuration'); + warn('not set. Must run auto-configuration'); return undefined; }; @@ -124,12 +124,13 @@ export const getControlPlane = (cfg: EnvConfig) => { const { info } = Logger(config, '[basePath]'); if (config?.configureUrl) { - logger && info(logger, 'config', config.configureUrl); + logger && info(`${logger}[config] ${config.configureUrl}`); return config.configureUrl; } if (process.env.NILEDB_CONFIGURE) { - logger && info(logger, 'NILEDB_CONFIGURE', process.env.NILEDB_CONFIGURE); + logger && + info(`${logger}[NILEDB_CONFIGURE] ${process.env.NILEDB_CONFIGURE}`); // backwards compatible, but not really if (!process.env.NILEDB_CONFIGURE.startsWith('http')) { return `https://${process.env.NILEDB_CONFIGURE}`; @@ -137,7 +138,7 @@ export const getControlPlane = (cfg: EnvConfig) => { return process.env.NILEDB_CONFIGURE; } - logger && info(logger, 'default', 'https://global.thenile.dev'); + logger && info(`${logger}[default] https://global.thenile.dev`); return 'https://global.thenile.dev'; }; @@ -146,22 +147,22 @@ export function getDbHost(cfg: EnvConfig) { const { info } = Logger(config, '[db.host]'); if (config?.db && config.db.host) { - logger && info(logger, 'config', config?.db.host); + logger && info(`${logger}[config] ${config?.db.host}`); return config.db.host; } if (process.env.NILEDB_POSTGRES_URL) { const pgUrl = new URL(process.env.NILEDB_POSTGRES_URL); - logger && info(logger, 'NILEDB_POSTGRES_URL', pgUrl.host); + logger && info(`${logger}[NILEDB_POSTGRES_URL] ${pgUrl.host}`); return pgUrl.host; } if (process.env.NILEDB_HOST) { - logger && info(logger, 'NILEDB_HOST', process.env.NILEDB_HOST); + logger && info(`${logger}[NILEDB_HOST] ${process.env.NILEDB_HOST}`); return process.env.NILEDB_HOST; } - logger && info(logger, 'default', 'db.thenile.dev'); + logger && info(`${logger}[default] db.thenile.dev`); return 'db.thenile.dev'; } @@ -169,15 +170,15 @@ export function getDbPort(cfg: EnvConfig): number { const { config, logger } = cfg; const { info } = Logger(config, '[db.port]'); if (config?.db?.port && config.db.port != null) { - logger && info(logger, 'config', config?.db.port); + logger && info(`${logger}[config] ${config?.db.port}`); return Number(config.db?.port); } if (process.env.NILEDB_PORT) { - logger && info(logger, 'config', process.env.NILEDB_PORT); + logger && info(`${logger}[NILEDB_PORT] ${process.env.NILEDB_PORT}`); return Number(process.env.NILEDB_PORT); } - logger && info(logger, 'default', 5432); + logger && info(`${logger}[default] 5432`); return 5432; } diff --git a/packages/server/src/utils/Config/index.ts b/packages/server/src/utils/Config/index.ts index 691076d9..bdbd4dc0 100644 --- a/packages/server/src/utils/Config/index.ts +++ b/packages/server/src/utils/Config/index.ts @@ -161,7 +161,7 @@ export class Config { database: this.databaseName, ...dbConfig, }; - info('[config set]', this); + info('[config set]', { db: this.db, api: this.api }); return this; } @@ -172,7 +172,7 @@ export class Config { if (databaseName) { url.searchParams.set('databaseName', databaseName); } - info('configuring from', url.href); + info(`configuring from ${url.href}`); const res = await fetch(url, { headers: { Authorization: `Bearer ${getInfoBearer({ config })}`, @@ -229,7 +229,7 @@ export class Config { database: this.databaseName, ...dbConfig, }; - info('[config set]', this); + info('[config set]', { db: this.db, api: this.api }); return this; }; } diff --git a/packages/server/src/utils/Logger.ts b/packages/server/src/utils/Logger.ts index 24fd58f1..8c5956e2 100644 --- a/packages/server/src/utils/Logger.ts +++ b/packages/server/src/utils/Logger.ts @@ -1,27 +1,42 @@ +/* eslint-disable no-console */ import { ServerConfig } from '../types'; -import { Config } from './Config'; +const red = '\x1b[31m'; +const yellow = '\x1b[33m'; +const reset = '\x1b[0m'; + +const baseLogger = (config: void | ServerConfig, ...params: unknown[]) => ({ + info(message: string | unknown, meta?: Record) { + if (config?.debug) { + console.info( + `[niledb][DEBUG]${params.join('')} ${message}`, + meta ? `\n${JSON.stringify(meta, null, 2)}` : '' + ); + } + }, + warn(message: string | unknown, meta?: Record) { + if (config?.debug) { + console.warn( + `${yellow}[niledb][WARN]${reset}${params.join('')} ${message}`, + JSON.stringify(meta, null, 2) + ); + } + }, + error(message: string | unknown, meta?: Record) { + console.error( + `${red}[niledb][ERROR]${reset}${params.join('')} ${message}`, + meta + ); + }, +}); export default function Logger( - config: void | Config | ServerConfig, + config: void | ServerConfig, ...params: unknown[] ) { - return { - info(...args: unknown[]) { - if (config?.debug) { - // eslint-disable-next-line no-console - console.info('[niledb]', ...params, ...args); - } - }, - warn(...args: unknown[]) { - if (config?.debug) { - // eslint-disable-next-line no-console - console.warn('[niledb]', ...params, ...args); - } - }, - error(...args: unknown[]) { - // eslint-disable-next-line no-console - console.error('[niledb]', '[ERROR]', ...params, ...args); - }, - }; + const base = baseLogger(config, params); + const info = config?.logger?.info ?? base.info; + const warn = config?.logger?.warn ?? base.warn; + const error = config?.logger?.error ?? base.error; + return { info, warn, error }; } diff --git a/packages/server/src/utils/fetch.ts b/packages/server/src/utils/fetch.ts index 7b82ea4a..f1482f29 100644 --- a/packages/server/src/utils/fetch.ts +++ b/packages/server/src/utils/fetch.ts @@ -97,21 +97,21 @@ export async function _fetch( .replace('{tenantId}', encodeURIComponent(String(tenantId))) .replace('{userId}', encodeURIComponent(String(userId))); - info('[fetch]', useableUrl); + info(`[fetch] ${useableUrl}`); const response = await fetch(useableUrl, { ...opts, headers: basicHeaders, }).catch((e) => { - error('[fetch]', '[response]', e); + error('[fetch][response]', { message: e.message, stack: e.stack }); }); if (response && response.status >= 200 && response.status < 300) { if (typeof response.clone === 'function') { try { - info('[fetch]', '[response]', await response.clone().json()); + info(`[fetch][response] ${await response.clone().json()}`); } catch (e) { - info('[fetch]', '[response]', await response.clone().text()); + info(`[fetch][response] ${await response.clone().text()}`, { e }); } } return response; @@ -136,11 +136,13 @@ export async function _fetch( if (errorHandler) { msg = await errorHandler.text(); if (msg) { - error('[fetch]', '[response]', `[status: ${errorHandler.status}]`, msg); + error(`[fetch][response] status: ${errorHandler.status}]`, { + message: msg, + }); } } if (!msg) { - error('[fetch]', '[response]', e); + error('[fetch][response]', { e }); } } if (msg) { @@ -149,32 +151,19 @@ export async function _fetch( if (res && 'message' in res) { const { message } = res; - error( - '[fetch]', - '[response]', - `[status: ${errorHandler?.status}]`, - message - ); + error(`[fetch][response] status: ${errorHandler?.status}] ${message}`); return new ResponseError(message, { status: 400 }); } if (res && 'errors' in res) { const { errors: [message], } = res; - error( - '[fetch]', - '[response]', - `[status: ${errorHandler?.status}]`, - message - ); + error(`[fetch][response] status: ${errorHandler?.status}] ${message}`); return new ResponseError(message, { status: 400 }); } - error( - '[fetch]', - '[response]', - `[status: ${errorHandler?.status}]`, - 'UNHANDLED ERROR' - ); + error(`[fetch][response] status: ${errorHandler?.status}] UNHANDLED ERROR`, { + res, + }); return new ResponseError(null, { status: (response as Response)?.status ?? 500, });