Skip to content

Commit

Permalink
Merge pull request #115 from sima-land/38-examples-bun
Browse files Browse the repository at this point in the history
Шаг 68 #38
  • Loading branch information
krutoo committed Mar 12, 2024
2 parents 6e31158 + ebdd5f9 commit f4af172
Show file tree
Hide file tree
Showing 21 changed files with 454 additions and 423 deletions.
3 changes: 2 additions & 1 deletion src/preset/bun/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { createPreset } from '../../di';
import { KnownToken } from '../../tokens';
import { PresetTuner } from '../isomorphic';
import { provideBaseConfig } from '../isomorphic/providers';
import { provideKnownHttpApiHosts, provideSsrBridgeServerSide } from '../node/providers';
import { provideKnownHttpApiHosts } from '../node/providers/known-http-api-hosts';
import { provideSsrBridgeServerSide } from '../node/providers/ssr-bridge-server-side';
import { BunProviders } from './providers';

/**
Expand Down
2 changes: 1 addition & 1 deletion src/preset/bun/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { KnownToken } from '../../../tokens';
import { ConfigSource, createConfigSource } from '../../../config';
import { Logger, createLogger } from '../../../log';
import { Handler, Middleware } from '../../../http';
import { providePinoHandler } from '../../node/providers';
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';
Expand Down
32 changes: 15 additions & 17 deletions src/preset/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@ import { provideAxiosFactory, provideBaseConfig, provideFetch } from '../isomorp
import { Preset, createPreset } from '../../di';
import { PresetTuner } from '../isomorphic/types';
import { healthCheck } from '../../utils/express/handler/health-check';
import {
provideConfigSource,
provideExpressErrorMiddleware,
provideExpressFactory,
provideExpressLogMiddleware,
provideExpressMetricsMiddleware,
provideExpressRequestMiddleware,
provideExpressTracingMiddleware,
provideKnownHttpApiHosts,
provideLogger,
provideMetricsHttpApp,
provideSpanExporter,
provideSsrBridgeServerSide,
provideTracer,
provideTracerProvider,
provideTracerProviderResource,
} from './providers';
import { provideConfigSource } from './providers/config-source';
import { provideExpressErrorMiddleware } from './providers/express-error-middleware';
import { provideExpressFactory } from './providers/express-factory';
import { provideExpressLogMiddleware } from './providers/express-log-middleware';
import { provideExpressMetricsMiddleware } from './providers/express-metrics-middleware';
import { provideExpressRequestMiddleware } from './providers/express-request-middleware';
import { provideExpressTracingMiddleware } from './providers/express-tracing-middleware';
import { provideKnownHttpApiHosts } from './providers/known-http-api-hosts';
import { provideLogger } from './providers/logger';
import { provideMetricsHttpApp } from './providers/metrics-http-app';
import { provideSpanExporter } from './providers/span-exporter';
import { provideSsrBridgeServerSide } from './providers/ssr-bridge-server-side';
import { provideTracer } from './providers/tracer';
import { provideTracerProvider } from './providers/tracer-provider';
import { provideTracerProviderResource } from './providers/tracer-provider-resource';

/**
* Возвращает preset с зависимостями по умолчанию для frontend-микросервисов на Node.js.
Expand Down
20 changes: 20 additions & 0 deletions src/preset/node/providers/config-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import path from 'node:path';
import { config as applyDotenv } from 'dotenv';
import { ConfigSource, createConfigSource } from '../../../config';

/**
* Провайдер источника конфигурации.
* @return Источник конфигурации.
*/
export function provideConfigSource(): ConfigSource {
const envName = process.env.NODE_ENV;

// подключаем соответствующий среде файл со значениями по умолчанию
if (envName) {
applyDotenv({ path: path.resolve(process.cwd(), `./.env.${envName}`) });
}

// @todo подключать .env, .env.local, .env.<envName>, .env.<envName>.local как в Vite/Bun

return createConfigSource(process.env);
}
10 changes: 10 additions & 0 deletions src/preset/node/providers/express-error-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type express from 'express';
import { Handlers } from '@sentry/node';

/**
* Провайдер промежуточного слоя обработки ошибок в рамках ответ на http-запросы.
* @return Промежуточный слой.
*/
export function provideExpressErrorMiddleware(): express.ErrorRequestHandler {
return Handlers.errorHandler();
}
9 changes: 9 additions & 0 deletions src/preset/node/providers/express-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express';

/**
* Провайдер фабрики http-серверов.
* @return Фабрика.
*/
export function provideExpressFactory() {
return express;
}
49 changes: 49 additions & 0 deletions src/preset/node/providers/express-log-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type express from 'express';
import type { Resolve } from '../../../di';
import type { ConventionalFluentInfo } from '../../../log/types';
import { KnownToken } from '../../../tokens';
import { getClientIp } from '../utils/get-client-ip';
import { toMilliseconds } from '../../../utils';

