mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-08-30 16:55:08 +08:00
feat(index): add back hot stats (#14664)
This commit is contained in:
@@ -52,16 +52,16 @@ describe('route throws an error', () => {
|
||||
expect(value).toBe('7');
|
||||
break;
|
||||
case 'Hot Routes:':
|
||||
expect(value).toBe('4 /test/:id<br>');
|
||||
expect(value).toBe('4 /test/:id<br>');
|
||||
break;
|
||||
case 'Hot Paths:':
|
||||
expect(value).toBe('2 /test/error<br>2 /test/slow<br>1 /test/httperror<br>1 /thisDoesNotExist<br>1 /<br>');
|
||||
expect(value).toBe('2 /test/error<br>2 /test/slow<br>1 /test/httperror<br>1 /thisDoesNotExist<br>1 /<br>');
|
||||
break;
|
||||
case 'Hot Error Routes:':
|
||||
expect(value).toBe('3 /test/:id<br>');
|
||||
expect(value).toBe('4 /test/:id<br>');
|
||||
break;
|
||||
case 'Hot Error Paths:':
|
||||
expect(value).toBe('2 /test/error<br>1 /test/httperror<br>1 /test/slow<br>1 /thisDoesNotExist<br>');
|
||||
expect(value).toBe('2 /test/error<br>1 /test/httperror<br>1 /test/slow<br>1 /thisDoesNotExist<br>');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
+16
-5
@@ -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 <a href="https://docs.rsshub.app/install/">host your own RSSHub instance</a> 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,
|
||||
})
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
+16
-1
@@ -1,8 +1,23 @@
|
||||
const debug = {
|
||||
type DebugInfo = {
|
||||
hitCache: number;
|
||||
request: number;
|
||||
etag: number;
|
||||
error: number;
|
||||
paths: Record<string, number>;
|
||||
routes: Record<string, number>;
|
||||
errorPaths: Record<string, number>;
|
||||
errorRoutes: Record<string, number>;
|
||||
};
|
||||
|
||||
const debug: DebugInfo = {
|
||||
hitCache: 0,
|
||||
request: 0,
|
||||
etag: 0,
|
||||
error: 0,
|
||||
routes: {},
|
||||
paths: {},
|
||||
errorRoutes: {},
|
||||
errorPaths: {},
|
||||
};
|
||||
|
||||
export const getDebugInfo = () => debug;
|
||||
|
||||
@@ -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);
|
||||
|
||||
+4
-4
@@ -41,13 +41,13 @@
|
||||
|
||||
<h1>Looks like something went wrong</h1>
|
||||
|
||||
<pre class="message">Route requested: {{@ requestPath }}</pre>
|
||||
<pre class="message">Path requested: {{@ requestPath }}</pre>
|
||||
<pre class="message">Error message: {{@ message }}</pre>
|
||||
<pre class="message">
|
||||
Helpful Information to provide when opening issue:
|
||||
Path: {{@ errorPath }}
|
||||
Node version: {{@ nodeVersion}}
|
||||
Git Hash: {{@ gitHash}}
|
||||
Route: {{@ errorRoute }}
|
||||
Node version: {{@ nodeVersion }}
|
||||
Git Hash: {{@ gitHash }}
|
||||
</pre>
|
||||
<pre class="message">
|
||||
If you believe this is an error caused by RSSHub, please <a href="https://github.com/DIYgod/RSSHub/issues/new?assignees=&labels=RSS+bug&template=bug_report_en.yml" target="_blank">report on github</a>
|
||||
|
||||
+76
-11
@@ -11,6 +11,19 @@ const Layout: FC = (props) => (
|
||||
<head>
|
||||
<title>Welcome to RSSHub!</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
{`
|
||||
details::-webkit-scrollbar {
|
||||
width: 0.25rem;
|
||||
}
|
||||
details::-webkit-scrollbar-thumb {
|
||||
border-radius: 0.125rem;
|
||||
background-color: #e4e4e7;
|
||||
}
|
||||
details::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #a1a1aa;
|
||||
}`}
|
||||
</style>
|
||||
</head>
|
||||
<body className="antialiased text-zinc-700">{props.children}</body>
|
||||
</html>
|
||||
@@ -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}
|
||||
<br />
|
||||
</>
|
||||
)),
|
||||
},
|
||||
{
|
||||
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}
|
||||
<br />
|
||||
</>
|
||||
)),
|
||||
},
|
||||
{
|
||||
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}
|
||||
<br />
|
||||
</>
|
||||
)),
|
||||
},
|
||||
{
|
||||
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}
|
||||
<br />
|
||||
</>
|
||||
)),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -105,22 +166,22 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {
|
||||
<p className="text-xl font-medium text-zinc-600">Everything is RSSible</p>
|
||||
<div className="font-bold space-x-4 text-sm">
|
||||
<a target="_blank" href="https://docs.rsshub.app">
|
||||
<button className="text-white bg-[#F5712C] py-2 px-4 rounded-full">View Docs</button>
|
||||
<button className="text-white bg-[#F5712C] hover:bg-[#DD4A15] py-2 px-4 rounded-full">View Docs</button>
|
||||
</a>
|
||||
<a target="_blank" href="https://github.com/DIYgod/RSSHub">
|
||||
<button className="bg-zinc-200 py-2 px-4 rounded-full">View on GitHub</button>
|
||||
<button className="bg-zinc-200 hover:bg-zinc-300 py-2 px-4 rounded-full">View on GitHub</button>
|
||||
</a>
|
||||
<a target="_blank" href="https://docs.rsshub.app/support" className="text-[#F5712C]">
|
||||
<button className="text-white bg-red-500 py-2 px-4 rounded-full">❤️ Sponsor</button>
|
||||
<button className="text-white bg-red-500 hover:bg-red-600 py-2 px-4 rounded-full">❤️ Sponsor</button>
|
||||
</a>
|
||||
</div>
|
||||
{info.showDebug ? (
|
||||
<details className="text-xs w-96 !mt-8">
|
||||
<details className="text-xs w-96 !mt-8 max-h-[400px] overflow-auto">
|
||||
<summary className="text-sm cursor-pointer">Debug Info</summary>
|
||||
{info.debug.map((item) => (
|
||||
<div class="debug-item my-3 pl-8">
|
||||
<span class="debug-key">{item.name}: </span>
|
||||
<span class="debug-value">{item.value}</span>
|
||||
<span class="debug-key w-32 text-right inline-block mr-2">{item.name}: </span>
|
||||
<span class="debug-value inline-block break-all align-top">{item.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</details>
|
||||
@@ -142,7 +203,11 @@ const Index: FC<{ debugQuery: string | undefined }> = ({ debugQuery }) => {
|
||||
</a>
|
||||
</p>
|
||||
<p className="!mt-6">
|
||||
Please consider <a target="_blank" href="https://docs.rsshub.app/support" className="text-[#F5712C]">sponsoring</a> to help keep this open source project alive.
|
||||
Please consider{' '}
|
||||
<a target="_blank" href="https://docs.rsshub.app/support" className="text-[#F5712C]">
|
||||
sponsoring
|
||||
</a>{' '}
|
||||
to help keep this open source project alive.
|
||||
</p>
|
||||
<p>
|
||||
Made with ❤️ by{' '}
|
||||
|
||||
Reference in New Issue
Block a user