Skip to content

Commit ce0eaa9

Browse files
authored
Add metrics for static analysis, remove traces and only instrument no… (#207)
1 parent 1a38730 commit ce0eaa9

File tree

4 files changed

+96
-28
lines changed

4 files changed

+96
-28
lines changed

src/services/cfnLint/CfnLintService.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { performance } from 'perf_hooks';
12
import { DateTime } from 'luxon';
23
import { Diagnostic, WorkspaceFolder } from 'vscode-languageserver';
34
import { URI } from 'vscode-uri';
@@ -9,10 +10,11 @@ import { SettingsConfigurable, ISettingsSubscriber, SettingsSubscription } from
910
import { DefaultSettings, CfnLintSettings } from '../../settings/Settings';
1011
import { LoggerFactory } from '../../telemetry/LoggerFactory';
1112
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
12-
import { Telemetry } from '../../telemetry/TelemetryDecorator';
13+
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
1314
import { Closeable } from '../../utils/Closeable';
1415
import { Delayer } from '../../utils/Delayer';
1516
import { extractErrorMessage } from '../../utils/Errors';
17+
import { byteSize } from '../../utils/String';
1618
import { DiagnosticCoordinator } from '../DiagnosticCoordinator';
1719
import { PyodideWorkerManager } from './PyodideWorkerManager';
1820

@@ -261,7 +263,9 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
261263
* @param uri The document URI
262264
* @param fileType The CloudFormation file type
263265
*/
266+
@Count({ name: 'lint.standaloneFile' })
264267
private async lintStandaloneFile(content: string, uri: string, fileType: CloudFormationFileType): Promise<void> {
268+
const startTime = performance.now();
265269
try {
266270
// Use worker to lint template
267271
const diagnosticPayloads = await this.workerManager.lintTemplate(content, uri, fileType);
@@ -284,6 +288,14 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
284288
const errorMessage = extractErrorMessage(error);
285289
this.logError(`linting ${fileType} by string`, error);
286290
this.publishErrorDiagnostics(uri, errorMessage);
291+
} finally {
292+
this.telemetry.histogram(
293+
'lint.standaloneFile.duration',
294+
(performance.now() - startTime) / byteSize(content),
295+
{
296+
unit: 'ms/byte',
297+
},
298+
);
287299
}
288300
}
289301

@@ -313,12 +325,14 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
313325
* @param fileType The CloudFormation file type
314326
* @param content The document content (used for GitSync deployment files)
315327
*/
328+
@Count({ name: 'lint.workspaceFile' })
316329
private async lintWorkspaceFile(
317330
uri: string,
318331
folder: WorkspaceFolder,
319332
fileType: CloudFormationFileType,
320333
content: string,
321334
): Promise<void> {
335+
const startTime = performance.now();
322336
try {
323337
// Ensure folder is mounted before linting
324338
await this.mountFolder(folder);
@@ -358,6 +372,14 @@ export class CfnLintService implements SettingsConfigurable, Closeable {
358372
const errorMessage = extractErrorMessage(error);
359373
this.logError(`linting ${fileType} by file`, error);
360374
this.publishErrorDiagnostics(uri, errorMessage);
375+
} finally {
376+
this.telemetry.histogram(
377+
'lint.workspaceFile.duration',
378+
(performance.now() - startTime) / byteSize(content),
379+
{
380+
unit: 'ms/byte',
381+
},
382+
);
361383
}
362384
}
363385

src/services/guard/GuardService.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { readFile } from 'fs/promises';
2+
import { performance } from 'perf_hooks';
23
import { Diagnostic, DiagnosticSeverity, Range } from 'vscode-languageserver';
34
import { SyntaxTreeManager } from '../../context/syntaxtree/SyntaxTreeManager';
45
import { CloudFormationFileType } from '../../document/Document';
@@ -8,10 +9,11 @@ import { SettingsConfigurable, ISettingsSubscriber, SettingsSubscription } from
89
import { DefaultSettings, GuardSettings } from '../../settings/Settings';
910
import { LoggerFactory } from '../../telemetry/LoggerFactory';
1011
import { ScopedTelemetry } from '../../telemetry/ScopedTelemetry';
11-
import { Telemetry } from '../../telemetry/TelemetryDecorator';
12+
import { Count, Telemetry } from '../../telemetry/TelemetryDecorator';
1213
import { Closeable } from '../../utils/Closeable';
1314
import { Delayer } from '../../utils/Delayer';
1415
import { extractErrorMessage } from '../../utils/Errors';
16+
import { byteSize } from '../../utils/String';
1517
import { DiagnosticCoordinator } from '../DiagnosticCoordinator';
1618
import { getRulesForPack, getAvailableRulePacks, GuardRuleData } from './GeneratedGuardRules';
1719
import { GuardEngine, GuardViolation, GuardRule } from './GuardEngine';
@@ -181,6 +183,7 @@ export class GuardService implements SettingsConfigurable, Closeable {
181183
* @param uri The document URI
182184
* @param forceUseContent If true, always use the provided content (for consistency with CfnLintService)
183185
*/
186+
@Count({ name: 'validate' })
184187
async validate(content: string, uri: string, _forceUseContent?: boolean): Promise<void> {
185188
const fileType = this.documentManager.get(uri)?.cfnFileType;
186189

@@ -200,6 +203,7 @@ export class GuardService implements SettingsConfigurable, Closeable {
200203

201204
this.telemetry.count(`validate.file.${CloudFormationFileType.Template}`, 1);
202205

206+
const startTime = performance.now();
203207
try {
204208
// Ensure Guard service is initialized
205209
if (!this.guardEngine.isReady()) {
@@ -240,6 +244,10 @@ export class GuardService implements SettingsConfigurable, Closeable {
240244

241245
// For other errors (WASM issues, timeouts, etc.), log as error and show diagnostic
242246
this.publishErrorDiagnostics(uri, errorMessage);
247+
} finally {
248+
this.telemetry.histogram('validate.duration', (performance.now() - startTime) / byteSize(content), {
249+
unit: 'ms/byte',
250+
});
243251
}
244252
}
245253

src/telemetry/OTELInstrumentation.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { arch, platform, type, release, machine } from 'os';
22
import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
33
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
44
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
5-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
65
import { resourceFromAttributes } from '@opentelemetry/resources';
76
import {
87
AggregationTemporality,
@@ -12,7 +11,7 @@ import {
1211
} from '@opentelemetry/sdk-metrics';
1312
import { NodeSDK } from '@opentelemetry/sdk-node';
1413
import { ClientInfo } from '../server/InitParams';
15-
import { isBeta, isAlpha, isProd, isTest, IsAlphaApp } from '../utils/Environment';
14+
import { isBeta, isAlpha, isProd, isTest } from '../utils/Environment';
1615
import { ExtensionId, ExtensionVersion } from '../utils/ExtensionConfig';
1716

1817
const ExportIntervalSeconds = 30;
@@ -29,15 +28,6 @@ export function otelSdk(clientId: string, client?: ClientInfo) {
2928
exportIntervalMillis: ExportIntervalSeconds * 1000,
3029
});
3130

32-
let traceExporter: OTLPTraceExporter | undefined;
33-
34-
// Only enable in alpha environment (excluding test env)
35-
if (IsAlphaApp) {
36-
traceExporter = new OTLPTraceExporter({
37-
url: `${telemetryUrl}/v1/traces`,
38-
});
39-
}
40-
4131
const sdk = new NodeSDK({
4232
resource: resourceFromAttributes({
4333
['service']: `${ExtensionId}-${ExtensionVersion}`,
@@ -50,7 +40,6 @@ export function otelSdk(clientId: string, client?: ClientInfo) {
5040
}),
5141
resourceDetectors: [],
5242
metricReader: metricsReader,
53-
traceExporter: traceExporter,
5443
views: [
5544
{
5645
instrumentName: '*.duration',
@@ -73,13 +62,50 @@ export function otelSdk(clientId: string, client?: ClientInfo) {
7362
],
7463
instrumentations: [
7564
getNodeAutoInstrumentations({
76-
'@opentelemetry/instrumentation-pino': {
77-
enabled: false,
78-
},
79-
'@opentelemetry/instrumentation-http': {
80-
enabled: false,
65+
'@opentelemetry/instrumentation-amqplib': { enabled: false },
66+
'@opentelemetry/instrumentation-aws-lambda': { enabled: false },
67+
'@opentelemetry/instrumentation-bunyan': { enabled: false },
68+
'@opentelemetry/instrumentation-cassandra-driver': { enabled: false },
69+
'@opentelemetry/instrumentation-connect': { enabled: false },
70+
'@opentelemetry/instrumentation-cucumber': { enabled: false },
71+
'@opentelemetry/instrumentation-dataloader': { enabled: false },
72+
'@opentelemetry/instrumentation-dns': { enabled: false },
73+
'@opentelemetry/instrumentation-express': { enabled: false },
74+
'@opentelemetry/instrumentation-fastify': { enabled: false },
75+
'@opentelemetry/instrumentation-fs': { enabled: false },
76+
'@opentelemetry/instrumentation-generic-pool': { enabled: false },
77+
'@opentelemetry/instrumentation-graphql': { enabled: false },
78+
'@opentelemetry/instrumentation-grpc': { enabled: false },
79+
'@opentelemetry/instrumentation-hapi': { enabled: false },
80+
'@opentelemetry/instrumentation-http': { enabled: false },
81+
'@opentelemetry/instrumentation-ioredis': { enabled: false },
82+
'@opentelemetry/instrumentation-kafkajs': { enabled: false },
83+
'@opentelemetry/instrumentation-knex': { enabled: false },
84+
'@opentelemetry/instrumentation-koa': { enabled: false },
85+
'@opentelemetry/instrumentation-lru-memoizer': { enabled: false },
86+
'@opentelemetry/instrumentation-memcached': { enabled: false },
87+
'@opentelemetry/instrumentation-mongodb': { enabled: false },
88+
'@opentelemetry/instrumentation-mongoose': { enabled: false },
89+
'@opentelemetry/instrumentation-mysql2': { enabled: false },
90+
'@opentelemetry/instrumentation-mysql': { enabled: false },
91+
'@opentelemetry/instrumentation-nestjs-core': { enabled: false },
92+
'@opentelemetry/instrumentation-net': { enabled: false },
93+
'@opentelemetry/instrumentation-oracledb': { enabled: false },
94+
'@opentelemetry/instrumentation-pg': { enabled: false },
95+
'@opentelemetry/instrumentation-pino': { enabled: false },
96+
'@opentelemetry/instrumentation-redis': { enabled: false },
97+
'@opentelemetry/instrumentation-restify': { enabled: false },
98+
'@opentelemetry/instrumentation-router': { enabled: false },
99+
'@opentelemetry/instrumentation-socket.io': { enabled: false },
100+
'@opentelemetry/instrumentation-tedious': { enabled: false },
101+
'@opentelemetry/instrumentation-undici': { enabled: false },
102+
'@opentelemetry/instrumentation-winston': { enabled: false },
103+
104+
'@opentelemetry/instrumentation-aws-sdk': {
105+
enabled: true,
81106
},
82107
'@opentelemetry/instrumentation-runtime-node': {
108+
enabled: true,
83109
monitoringPrecision: ExportIntervalSeconds * 1000,
84110
},
85111
}),

webpack.config.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar
9797
apply: (compiler) => {
9898
compiler.hooks.beforeRun.tapAsync('InstallDependencies', (compilation, callback) => {
9999
try {
100+
console.log('[InstallDependencies] Starting dependency installation...');
101+
console.log(`[InstallDependencies] Target: ${targetPlatform}-${targetArch}`);
102+
100103
const tmpPkg = {
101104
...Package,
102105
main: `./${BUNDLE_NAME}.js`,
@@ -107,13 +110,15 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar
107110
delete tmpPkg['externalDependencies'];
108111
delete tmpPkg['nativePrebuilds'];
109112

113+
console.log('[InstallDependencies] Cleaning temp directory...');
110114
if (fs.existsSync(tmpDir)) {
111115
fs.rmSync(tmpDir, { recursive: true, force: true });
112116
}
113117
fs.mkdirSync(tmpDir, { recursive: true });
114118
fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify(tmpPkg, null, 2));
115119
fs.copyFileSync('package-lock.json', `${tmpDir}/package-lock.json`);
116120

121+
console.log('[InstallDependencies] Running npm ci --omit=dev');
117122
execSync('npm ci --omit=dev', { cwd: tmpDir, stdio: 'inherit' });
118123
const otherDeps = Object.entries(Package.nativePrebuilds)
119124
.filter(([key, _version]) => {
@@ -124,17 +129,24 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar
124129
})
125130
.join(' ');
126131

132+
console.log(`[InstallDependencies] Installing native prebuilds: ${JSON.stringify(otherDeps)}`);
127133
execSync(`npm install --save-exact --force ${otherDeps}`, { cwd: tmpDir, stdio: 'inherit' });
128134
callback();
129135
} catch (error) {
136+
console.error('[InstallDependencies] Error:', error);
130137
callback(error);
131138
}
132139
});
133140

134141
compiler.hooks.afterEmit.tap('CleanUnusedNativeModules', () => {
142+
console.log('[CleanUnusedNativeModules] Starting cleanup of unused native modules...');
143+
135144
const nodeModulesPath = path.join(outputPath, 'node_modules');
136145

137-
if (!fs.existsSync(nodeModulesPath)) return;
146+
if (!fs.existsSync(nodeModulesPath)) {
147+
console.log('[CleanUnusedNativeModules] No node_modules found, skipping cleanup');
148+
return;
149+
}
138150

139151
function cleanPlatformDirs(dir) {
140152
if (!fs.existsSync(dir)) return;
@@ -148,9 +160,10 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar
148160
const shouldKeep = entry.name.includes(`${targetPlatform}-${targetArch}`);
149161

150162
if (isPlatformDir && !shouldKeep) {
163+
console.log(`[CleanUnusedNativeModules] Deleted: ${entryPath}`);
151164
fs.rmSync(entryPath, { recursive: true, force: true });
152-
console.log(`Deleted: ${entryPath}`);
153165
} else if (entry.name === 'prebuilds') {
166+
console.log(`[CleanUnusedNativeModules] Scanning prebuilds: ${entryPath}`);
154167
cleanPlatformDirs(entryPath);
155168
} else {
156169
cleanPlatformDirs(entryPath);
@@ -159,17 +172,22 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar
159172
}
160173

161174
cleanPlatformDirs(nodeModulesPath);
175+
console.log('[CleanUnusedNativeModules] Cleanup complete');
162176
});
163177

164178
compiler.hooks.done.tap('CleanupTemp', () => {
179+
console.log('[CleanupTemp] Cleaning up temporary files...');
165180
if (fs.existsSync(tmpDir)) {
181+
console.log(`[CleanupTemp] Removing: ${tmpDir}`);
166182
fs.rmSync(tmpDir, { recursive: true, force: true });
167183
}
168184

169185
const dotPackageLock = 'bundle/production/node_modules/.package-lock.json';
170186
if (fs.existsSync(dotPackageLock)) {
187+
console.log(`[CleanupTemp] Removing: ${dotPackageLock}`);
171188
fs.rmSync(dotPackageLock, { force: true });
172189
}
190+
console.log('[CleanupTemp] Cleanup complete');
173191
});
174192
},
175193
});
@@ -260,13 +278,7 @@ const baseConfig = {
260278
},
261279
],
262280
},
263-
stats: {
264-
colors: true,
265-
modules: false,
266-
children: false,
267-
chunks: false,
268-
chunkModules: false,
269-
},
281+
stats: 'normal',
270282
performance: {
271283
hints: 'warning',
272284
},

0 commit comments

Comments
 (0)