From 7ab7ca0ae1f271d7d581246b6d0e442cd75d485b Mon Sep 17 00:00:00 2001 From: krutoo Date: Tue, 12 Mar 2024 16:52:22 +0500 Subject: [PATCH] #38 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - preset/bun: модуль провайдеров разбит на отдельные модули по провайдерам (patch) - preset/node: переименования части провайдеров (patch) --- src/preset/bun/index.ts | 25 ++-- src/preset/bun/providers/config-source.ts | 6 + src/preset/bun/providers/fetch-middleware.ts | 7 + src/preset/bun/providers/index.ts | 130 ------------------ .../bun/providers/log-handler-sentry.ts | 19 +++ src/preset/bun/providers/logger.ts | 14 ++ src/preset/bun/providers/serve-metrics.ts | 20 +++ src/preset/bun/providers/serve-middleware.ts | 29 ++++ src/preset/bun/providers/serve.ts | 31 +++++ src/preset/bun/providers/service-routes.ts | 12 ++ src/preset/node/providers/log-handler-pino.ts | 2 +- .../node/providers/log-handler-sentry.ts | 2 +- src/preset/node/providers/logger.ts | 8 +- 13 files changed, 160 insertions(+), 145 deletions(-) create mode 100644 src/preset/bun/providers/config-source.ts create mode 100644 src/preset/bun/providers/fetch-middleware.ts delete mode 100644 src/preset/bun/providers/index.ts create mode 100644 src/preset/bun/providers/log-handler-sentry.ts create mode 100644 src/preset/bun/providers/logger.ts create mode 100644 src/preset/bun/providers/serve-metrics.ts create mode 100644 src/preset/bun/providers/serve-middleware.ts create mode 100644 src/preset/bun/providers/serve.ts create mode 100644 src/preset/bun/providers/service-routes.ts diff --git a/src/preset/bun/index.ts b/src/preset/bun/index.ts index e93c867..9348c66 100644 --- a/src/preset/bun/index.ts +++ b/src/preset/bun/index.ts @@ -2,9 +2,16 @@ import { createPreset } from '../../di'; import { KnownToken } from '../../tokens'; import { PresetTuner } from '../isomorphic'; import { provideBaseConfig } from '../isomorphic/providers/base-config'; +import { provideFetch } from '../isomorphic/providers/fetch'; import { provideKnownHttpApiHosts } from '../node/providers/known-http-api-hosts'; import { provideSsrBridgeServerSide } from '../node/providers/ssr-bridge-server-side'; -import { BunProviders } from './providers'; +import { provideConfigSource } from './providers/config-source'; +import { provideLogger } from './providers/logger'; +import { provideServe } from './providers/serve'; +import { provideServeMetrics } from './providers/serve-metrics'; +import { provideFetchMiddleware } from './providers/fetch-middleware'; +import { provideServiceRoutes } from './providers/service-routes'; +import { provideServeMiddleware } from './providers/serve-middleware'; /** * Возвращает preset с зависимостями для запуска приложения в Bun. @@ -16,26 +23,26 @@ export function PresetBun(customize?: PresetTuner) { const preset = createPreset(); // config - preset.set(KnownToken.Config.source, BunProviders.configSource); + preset.set(KnownToken.Config.source, provideConfigSource); preset.set(KnownToken.Config.base, provideBaseConfig); // log - preset.set(KnownToken.logger, BunProviders.logger); + preset.set(KnownToken.logger, provideLogger); // tracing // @todo // metrics - preset.set(KnownToken.Metrics.httpHandler, BunProviders.serveMetrics); + preset.set(KnownToken.Metrics.httpHandler, provideServeMetrics); // http fetch - preset.set(KnownToken.Http.fetch, BunProviders.fetch); - preset.set(KnownToken.Http.Fetch.middleware, BunProviders.fetchMiddleware); + preset.set(KnownToken.Http.fetch, provideFetch); + preset.set(KnownToken.Http.Fetch.middleware, provideFetchMiddleware); // http serve - preset.set(KnownToken.Http.serve, BunProviders.serve); - preset.set(KnownToken.Http.Serve.serviceRoutes, BunProviders.serviceRoutes); - preset.set(KnownToken.Http.Serve.middleware, BunProviders.serveMiddleware); + preset.set(KnownToken.Http.serve, provideServe); + preset.set(KnownToken.Http.Serve.serviceRoutes, provideServiceRoutes); + preset.set(KnownToken.Http.Serve.middleware, provideServeMiddleware); // http api preset.set(KnownToken.Http.Api.knownHosts, provideKnownHttpApiHosts); diff --git a/src/preset/bun/providers/config-source.ts b/src/preset/bun/providers/config-source.ts new file mode 100644 index 0000000..c50e995 --- /dev/null +++ b/src/preset/bun/providers/config-source.ts @@ -0,0 +1,6 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { ConfigSource, createConfigSource } from '../../../config'; + +export function provideConfigSource(): ConfigSource { + return createConfigSource(Bun.env); +} diff --git a/src/preset/bun/providers/fetch-middleware.ts b/src/preset/bun/providers/fetch-middleware.ts new file mode 100644 index 0000000..4bb440c --- /dev/null +++ b/src/preset/bun/providers/fetch-middleware.ts @@ -0,0 +1,7 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Middleware } from '../../../http'; + +export function provideFetchMiddleware(): Middleware[] { + // @todo добавить логирование ошибок? + return []; +} diff --git a/src/preset/bun/providers/index.ts b/src/preset/bun/providers/index.ts deleted file mode 100644 index f0aece0..0000000 --- a/src/preset/bun/providers/index.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ -import { Resolve } from '../../../di'; -import { KnownToken } from '../../../tokens'; -import { ConfigSource, createConfigSource } from '../../../config'; -import { Logger, createLogger } from '../../../log'; -import { Handler, Middleware } from '../../../http'; -import { providePinoHandler } from '../../node/providers/log-handler-pino'; -import { route, router } from '@krutoo/fetch-tools'; -import { getCurrentHub, init, runWithAsyncContext } from '@sentry/bun'; -import { createSentryHandler } from '../../../log/handler/sentry'; -import { provideFetch } from '../../isomorphic/providers/fetch'; -import { ServerHandler, ServerMiddleware } from '../../server/types'; -import { getStatsHandler } from '../utils/get-stats-handler'; -import { getHealthCheck } from '../../server/utils/get-health-check'; -import { getServeLogging } from '../../server/utils/get-serve-logging'; -import { getServeErrorLogging } from '../../server/utils/get-serve-error-logging'; -import { getServeMeasuring } from '../../server/utils/get-serve-measuring'; -import { applyServerMiddleware } from '../../server/utils/apply-server-middleware'; -import PromClient from 'prom-client'; - -export const BunProviders = { - configSource(): ConfigSource { - return createConfigSource(Bun.env); - }, - - logger(resolve: Resolve): Logger { - const logger = createLogger(); - - logger.subscribe(BunProviders.logHandlerPino(resolve)); - logger.subscribe(BunProviders.logHandlerSentry(resolve)); - - return logger; - }, - - logHandlerPino(resolve: Resolve) { - return providePinoHandler(resolve); - }, - - logHandlerSentry(resolve: Resolve) { - const source = resolve(KnownToken.Config.source); - - init({ - dsn: source.require('SENTRY_DSN'), - release: source.require('SENTRY_RELEASE'), - environment: source.require('SENTRY_ENVIRONMENT'), - }); - - // ВАЖНО: передаем функцию чтобы брать текущий hub в момент вызова метода logger'а - // это нужно чтобы хлебные крошки в ошибках Sentry группировались по запросам - return createSentryHandler(getCurrentHub); - }, - - fetch: provideFetch, - - fetchMiddleware(): Middleware[] { - return []; - }, - - serve(resolve: Resolve): Handler { - const middleware = resolve(KnownToken.Http.Serve.middleware); - const routes = resolve(KnownToken.Http.Serve.routes); - const serviceRoutes = resolve(KnownToken.Http.Serve.serviceRoutes); - - const enhance = applyServerMiddleware(...middleware); - - return router( - // маршруты с промежуточными слоями - ...routes.map(([pattern, handler]) => { - const enhancedHandler = enhance(handler); - - return route.get(pattern, request => - enhancedHandler(request, { events: new EventTarget() }), - ); - }), - - // @todo вместо routes обрабатывать pageRoutes с помощью route.get() из новой версии fetch-tools (для явности) - // @todo также добавить apiRoutes и обрабатывать их с помощью с помощью route()? - - // служебные маршруты (без промежуточных слоев) - ...serviceRoutes.map(([pattern, handler]) => - route(pattern, request => handler(request, { events: new EventTarget() })), - ), - ); - }, - - serviceRoutes(): Array<[string, ServerHandler]> { - return [ - // служебные маршруты (без промежуточных слоев) - ['/healthcheck', getHealthCheck()], - ['/stats', getStatsHandler()], - ]; - }, - - serveMiddleware(resolve: Resolve): ServerMiddleware[] { - const config = resolve(KnownToken.Config.base); - const logger = resolve(KnownToken.logger); - - return [ - // ВАЖНО: изолируем хлебные крошки чтобы они группировались по входящим запросам - (request, next) => runWithAsyncContext(async () => next(request)), - - // ВАЖНО: слой логирования ошибки ПЕРЕД остальными слоями чтобы не упустить ошибки выше - getServeErrorLogging(logger), - - // @todo tracing - - // метрики - getServeMeasuring(config), - - // ВАЖНО: слой логирования запроса и ответа ПОСЛЕ остальных слоев чтобы использовать актуальные данные - getServeLogging(logger), - ]; - }, - - serveMetrics(): Handler { - // @todo задействовать когда Bun реализует pref_hooks.monitorEventLoopDelay (https://github.com/siimon/prom-client/issues/570) - // PromClient.collectDefaultMetrics(); - - return router( - route.get('/', async () => { - const metrics = await PromClient.register.metrics(); - const headers = new Headers(); - - headers.set('Content-Type', PromClient.register.contentType); - - return new Response(metrics, { headers }); - }), - ); - }, -} as const; diff --git a/src/preset/bun/providers/log-handler-sentry.ts b/src/preset/bun/providers/log-handler-sentry.ts new file mode 100644 index 0000000..c549d33 --- /dev/null +++ b/src/preset/bun/providers/log-handler-sentry.ts @@ -0,0 +1,19 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Resolve } from '../../../di'; +import { KnownToken } from '../../../tokens'; +import { getCurrentHub, init } from '@sentry/bun'; +import { createSentryHandler } from '../../../log/handler/sentry'; + +export function provideLogHandlerSentry(resolve: Resolve) { + const source = resolve(KnownToken.Config.source); + + init({ + dsn: source.require('SENTRY_DSN'), + release: source.require('SENTRY_RELEASE'), + environment: source.require('SENTRY_ENVIRONMENT'), + }); + + // ВАЖНО: передаем функцию чтобы брать текущий hub в момент вызова метода logger'а + // это нужно чтобы хлебные крошки в ошибках Sentry группировались по запросам + return createSentryHandler(getCurrentHub); +} diff --git a/src/preset/bun/providers/logger.ts b/src/preset/bun/providers/logger.ts new file mode 100644 index 0000000..da5702e --- /dev/null +++ b/src/preset/bun/providers/logger.ts @@ -0,0 +1,14 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Resolve } from '../../../di'; +import { Logger, createLogger } from '../../../log'; +import { provideLogHandlerPino } from '../../node/providers/log-handler-pino'; +import { provideLogHandlerSentry } from './log-handler-sentry'; + +export function provideLogger(resolve: Resolve): Logger { + const logger = createLogger(); + + logger.subscribe(provideLogHandlerPino(resolve)); + logger.subscribe(provideLogHandlerSentry(resolve)); + + return logger; +} diff --git a/src/preset/bun/providers/serve-metrics.ts b/src/preset/bun/providers/serve-metrics.ts new file mode 100644 index 0000000..6213347 --- /dev/null +++ b/src/preset/bun/providers/serve-metrics.ts @@ -0,0 +1,20 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Handler } from '../../../http'; +import { route, router } from '@krutoo/fetch-tools'; +import PromClient from 'prom-client'; + +export function provideServeMetrics(): Handler { + // @todo задействовать когда Bun реализует pref_hooks.monitorEventLoopDelay (https://github.com/siimon/prom-client/issues/570) + // PromClient.collectDefaultMetrics(); + + return router( + route.get('/', async () => { + const metrics = await PromClient.register.metrics(); + const headers = new Headers(); + + headers.set('Content-Type', PromClient.register.contentType); + + return new Response(metrics, { headers }); + }), + ); +} diff --git a/src/preset/bun/providers/serve-middleware.ts b/src/preset/bun/providers/serve-middleware.ts new file mode 100644 index 0000000..397ddc4 --- /dev/null +++ b/src/preset/bun/providers/serve-middleware.ts @@ -0,0 +1,29 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Resolve } from '../../../di'; +import { KnownToken } from '../../../tokens'; +import { runWithAsyncContext } from '@sentry/bun'; +import { ServerMiddleware } from '../../server/types'; +import { getServeLogging } from '../../server/utils/get-serve-logging'; +import { getServeErrorLogging } from '../../server/utils/get-serve-error-logging'; +import { getServeMeasuring } from '../../server/utils/get-serve-measuring'; + +export function provideServeMiddleware(resolve: Resolve): ServerMiddleware[] { + const config = resolve(KnownToken.Config.base); + const logger = resolve(KnownToken.logger); + + return [ + // ВАЖНО: изолируем хлебные крошки чтобы они группировались по входящим запросам + (request, next) => runWithAsyncContext(async () => next(request)), + + // ВАЖНО: слой логирования ошибки ПЕРЕД остальными слоями чтобы не упустить ошибки выше + getServeErrorLogging(logger), + + // @todo tracing + + // метрики + getServeMeasuring(config), + + // ВАЖНО: слой логирования запроса и ответа ПОСЛЕ остальных слоев чтобы использовать актуальные данные + getServeLogging(logger), + ]; +} diff --git a/src/preset/bun/providers/serve.ts b/src/preset/bun/providers/serve.ts new file mode 100644 index 0000000..b42e2f6 --- /dev/null +++ b/src/preset/bun/providers/serve.ts @@ -0,0 +1,31 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { Resolve } from '../../../di'; +import { KnownToken } from '../../../tokens'; +import { Handler } from '../../../http'; +import { route, router } from '@krutoo/fetch-tools'; +import { applyServerMiddleware } from '../../server/utils/apply-server-middleware'; + +export function provideServe(resolve: Resolve): Handler { + const middleware = resolve(KnownToken.Http.Serve.middleware); + const routes = resolve(KnownToken.Http.Serve.routes); + const serviceRoutes = resolve(KnownToken.Http.Serve.serviceRoutes); + + const enhance = applyServerMiddleware(...middleware); + + return router( + // маршруты с промежуточными слоями + ...routes.map(([pattern, handler]) => { + const enhancedHandler = enhance(handler); + + return route.get(pattern, request => enhancedHandler(request, { events: new EventTarget() })); + }), + + // @todo вместо routes обрабатывать pageRoutes с помощью route.get() из новой версии fetch-tools (для явности) + // @todo также добавить apiRoutes и обрабатывать их с помощью с помощью route()? + + // служебные маршруты (без промежуточных слоев) + ...serviceRoutes.map(([pattern, handler]) => + route(pattern, request => handler(request, { events: new EventTarget() })), + ), + ); +} diff --git a/src/preset/bun/providers/service-routes.ts b/src/preset/bun/providers/service-routes.ts new file mode 100644 index 0000000..94cb6e3 --- /dev/null +++ b/src/preset/bun/providers/service-routes.ts @@ -0,0 +1,12 @@ +/* eslint-disable require-jsdoc, jsdoc/require-jsdoc */ +import { ServerHandler } from '../../server/types'; +import { getStatsHandler } from '../utils/get-stats-handler'; +import { getHealthCheck } from '../../server/utils/get-health-check'; + +export function provideServiceRoutes(): Array<[string, ServerHandler]> { + return [ + // служебные маршруты (без промежуточных слоев) + ['/healthcheck', getHealthCheck()], + ['/stats', getStatsHandler()], + ]; +} diff --git a/src/preset/node/providers/log-handler-pino.ts b/src/preset/node/providers/log-handler-pino.ts index 4eb802d..e089640 100644 --- a/src/preset/node/providers/log-handler-pino.ts +++ b/src/preset/node/providers/log-handler-pino.ts @@ -10,7 +10,7 @@ import { KnownToken } from '../../../tokens'; * @param resolve Функция для получения зависимости по токену. * @return Обработчик. */ -export function providePinoHandler(resolve: Resolve): LogHandler { +export function provideLogHandlerPino(resolve: Resolve): LogHandler { const config = resolve(KnownToken.Config.base); const pinoLogger = pino( diff --git a/src/preset/node/providers/log-handler-sentry.ts b/src/preset/node/providers/log-handler-sentry.ts index 26cb116..b491993 100644 --- a/src/preset/node/providers/log-handler-sentry.ts +++ b/src/preset/node/providers/log-handler-sentry.ts @@ -9,7 +9,7 @@ import { KnownToken } from '../../../tokens'; * @param resolve Функция для получения зависимости по токену. * @return Обработчик. */ -export function provideSentryHandler(resolve: Resolve): LogHandler { +export function provideLogHandlerSentry(resolve: Resolve): LogHandler { const source = resolve(KnownToken.Config.source); // экспериментально пробуем не использовать вручную созданный клиент diff --git a/src/preset/node/providers/logger.ts b/src/preset/node/providers/logger.ts index 6ed1784..da1623b 100644 --- a/src/preset/node/providers/logger.ts +++ b/src/preset/node/providers/logger.ts @@ -1,7 +1,7 @@ import { Resolve } from '../../../di'; import { Logger, createLogger } from '../../../log'; -import { providePinoHandler } from './log-handler-pino'; -import { provideSentryHandler } from './log-handler-sentry'; +import { provideLogHandlerPino } from './log-handler-pino'; +import { provideLogHandlerSentry } from './log-handler-sentry'; /** * Провайдер Logger'а. @@ -12,8 +12,8 @@ export function provideLogger(resolve: Resolve): Logger { const logger = createLogger(); // @todo возможно надо придумать как не давать вызывать провайдеры внутри провайдеров - logger.subscribe(providePinoHandler(resolve)); - logger.subscribe(provideSentryHandler(resolve)); + logger.subscribe(provideLogHandlerPino(resolve)); + logger.subscribe(provideLogHandlerSentry(resolve)); return logger; }