From d17ebe81d08670ceab8ed71b2fb059b59de484ed Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 5 Mar 2024 13:55:39 +0800 Subject: [PATCH] feat(index): add back hot stats (#14664) --- lib/errors/index.test.ts | 8 ++-- lib/errors/index.ts | 21 +++++++--- lib/middleware/debug.ts | 11 +++++ lib/utils/debug-info.ts | 17 +++++++- lib/utils/git-hash.ts | 2 +- lib/views/error.art | 8 ++-- lib/views/index.tsx | 87 +++++++++++++++++++++++++++++++++++----- 7 files changed, 128 insertions(+), 26 deletions(-) diff --git a/lib/errors/index.test.ts b/lib/errors/index.test.ts index 0f1ff4ed40..fb2131387d 100644 --- a/lib/errors/index.test.ts +++ b/lib/errors/index.test.ts @@ -52,16 +52,16 @@ describe('route throws an error', () => { expect(value).toBe('7'); break; case 'Hot Routes:': - expect(value).toBe('4 /test/:id
'); + expect(value).toBe('4 /test/:id
'); break; case 'Hot Paths:': - expect(value).toBe('2 /test/error
2 /test/slow
1 /test/httperror
1 /thisDoesNotExist
1 /
'); + expect(value).toBe('2 /test/error
2 /test/slow
1 /test/httperror
1 /thisDoesNotExist
1 /
'); break; case 'Hot Error Routes:': - expect(value).toBe('3 /test/:id
'); + expect(value).toBe('4 /test/:id
'); break; case 'Hot Error Paths:': - expect(value).toBe('2 /test/error
1 /test/httperror
1 /test/slow
1 /thisDoesNotExist
'); + expect(value).toBe('2 /test/error
1 /test/httperror
1 /test/slow
1 /thisDoesNotExist
'); break; default: } diff --git a/lib/errors/index.ts b/lib/errors/index.ts index 6b775e5693..db0c66849a 100644 --- a/lib/errors/index.ts +++ b/lib/errors/index.ts @@ -15,6 +15,9 @@ import { getCurrentPath } from '@/utils/helpers'; const __dirname = getCurrentPath(import.meta.url); export const errorHandler: ErrorHandler = (error, ctx) => { + const requestPath = ctx.req.path; + const matchedRoute = ctx.req.routePath; + const hasMatchedRoute = matchedRoute !== '/*'; let message = ''; if (error.name && (error.name === 'HTTPError' || error.name === 'RequestError')) { message = `${error.message}: target website might be blocking our access, you can host your own RSSHub instance for a better usability.`; @@ -27,16 +30,26 @@ export const errorHandler: ErrorHandler = (error, ctx) => { debug.hitCache++; } debug.error++; + + if (!debug.errorPaths[requestPath]) { + debug.errorPaths[requestPath] = 0; + } + debug.errorPaths[requestPath]++; + + if (!debug.errorRoutes[matchedRoute] && hasMatchedRoute) { + debug.errorRoutes[matchedRoute] = 0; + } + hasMatchedRoute && debug.errorRoutes[matchedRoute]++; setDebugInfo(debug); if (config.sentry.dsn) { Sentry.withScope((scope) => { - scope.setTag('name', ctx.req.path.split('/')[1]); + scope.setTag('name', requestPath.split('/')[1]); Sentry.captureException(error); }); } - logger.error(`Error in ${ctx.req.path}: ${message}`); + logger.error(`Error in ${requestPath}: ${message}`); if (config.isPackage) { return ctx.json({ @@ -59,13 +72,11 @@ export const errorHandler: ErrorHandler = (error, ctx) => { ctx.status(404); } - const requestPath = ctx.req.path; - return ctx.html( art(path.resolve(__dirname, '../views/error.art'), { requestPath, message, - errorPath: ctx.req.path, + errorRoute: hasMatchedRoute ? matchedRoute : requestPath, nodeVersion: process.version, gitHash, }) diff --git a/lib/middleware/debug.ts b/lib/middleware/debug.ts index 7a1226be41..c9fc8799f5 100644 --- a/lib/middleware/debug.ts +++ b/lib/middleware/debug.ts @@ -4,6 +4,11 @@ import { getDebugInfo, setDebugInfo } from '@/utils/debug-info'; const middleware: MiddlewareHandler = async (ctx, next) => { { const debug = getDebugInfo(); + if (!debug.paths[ctx.req.path]) { + debug.paths[ctx.req.path] = 0; + } + debug.paths[ctx.req.path]++; + debug.request++; setDebugInfo(debug); } @@ -12,6 +17,12 @@ const middleware: MiddlewareHandler = async (ctx, next) => { { const debug = getDebugInfo(); + const hasMatchedRoute = ctx.req.routePath !== '/*'; + if (!debug.routes[ctx.req.routePath] && hasMatchedRoute) { + debug.routes[ctx.req.routePath] = 0; + } + hasMatchedRoute && debug.routes[ctx.req.routePath]++; + if (ctx.res.headers.get('RSSHub-Cache-Status')) { debug.hitCache++; } diff --git a/lib/utils/debug-info.ts b/lib/utils/debug-info.ts index e1382068a9..e8699a6176 100644 --- a/lib/utils/debug-info.ts +++ b/lib/utils/debug-info.ts @@ -1,8 +1,23 @@ -const debug = { +type DebugInfo = { + hitCache: number; + request: number; + etag: number; + error: number; + paths: Record; + routes: Record; + errorPaths: Record; + errorRoutes: Record; +}; + +const debug: DebugInfo = { hitCache: 0, request: 0, etag: 0, error: 0, + routes: {}, + paths: {}, + errorRoutes: {}, + errorPaths: {}, }; export const getDebugInfo = () => debug; diff --git a/lib/utils/git-hash.ts b/lib/utils/git-hash.ts index 60d2b4b3bf..6c60600064 100644 --- a/lib/utils/git-hash.ts +++ b/lib/utils/git-hash.ts @@ -1,7 +1,7 @@ import gitRevSync from 'git-rev-sync'; let gitHash = process.env.HEROKU_SLUG_COMMIT?.slice(0, 8) || process.env.VERCEL_GIT_COMMIT_SHA?.slice(0, 8); -let gitDate; +let gitDate: Date | undefined; if (!gitHash) { try { gitHash = gitRevSync.short(undefined, 8); diff --git a/lib/views/error.art b/lib/views/error.art index e128d63fcd..3a98d2d86d 100644 --- a/lib/views/error.art +++ b/lib/views/error.art @@ -41,13 +41,13 @@

Looks like something went wrong

-
Route requested: {{@ requestPath }}
+
Path requested: {{@ requestPath }}
Error message: {{@ message }}
 Helpful Information to provide when opening issue:
-Path: {{@ errorPath }}
-Node version: {{@ nodeVersion}}
-Git Hash: {{@ gitHash}}
+Route: {{@ errorRoute }}
+Node version: {{@ nodeVersion }}
+Git Hash: {{@ gitHash }}
         
 If you believe this is an error caused by RSSHub, please report on github
diff --git a/lib/views/index.tsx b/lib/views/index.tsx
index 2de58d71de..1a04ab81a7 100644
--- a/lib/views/index.tsx
+++ b/lib/views/index.tsx
@@ -11,6 +11,19 @@ const Layout: FC = (props) => (
         
             Welcome to RSSHub!
             
+            
         
         {props.children}
     
@@ -20,7 +33,7 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {
     const debug = getDebugInfo();
 
     const showDebug = !config.debugInfo || config.debugInfo === 'false' ? false : config.debugInfo === 'true' || config.debugInfo === debugQuery;
-    const { disallowRobot, nodeName } = config;
+    const { disallowRobot, nodeName, cache } = config;
 
     const duration = Date.now() - startTime;
 
@@ -57,8 +70,8 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {
                   ]
                 : []),
             {
-                name: 'Cache Length',
-                value: config.cache.routeExpire + 's',
+                name: 'Cache Duration',
+                value: cache.routeExpire + 's',
             },
             {
                 name: 'Request Amount',
@@ -81,9 +94,57 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {
                 value: debug.request ? ((1 - debug.error / debug.request) * 100).toFixed(2) + '%' : 0,
             },
             {
-                name: 'Run Time',
+                name: 'Uptime',
                 value: (duration / 3_600_000).toFixed(2) + ' hour(s)',
             },
+            {
+                name: 'Hot Routes',
+                value: Object.keys(debug.routes)
+                    .sort((a, b) => debug.routes[b] - debug.routes[a])
+                    .slice(0, 30)
+                    .map((route) => (
+                        <>
+                            {debug.routes[route]} {route}
+                            
+ + )), + }, + { + name: 'Hot Paths', + value: Object.keys(debug.paths) + .sort((a, b) => debug.paths[b] - debug.paths[a]) + .slice(0, 30) + .map((path) => ( + <> + {debug.paths[path]} {path} +
+ + )), + }, + { + name: 'Hot Error Routes', + value: Object.keys(debug.errorRoutes) + .sort((a, b) => debug.errorRoutes[b] - debug.errorRoutes[a]) + .slice(0, 30) + .map((route) => ( + <> + {debug.routes[route]} {route} +
+ + )), + }, + { + name: 'Hot Error Paths', + value: Object.keys(debug.errorPaths) + .sort((a, b) => debug.errorPaths[b] - debug.errorPaths[a]) + .slice(0, 30) + .map((path) => ( + <> + {debug.errorPaths[path]} {path} +
+ + )), + }, ], }; @@ -105,22 +166,22 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {

Everything is RSSible

{info.showDebug ? ( -
+
Debug Info {info.debug.map((item) => (
- {item.name}: - {item.value} + {item.name}: + {item.value}
))}
@@ -142,7 +203,11 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {

- Please consider sponsoring to help keep this open source project alive. + Please consider{' '} + + sponsoring + {' '} + to help keep this open source project alive.

Made with ❤️ by{' '}