@samitouri / QOSami-HFS / commits / 9c5b4256

log speed #886

Massimo Melina committed Feb 10, 2025 at 14:09 UTC 9c5b42562dfc47d21e4325b95c9f7468f67ce36a
3 files changed +15 -8
admin/src/LogsPage.ts
+4 -4
@@ -6,7 +6,7 @@ import { API_URL, apiCall, useApi, useApiList } from './api'
6 import { DataTable, DataTableProps } from './DataTable'
7 import {
8 CFG, Dict, formatBytes, HTTP_UNAUTHORIZED, newDialog, prefix, shortenAgent, splitAt, tryJson, md,
9 - typedKeys, NBSP, _dbg, mapFilter, safeDecodeURIComponent, stringAfter, onlyTruthy, formatTimestamp
9 + typedKeys, _dbg, mapFilter, safeDecodeURIComponent, stringAfter, onlyTruthy, formatTimestamp, formatSpeed
10 } from '@hfs/shared'
11 import {
12 NetmaskField, Flex, IconBtn, useBreakpoint, usePauseButton, useToggleButton, WildcardsSupported, Country,
@@ -283,7 +283,7 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
283 {
284 field: 'notes',
285 headerName: "Notes",
286 - width: 105, // https://github.com/rejetto/hfs/discussions/388
286 + width: 110,
287 hideUnder: 'sm',
288 cellClassName: 'wrap',
289 renderCell: ({ value }) => value && md(value),
@@ -323,8 +323,8 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
323 const upload = row.method === 'PUT' || extra?.ul
324 if (upload)
325 row.length = extra?.size
326 - row.notes = extra?.dl ? "fully downloaded" // 'dl' here is not the '?dl' of the url, and has a different meaning
327 - : upload ? `${partial ? "partial " : ""} upload`
326 + row.notes = extra?.dl ? "full download " + (extra.speed ? formatSpeed(extra.speed, { sep: ' ' }) : '') // 'dl' here is not the '?dl' of the url, and has a different meaning
327 + : upload ? `${partial ? "partial " : ""} upload ${extra.speed ? formatSpeed(extra.speed, { sep: ' ' }) : 0}`
328 : row.status === HTTP_UNAUTHORIZED && row.uri?.startsWith(API_URL + 'loginSrp') ? "login failed" + prefix(':\n', extra?.u)
329 : _.map(extra?.params, (v, k) => `${k}: ${v}\n`).join('') + (row.notes || '')
330 }
src/cross.ts
+1 -1
@@ -103,7 +103,7 @@ export function formatBytes(n: number, { post='B', k=0, digits=NaN, sep=' ' }={}
103 return nAsString + sep + (MULTIPLIERS[i]||'') + post
104 } // formatBytes
105
106 -export function formatSpeed(n: number, options: { digits?: number }={}) {
106 +export function formatSpeed(n: number, options: Parameters<typeof formatBytes>[1]={}) {
107 return formatBytes(n, { post: 'B/s', ...options })
108 }
109
src/log.ts
+10 -3
@@ -71,7 +71,7 @@ const logSpam = defineConfig(CFG.log_spam, false)
71 const debounce = _.debounce(cb => cb(), 1000) // with this technique, i'll be able to debounce some code respecting the references in its closure
72
73 export const logMw: Koa.Middleware = async (ctx, next) => {
74 - const now = new Date()
74 + const now = new Date() // request start
75 // do it now so it's available for returning plugins
76 ctx.state.completed = Promise.race([ once(ctx.res, 'finish'), once(ctx.res, 'close') ])
77 await next()
@@ -118,8 +118,15 @@ export const logMw: Koa.Middleware = async (ctx, next) => {
118 const user = getCurrentUsername(ctx)
119 const length = ctx.state.length ?? ctx.length
120 const uri = ctx.originalUrl
121 - ctx.logExtra(ctx.state.includesLastByte && ctx.vfsNode && ctx.res.finished && { dl: 1 }
122 - || ctx.state.uploadPath && { size: ctx.state.opTotal, ul: ctx.state.uploads })
121 + const duration = (Date.now() - Number(now)) / 1000
122 + ctx.logExtra(ctx.vfsNode && {
123 + speed: Math.round(length / duration),
124 + ...ctx.state.includesLastByte && ctx.res.finished && { dl: 1 }
125 + } || ctx.state.uploadPath && {
126 + ul: ctx.state.uploads,
127 + size: ctx.state.opTotal,
128 + speed: Math.round((ctx.state.opTotal! - (ctx.state.opOffset || 0)) / duration)
129 + })
130 if (conn?.country)
131 ctx.logExtra({ country: conn.country })
132 if (logUA.get())