/**
* Провайдер промежуточного слоя логирования входящих http-запросов.
* @param resolve Функция для получения зависимости по токену.
* @return Промежуточный слой.
*/
export function provideExpressLogMiddleware(resolve: Resolve): express.Handler {
const config = resolve(KnownToken.Config.base);
const logger = resolve(KnownToken.logger);

return (req, res, next) => {
const start = process.hrtime.bigint();
const remoteIp = getClientIp(req) ?? '';

const startMsg: Omit<ConventionalFluentInfo, 'latency' | 'status'> & { type: string } = {
type: 'http.request[incoming]',
version: config.appVersion,
route: req.originalUrl,
method: req.method,
remote_ip: remoteIp,
};

logger.info(startMsg);

res.once('finish', () => {
const finish = process.hrtime.bigint();

const finishMsg: ConventionalFluentInfo & { type: string } = {
type: 'http.response[outgoing]',
version: config.appVersion,
route: req.originalUrl,
method: req.method,
status: res.statusCode,
remote_ip: remoteIp,
latency: toMilliseconds(finish - start),
};

logger.info(finishMsg);
});

next();
};
}
64 changes: 64 additions & 0 deletions src/preset/node/providers/express-metrics-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type express from 'express';
import type { Resolve } from '../../../di';
import { LABEL_NAMES, getHandlerMetrics } from '../../server/utils/get-handler-metrics';
import { KnownToken } from '../../../tokens';
import { PAGE_HANDLER_EVENT_TYPE } from '../../server';
import { toMilliseconds } from '../../../utils';

/**
* Провайдер промежуточного слоя сбора метрик входящих http-запросов.
* @param resolve Функция для получения зависимости по токену.
* @return Промежуточный слой.
*/
export function provideExpressMetricsMiddleware(resolve: Resolve): express.Handler {
const config = resolve(KnownToken.Config.base);
const { requestCount, renderDuration, responseDuration } = getHandlerMetrics();

/**
* Функция формирования labels.
* @param req Request.
* @param res Response.
* @return Labels.
*/
const getLabels = (
req: express.Request,
res: express.Response,
): Record<(typeof LABEL_NAMES.httpResponse)[number], string | number> => ({
version: config.appVersion,
route: req.baseUrl + req.path,
code: res.statusCode,
method: req.method,
});

/** @inheritdoc */
const getRenderLabels = (request: express.Request) =>
({
version: config.appVersion,
method: request.method,
route: request.url,
}) satisfies Record<(typeof LABEL_NAMES.pageRender)[number], string | number>;

return (req, res, next) => {
const responseStart = process.hrtime.bigint();

requestCount.inc(getLabels(req, res), 1);

res.once(PAGE_HANDLER_EVENT_TYPE.renderStart, () => {
const renderStart = process.hrtime.bigint();

res.once(PAGE_HANDLER_EVENT_TYPE.renderFinish, () => {
const renderFinish = process.hrtime.bigint();

renderDuration.observe(getRenderLabels(req), toMilliseconds(renderFinish - renderStart));
});
});

res.once('finish', () => {
const responseFinish = process.hrtime.bigint();

responseDuration.observe(getLabels(req, res), toMilliseconds(responseFinish - responseStart));
});

next();
};
}
10 changes: 10 additions & 0 deletions src/preset/node/providers/express-request-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type express from 'express';
import { Handlers } from '@sentry/node';

/**
* Провайдер промежуточного слоя учета входящих http-запросов.
* @return Промежуточный слой.
*/
export function provideExpressRequestMiddleware(): express.Handler {
return Handlers.requestHandler();
}
64 changes: 64 additions & 0 deletions src/preset/node/providers/express-tracing-middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type express from 'express';
import type { Resolve } from '../../../di';
import { KnownToken } from '../../../tokens';
import { ROOT_CONTEXT, propagation, trace } from '@opentelemetry/api';
import { PAGE_HANDLER_EVENT_TYPE } from '../../server';

/**
* Провайдер промежуточного слоя трассировки входящих http-запросов.
* @param resolve Функция для получения зависимости по токену.
* @return Промежуточный слой.
*/
export function provideExpressTracingMiddleware(resolve: Resolve): express.Handler {
const tracer = resolve(KnownToken.Tracing.tracer);

/**
* Возвращает набор стандартных атрибутов для спана.
* @param req Входящий http-запрос.
* @return Атрибуты.
*/
const getConventionalRequestAttrs = (
req: express.Request,
): Record<string, string | undefined> => {
const result: Record<string, string | undefined> = {
'request.path': req.originalUrl,
};

for (const headerName in req.headers) {
if (headerName.toLowerCase().startsWith('simaland-')) {
result[headerName] = req.header(headerName);
}
}

return result;
};

return (req, res, next) => {
const externalContext = propagation.extract(ROOT_CONTEXT, req.headers);
const rootSpan = tracer.startSpan('response', undefined, externalContext);

rootSpan.setAttributes(getConventionalRequestAttrs(req));

const rootContext = trace.setSpan(externalContext, rootSpan);

res.locals.tracing = {
rootSpan,
rootContext,
renderSpan: null,
};

res.once(PAGE_HANDLER_EVENT_TYPE.renderStart, () => {
res.locals.tracing.renderSpan = tracer.startSpan('render', undefined, rootContext);

res.once(PAGE_HANDLER_EVENT_TYPE.renderFinish, () => {
res.locals.tracing.renderSpan.end();
});
});

res.once('finish', () => {
rootSpan.end();
});

next();
};
}
Loading

0 comments on commit f4af172

Please sign in to comment.