mirror of
https://github.com/HeyPuter/puter.git
synced 2026-09-17 10:45:44 +00:00
cleanup: remove dead cloudwatch call (#2155)
This commit is contained in:
@@ -9,6 +9,7 @@ import controlStructureSpacing from './eslint/control-structure-spacing.js';
|
||||
import spaceUnaryOpsWithException from './eslint/space-unary-ops-with-exception.js';
|
||||
|
||||
export const rules = {
|
||||
'no-invalid-this': 'error',
|
||||
'no-unused-vars': ['error', {
|
||||
vars: 'all',
|
||||
args: 'after-used',
|
||||
|
||||
@@ -25,6 +25,7 @@ const mandatoryRules = {
|
||||
'no-use-before-define': ['error', {
|
||||
'functions': false,
|
||||
}],
|
||||
'no-invalid-this': 'error',
|
||||
};
|
||||
|
||||
export default defineConfig([
|
||||
|
||||
@@ -358,9 +358,6 @@ const install = async ({ context, services, app, useapi, modapi }) => {
|
||||
const { SNSService } = require('./services/SNSService');
|
||||
services.registerService('sns', SNSService);
|
||||
|
||||
const { PerformanceMonitor } = require('./monitor/PerformanceMonitor');
|
||||
services.registerService('performance-monitor', PerformanceMonitor);
|
||||
|
||||
const { WispService } = require('./services/WispService');
|
||||
services.registerService('wisp', WispService);
|
||||
// const { AWSSecretsPopulator } = require('./services/AWSSecretsPopulator.js');
|
||||
|
||||
@@ -42,7 +42,8 @@ class CodeUtil {
|
||||
|
||||
return async function (...a) {
|
||||
const op = new cls();
|
||||
op.self = this;
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
op.self = this; // TODO: fix this odd structure, what is this even bound to ?
|
||||
return await op.run(...a);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -360,7 +360,8 @@ class Sequence {
|
||||
if ( opt_values && opt_values instanceof Sequence.SequenceState ) {
|
||||
opt_values = opt_values.scope_;
|
||||
}
|
||||
const state = new Sequence.SequenceState(sequence, this);
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
const state = new Sequence.SequenceState(sequence, this); // TODO: fix this odd structure, what is this even bound to ?
|
||||
await state.run(opt_values ?? undefined);
|
||||
return state.last_return_;
|
||||
};
|
||||
|
||||
@@ -243,8 +243,6 @@ class FilesystemService extends BaseService {
|
||||
}
|
||||
|
||||
async update_child_paths (old_path, new_path, user_id) {
|
||||
const svc_performanceMonitor = this.services.get('performance-monitor');
|
||||
const monitor = svc_performanceMonitor.createContext('update_child_paths');
|
||||
|
||||
if ( ! old_path.endsWith('/') ) old_path += '/';
|
||||
if ( ! new_path.endsWith('/') ) new_path += '/';
|
||||
@@ -255,7 +253,6 @@ class FilesystemService extends BaseService {
|
||||
const log = this.services.get('log-service').create('update_child_paths');
|
||||
log.debug(`updated ${old_path} -> ${new_path}`);
|
||||
|
||||
monitor.end();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,10 +24,10 @@ const { ManagedError } = require('./util/errorutil.js');
|
||||
const { spanify } = require('./util/otelutil.js');
|
||||
const APIError = require('./api/APIError.js');
|
||||
const { DB_READ, DB_WRITE } = require('./services/database/consts.js');
|
||||
const { BaseDatabaseAccessService } = require('./services/database/BaseDatabaseAccessService.js');
|
||||
const { Context } = require('./util/context');
|
||||
const { NodeUIDSelector } = require('./filesystem/node/selectors');
|
||||
const { object_returned_by_get_app } = require('./annotatedobjects.js');
|
||||
const { kv } = require('./util/kvSingleton');
|
||||
|
||||
let services = null;
|
||||
const tmp_provide_services = async ss => {
|
||||
@@ -1314,8 +1314,6 @@ function seconds_to_string (seconds) {
|
||||
* @param {*} options
|
||||
*/
|
||||
async function suggest_app_for_fsentry (fsentry, options) {
|
||||
const svc_performanceMonitor = services.get('performance-monitor');
|
||||
const monitor = svc_performanceMonitor.createContext('suggest_app_for_fsentry');
|
||||
const suggested_apps_promises = [];
|
||||
|
||||
let content_type = mime.contentType(fsentry.name);
|
||||
@@ -1470,7 +1468,6 @@ async function suggest_app_for_fsentry (fsentry, options) {
|
||||
//---------------------------------------------
|
||||
const apps = kv.get(`assocs:${file_extension.slice(1)}:apps`) ?? [];
|
||||
|
||||
monitor.label('third party associations');
|
||||
for ( const app_id of apps ) {
|
||||
suggested_apps_promises.push((async () => {
|
||||
// retrieve app from DB
|
||||
@@ -1484,8 +1481,6 @@ async function suggest_app_for_fsentry (fsentry, options) {
|
||||
}
|
||||
})());
|
||||
}
|
||||
monitor.stamp();
|
||||
monitor.end();
|
||||
|
||||
// return list
|
||||
const suggested_apps = await Promise.all(suggested_apps_promises);
|
||||
|
||||
@@ -480,31 +480,22 @@ class LogService extends BaseService {
|
||||
new winston.transports.DailyRotateFile({
|
||||
filename: `${this.log_directory}/%DATE%.log`,
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: true,
|
||||
maxSize: '20m',
|
||||
|
||||
// TODO: uncomment when we have a log backup strategy
|
||||
// maxFiles: '14d',
|
||||
maxFiles: '14d',
|
||||
}),
|
||||
new winston.transports.DailyRotateFile({
|
||||
level: 'error',
|
||||
filename: `${this.log_directory}/error-%DATE%.log`,
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: true,
|
||||
maxSize: '20m',
|
||||
|
||||
// TODO: uncomment when we have a log backup strategy
|
||||
// maxFiles: '14d',
|
||||
maxFiles: '14d',
|
||||
}),
|
||||
new winston.transports.DailyRotateFile({
|
||||
level: 'system',
|
||||
filename: `${this.log_directory}/system-%DATE%.log`,
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: true,
|
||||
maxSize: '20m',
|
||||
|
||||
// TODO: uncomment when we have a log backup strategy
|
||||
// maxFiles: '14d',
|
||||
maxFiles: '14d',
|
||||
}),
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024-present Puter Technologies Inc.
|
||||
*
|
||||
* This file is part of Puter.
|
||||
*
|
||||
* Puter is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const config = require('../config');
|
||||
const BaseService = require('../services/BaseService');
|
||||
const { CloudWatchClient } = require('@aws-sdk/client-cloudwatch');
|
||||
|
||||
class Metric {
|
||||
constructor (windowSize) {
|
||||
this.count = 0;
|
||||
this.cumulative = 0;
|
||||
this.window = [];
|
||||
|
||||
this.WINDOW_SIZE = windowSize;
|
||||
}
|
||||
|
||||
pushValue (v) {
|
||||
this.window.push(v);
|
||||
this.cumulative += v;
|
||||
this.count++;
|
||||
this.update_();
|
||||
}
|
||||
|
||||
update_ () {
|
||||
while ( this.window.length >= this.WINDOW_SIZE ) {
|
||||
this.window.shift();
|
||||
}
|
||||
|
||||
this.windowAverage = this.window.reduce((sum, v) => sum + v)
|
||||
/ this.window.length;
|
||||
this.cumulativeAverage = this.cumulative / this.count;
|
||||
}
|
||||
|
||||
getCloudwatchMetrics (prefix) {
|
||||
const metrics = [];
|
||||
if ( this.count === 0 ) return [];
|
||||
|
||||
const Timestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
const Dimensions = [
|
||||
{
|
||||
Name: 'server-id',
|
||||
Value: config.server_id || 'unknown',
|
||||
},
|
||||
{
|
||||
Name: 'environment',
|
||||
Value: config.env || 'unknown',
|
||||
},
|
||||
];
|
||||
|
||||
if ( this.cumulativeAverage ) {
|
||||
metrics.push({
|
||||
MetricName: `${prefix }.` + 'cumulative-avg',
|
||||
Value: this.cumulativeAverage,
|
||||
Timestamp,
|
||||
Unit: 'Milliseconds',
|
||||
Dimensions,
|
||||
});
|
||||
}
|
||||
|
||||
if ( this.windowAverage && this.count >= this.WINDOW_SIZE ) {
|
||||
metrics.push({
|
||||
MetricName: `${prefix }.` + 'window-avg',
|
||||
Value: this.windowAverage,
|
||||
Timestamp,
|
||||
Unit: 'Milliseconds',
|
||||
Dimensions,
|
||||
});
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
|
||||
class PerformanceMonitorContext {
|
||||
constructor ({ performanceMonitor, name }) {
|
||||
this.performanceMonitor = performanceMonitor;
|
||||
this.name = name;
|
||||
this.stamps = [];
|
||||
this.children = [];
|
||||
|
||||
this.stamp('monitor-created');
|
||||
}
|
||||
|
||||
branch () {
|
||||
}
|
||||
|
||||
stamp (name) {
|
||||
if ( ! name ) {
|
||||
this.stamps[this.stamps.length - 1].end = Date.now();
|
||||
return;
|
||||
}
|
||||
this.stamps.push({
|
||||
name,
|
||||
ts: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
label (name) {
|
||||
this.stamps.push({
|
||||
name,
|
||||
start: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
end () {
|
||||
this.stamp('end');
|
||||
this.performanceMonitor.logMonitorContext(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PerformanceMonitor extends BaseService {
|
||||
static LOG_DEBUG = true;
|
||||
|
||||
_construct () {
|
||||
this.performanceMetrics = {};
|
||||
|
||||
this.operationCounts = {};
|
||||
this.lastCountPollTS = Date.now();
|
||||
}
|
||||
|
||||
_init () {
|
||||
if ( config.cloudwatch ) {
|
||||
this.cw = new CloudWatchClient(config.cloudwatch);
|
||||
}
|
||||
|
||||
if ( config.monitor ) {
|
||||
this.config = config.monitor;
|
||||
}
|
||||
if ( this.config.metricsInterval > 0 ) {
|
||||
setInterval(async () => {
|
||||
await this.recordMetrics_();
|
||||
}, this.config.metricsInterval);
|
||||
}
|
||||
}
|
||||
|
||||
createContext (name) {
|
||||
return new PerformanceMonitorContext({
|
||||
performanceMonitor: this,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
logMonitorContext (ctx) {
|
||||
if ( ! this.performanceMetrics[ctx.name] ) {
|
||||
this.performanceMetrics[ctx.name] =
|
||||
new Metric(config.windowSize ?? 30);
|
||||
}
|
||||
|
||||
const metricsToUpdate = {};
|
||||
|
||||
// Update averaging metrics
|
||||
{
|
||||
const begin = ctx.stamps[0];
|
||||
for ( const stamp of ctx.stamps ) {
|
||||
let start = stamp.start ?? begin.ts;
|
||||
let end = stamp.end ?? stamp.ts;
|
||||
metricsToUpdate[stamp.name] =
|
||||
(metricsToUpdate[stamp.name] ?? 0) +
|
||||
(end - start);
|
||||
}
|
||||
|
||||
for ( const name in metricsToUpdate ) {
|
||||
const value = metricsToUpdate[name];
|
||||
this.updateMetric_(`${ctx.name}.${name}`, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Update operation counts
|
||||
{
|
||||
if ( ! this.operationCounts[ctx.name] ) {
|
||||
this.operationCounts[ctx.name] = 0;
|
||||
}
|
||||
this.operationCounts[ctx.name]++;
|
||||
}
|
||||
|
||||
if ( ! config.performance_monitors_stdout ) return;
|
||||
|
||||
// Write to stout
|
||||
{
|
||||
console.log('[Monitor Snapshot]', ctx.name);
|
||||
const begin = ctx.stamps[0];
|
||||
for ( const stamp of ctx.stamps ) {
|
||||
let start = stamp.start ?? begin.ts;
|
||||
let end = stamp.end ?? stamp.ts;
|
||||
console.log('|', stamp.name, `${end - start }ms`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateMetric_ (key, value) {
|
||||
const metric = this.performanceMetrics[key] ??
|
||||
(this.performanceMetrics[key] = new Metric(30));
|
||||
metric.pushValue(value);
|
||||
}
|
||||
|
||||
async recordMetrics_ () {
|
||||
this.log.info('recording metrics');
|
||||
// Only record metrics of CloudWatch is enabled
|
||||
if ( ! this.cw ) return;
|
||||
|
||||
const MetricData = [];
|
||||
|
||||
for ( let key in this.performanceMetrics ) {
|
||||
const prefix = key.replace(/\s+/g, '-');
|
||||
const metric = this.performanceMetrics[key];
|
||||
|
||||
MetricData.push(...metric.getCloudwatchMetrics(prefix));
|
||||
}
|
||||
|
||||
const Dimensions = [
|
||||
{
|
||||
Name: 'server-id',
|
||||
Value: config.server_id || 'unknown',
|
||||
},
|
||||
{
|
||||
Name: 'environment',
|
||||
Value: config.env || 'unknown',
|
||||
},
|
||||
];
|
||||
|
||||
const ts = Date.now();
|
||||
const periodInSeconds = (ts - this.lastCountPollTS) / 1000;
|
||||
for ( let key in this.operationCounts ) {
|
||||
const value = this.operationCounts[key] / periodInSeconds;
|
||||
if ( Number.isNaN(value) ) continue;
|
||||
const prefix = key.replace(/\s+/g, '-');
|
||||
MetricData.push({
|
||||
MetricName: `${prefix }.operations`,
|
||||
Unit: 'Count/Second',
|
||||
Value: value,
|
||||
Dimensions,
|
||||
});
|
||||
this.operationCounts[key] = 0;
|
||||
}
|
||||
this.lastCountPollTS = ts;
|
||||
|
||||
if ( MetricData.length === 0 ) {
|
||||
this.log.info('no metrics to record');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
Namespace: 'heyputer',
|
||||
MetricData,
|
||||
};
|
||||
|
||||
// console.log(
|
||||
// JSON.stringify(params, null, ' ')
|
||||
// );
|
||||
|
||||
try {
|
||||
await this.cw.putMetricData(params).promise();
|
||||
} catch (e) {
|
||||
// TODO: alarm condition
|
||||
console.error('Failed to send metrics to CloudWatch',
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
PerformanceMonitor,
|
||||
};
|
||||
Reference in New Issue
Block a user