Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/fix-hono-path-normalization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@naverpay/prometheus-hono': minor
---

fix(hono): 경로 정규화가 동작하지 않아 동적 경로가 메트릭 라벨에 그대로 쌓이던 문제 수정

- `getHonoMetricsMiddleware`: Next.js 라우트 그룹핑에 `url.href`(scheme/host 포함)가 아니라 `url.pathname`을 넘기도록 수정. 기존에는 `/http://host/...` 형태의 비정규화 라벨이 생성되었습니다.
- `getHonoMetricsMiddleware`: 사용자가 제공한 `normalizePath`가 우선 적용되도록 수정. 기존 `||` 체이닝에서는 Next.js 그룹핑이 미매칭 시에도 비어있지 않은 값을 반환해 `normalizePath`가 무시되었습니다. 이제 `normalizePath`가 값을 반환하면 그 값을 사용하고, `undefined`를 반환하면 Next.js 그룹핑 → 기본 정규화로 위임합니다(하이브리드 앱에서 "API 경로만 직접 정규화, 나머지는 Next.js에 위임" 가능).
- `normalizePath` 반환 타입을 `string | undefined`로 확장.
- `createNormalizedHonoRouterPath`: `prefix`가 두 번 부착되던 버그 수정(`/prefix/prefix/...`). 이제 `prefix` 인자가 정상 동작합니다.
4 changes: 2 additions & 2 deletions packages/hono/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ interface HonoPrometheusExporterOptions {
collectDefaultMetrics?: boolean
/** 요청 바이패스 함수 */
bypass?: (context: Context) => boolean
/** 경로 정규화 함수 */
normalizePath?: (context: Context) => string
/** 경로 정규화 함수 (undefined 반환 시 Next.js 그룹핑/기본 정규화로 위임) */
normalizePath?: (context: Context) => string | undefined
/** 상태 코드 포맷팅 함수 */
formatStatusCode?: (context: Context) => string
}
Expand Down
22 changes: 17 additions & 5 deletions packages/hono/src/middleware/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,23 @@ export function getHonoMetricsMiddleware({

const extendedNormalizePath = (context: Context) => {
const url = new URL(context.req.url)
return (
normalizeNextRoutesPath?.(url.href) ||
normalizePath?.(context) ||
normalizeUrlWithTrimming(url.pathname, maxNormalizedUrlDepth)
)

// 사용자가 제공한 normalizePath를 최우선으로 사용한다.
// 값을 반환하면 그 값을 쓰고, undefined를 반환하면 "이 경로는 처리하지 않음"으로 보고
// Next.js 그룹핑 → 기본 정규화 순서로 위임한다.
// (기존 `||` 체이닝은 Next.js 그룹핑이 미매칭 시에도 비어있지 않은 값을 반환해 normalizePath가 무시되었다)
const normalized = normalizePath?.(context)
if (normalized !== undefined) {
return normalized
}

// Next.js 라우트 그룹핑은 전체 URL(href)이 아닌 pathname 기준으로 매칭해야 한다.
// (href를 넘기면 scheme/host가 경로에 섞여 `/http://host/...` 형태의 비정규화 라벨이 생성된다)
if (normalizeNextRoutesPath) {
return normalizeNextRoutesPath(url.pathname)
}

return normalizeUrlWithTrimming(url.pathname, maxNormalizedUrlDepth)
}

return async (context, next) => {
Expand Down
4 changes: 3 additions & 1 deletion packages/hono/src/router/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export function createNormalizedHonoRouterPath<E extends Env, S extends Schema,
app: Hono<E, S, B>,
prefix = '',
) {
const paths = getHonoRouterPaths(app, prefix)
// getHonoRouterPaths에 prefix를 넘기지 않는다. prefix 부착은 createPathTesters가 전담하며,
// 양쪽에 모두 넘기면 prefix가 두 번 붙어(`/prefix/prefix/...`) 정규식 매칭이 깨진다.
const paths = getHonoRouterPaths(app)
const testers = createPathTesters(paths, prefix)

return function getNormalizedHonoRouterPath(pathname: string) {
Expand Down
8 changes: 6 additions & 2 deletions packages/hono/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import type {Context} from 'hono'
export interface HonoPrometheusExporterOptions extends CommonPrometheusExporterOptions {
/** Function to determine if a request should be bypassed from metrics collection */
bypass?: (context: Context) => boolean
/** Function to normalize/group request paths for metrics */
normalizePath?: (context: Context) => string
/**
* Function to normalize/group request paths for metrics.
* Return a string to use it as the path label. Return `undefined` to skip this path and
* fall through to Next.js route grouping (when `nextjs` is enabled) and then default normalization.
*/
normalizePath?: (context: Context) => string | undefined
/** Function to format status codes for metrics */
formatStatusCode?: (context: Context) => string
}
Loading