nucleus-core-ts 0.9.947 → 0.9.948
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.build-ok
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.948
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ and limitations under the License.
|
|
|
71
71
|
</div>
|
|
72
72
|
</body>
|
|
73
73
|
</html>
|
|
74
|
-
`}getActiveAlerts(){return Array.from(this.state.activeAlerts.values())}acknowledgeAlert(alertId){for(let[_type,alert]of this.state.activeAlerts)if(alert.id===alertId)return alert.acknowledged=!0,!0;return!1}clearAlert(type){this.state.activeAlerts.delete(type)}}var init_AlertService=()=>{};class ApplicationCollector{config;requestCount=0;responseTimes=[];errorCount=0;rateLimitBlocks=0;byEndpoint={};byMethod={};byStatus={};byErrorType={};lastCollectTime=Date.now();buckets=[];constructor(config){this.config=config}bucketFor(second){let index=second%60,bucket=this.buckets[index];if(!bucket)return bucket={second,requests:0,errors:0,blocks:0},this.buckets[index]=bucket,bucket;if(bucket.second!==second)bucket.second=second,bucket.requests=0,bucket.errors=0,bucket.blocks=0;return bucket}windowed(pick){let oldest=Math.floor(Date.now()/1000)-60,total=0;for(let bucket of this.buckets)if(bucket&&bucket.second>oldest)total+=pick(bucket);return total}recordRequest(params){if(!this.config?.enabled)return;this.requestCount++;let bucket=this.bucketFor(Math.floor(Date.now()/1000));if(bucket.requests+=1,params.isError)bucket.errors+=1;if(this.config.metrics?.responseTime!==!1){if(this.responseTimes.push(params.responseTimeMs),this.responseTimes.length>
|
|
74
|
+
`}getActiveAlerts(){return Array.from(this.state.activeAlerts.values())}acknowledgeAlert(alertId){for(let[_type,alert]of this.state.activeAlerts)if(alert.id===alertId)return alert.acknowledged=!0,!0;return!1}clearAlert(type){this.state.activeAlerts.delete(type)}}var init_AlertService=()=>{};class ApplicationCollector{config;requestCount=0;responseTimes=[];errorCount=0;rateLimitBlocks=0;byEndpoint={};byMethod={};byStatus={};byErrorType={};lastCollectTime=Date.now();buckets=[];constructor(config){this.config=config}bucketFor(second){let index=second%60,bucket=this.buckets[index];if(!bucket)return bucket={second,requests:0,errors:0,blocks:0},this.buckets[index]=bucket,bucket;if(bucket.second!==second)bucket.second=second,bucket.requests=0,bucket.errors=0,bucket.blocks=0;return bucket}windowed(pick){let oldest=Math.floor(Date.now()/1000)-60,total=0;for(let bucket of this.buckets)if(bucket&&bucket.second>oldest)total+=pick(bucket);return total}recordRequest(params){if(!this.config?.enabled)return;this.requestCount++;let bucket=this.bucketFor(Math.floor(Date.now()/1000));if(bucket.requests+=1,params.isError)bucket.errors+=1;if(this.config.metrics?.responseTime!==!1){if(this.responseTimes.push({at:Date.now(),ms:params.responseTimeMs}),this.responseTimes.length>50000)this.pruneResponseTimes()}if(this.config.metrics?.requests!==!1)this.byEndpoint[params.endpoint]=(this.byEndpoint[params.endpoint]||0)+1,this.byMethod[params.method]=(this.byMethod[params.method]||0)+1,this.byStatus[String(params.status)]=(this.byStatus[String(params.status)]||0)+1;if(this.config.metrics?.errors!==!1&¶ms.isError){if(this.errorCount++,params.errorType)this.byErrorType[params.errorType]=(this.byErrorType[params.errorType]||0)+1}}recordRateLimitBlock(){if(!this.config?.enabled||this.config.metrics?.rateLimits===!1)return;this.rateLimitBlocks++,this.bucketFor(Math.floor(Date.now()/1000)).blocks+=1}pruneResponseTimes(){let oldest=Date.now()-60000,kept=this.responseTimes.filter((sample)=>sample.at>oldest);if(kept.length>50000)kept=kept.slice(-50000);this.responseTimes=kept}collect(){if(!this.config?.enabled)return null;let perMinute=this.windowed((b)=>b.requests),blockedPerMinute=this.windowed((b)=>b.blocks);this.pruneResponseTimes();let sortedTimes=this.responseTimes.map((s)=>s.ms).sort((a,b)=>a-b),len=sortedTimes.length;return{requests:{total:this.requestCount,perMinute,byEndpoint:{...this.byEndpoint},byMethod:{...this.byMethod},byStatus:{...this.byStatus}},responseTime:{avg:len>0?Math.round(sortedTimes.reduce((a,b)=>a+b,0)/len*100)/100:0,min:len>0?sortedTimes[0]??0:0,max:len>0?sortedTimes[len-1]??0:0,p50:len>0?sortedTimes[Math.floor(len*0.5)]??0:0,p95:len>0?sortedTimes[Math.floor(len*0.95)]??0:0,p99:len>0?sortedTimes[Math.floor(len*0.99)]??0:0},errors:{total:this.errorCount,rate:(()=>{let requests=perMinute;if(requests===0)return 0;let errors=this.windowed((b)=>b.errors);return Math.round(errors/requests*100*100)/100})(),byType:{...this.byErrorType}},rateLimits:{blocked:this.rateLimitBlocks,blockedPerMinute}}}reset(){this.requestCount=0,this.responseTimes=[],this.errorCount=0,this.rateLimitBlocks=0,this.byEndpoint={},this.byMethod={},this.byStatus={},this.byErrorType={},this.buckets=[],this.lastCollectTime=Date.now()}getRequestsPerMinute(){return this.windowed((b)=>b.requests)}getErrorRate(){let requests=this.windowed((b)=>b.requests);return requests>0?this.windowed((b)=>b.errors)/requests*100:0}getRateLimitBlocksPerMinute(){return this.windowed((b)=>b.blocks)}getAvgResponseTime(){if(this.pruneResponseTimes(),this.responseTimes.length===0)return 0;return this.responseTimes.reduce((total,s)=>total+s.ms,0)/this.responseTimes.length}}class DatabaseCollector{config;execute;lastTotals=null;activeTimeSupported=!0;constructor(config,execute){this.config=config,this.execute=execute}async collect(){if(!this.config.enabled)return null;let metrics={connections:{active:0,idle:0,total:0},queries:{total:0,avgTime:0,slowQueries:0}};if(this.config.metrics?.connections!==!1)metrics.connections=await this.collectConnections();if(this.config.metrics?.queryTime!==!1)metrics.queries=await this.collectQueries();return metrics}toNumber(value){let num=Number.parseFloat(String(value??0));return Number.isNaN(num)?0:num}async collectConnections(){try{let row=(await this.execute(`SELECT
|
|
75
75
|
count(*) FILTER (WHERE state = 'active') AS active,
|
|
76
76
|
count(*) FILTER (WHERE state = 'idle') AS idle,
|
|
77
77
|
count(*) AS total
|
|
@@ -2,6 +2,16 @@ import type { ApplicationMetrics, MonitoringConfig } from '../types';
|
|
|
2
2
|
export declare class ApplicationCollector {
|
|
3
3
|
private config;
|
|
4
4
|
private requestCount;
|
|
5
|
+
/**
|
|
6
|
+
* Response times with the moment they happened, so percentiles describe NOW.
|
|
7
|
+
*
|
|
8
|
+
* They used to be a plain list of the last 5–10.000 numbers. On a quiet
|
|
9
|
+
* install that is not a window, it is a memory: run a load test at lunchtime
|
|
10
|
+
* and the p95 on the screen still describes the load test at six in the
|
|
11
|
+
* evening, because nothing has come along to push those samples out. The
|
|
12
|
+
* peak recorder then writes that stale p95 into a peak's context as "what
|
|
13
|
+
* the response time was at the time", which it was not.
|
|
14
|
+
*/
|
|
5
15
|
private responseTimes;
|
|
6
16
|
private errorCount;
|
|
7
17
|
private rateLimitBlocks;
|
|
@@ -47,6 +57,8 @@ export declare class ApplicationCollector {
|
|
|
47
57
|
errorType?: string;
|
|
48
58
|
}): void;
|
|
49
59
|
recordRateLimitBlock(): void;
|
|
60
|
+
/** Drops samples that have fallen out of the window, then out of the cap. */
|
|
61
|
+
private pruneResponseTimes;
|
|
50
62
|
collect(): ApplicationMetrics | null;
|
|
51
63
|
/**
|
|
52
64
|
* Clears the cumulative counters.
|
|
@@ -65,5 +77,6 @@ export declare class ApplicationCollector {
|
|
|
65
77
|
*/
|
|
66
78
|
getErrorRate(): number;
|
|
67
79
|
getRateLimitBlocksPerMinute(): number;
|
|
80
|
+
/** Mean response time over the same minute the other rates describe. */
|
|
68
81
|
getAvgResponseTime(): number;
|
|
69
82
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.948",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|