network-performance-analyzer
Version:
Automated analysis tool for network performance test datasets containing DNS testing results and iperf3 performance measurements
988 lines (782 loc) • 28.8 kB
Markdown
# Extending the Network Performance Analyzer
This document provides comprehensive guidance on how to extend the Network Performance Analyzer with new metrics, analysis capabilities, and report sections. For more detailed guides on specific topics, see:
- [API Documentation](./api.md) - Detailed API reference
- [Custom Plugins Guide](./custom-plugins-guide.md) - Step-by-step guide to creating plugins
## Table of Contents
1. [Configuration System](#configuration-system)
2. [Plugin Architecture](#plugin-architecture)
3. [Creating Custom Plugins](#creating-custom-plugins)
4. [Customizing Report Templates](#customizing-report-templates)
5. [Environment-Specific Configuration](#environment-specific-configuration)
6. [Advanced Customization](#advanced-customization)
7. [Integration with External Systems](#integration-with-external-systems)
## Configuration System
The Network Performance Analyzer uses a flexible configuration system that allows you to customize various aspects of the analysis process.
### Configuration File Structure
The configuration file is a JSON file with the following structure:
```json
{
"analysis": {
"continueOnError": true,
"logProgress": true,
"useParallelProcessing": true,
"maxParallelTasks": 4,
"enablePerformanceMonitoring": false,
"memoryThresholdPercent": 80
},
"anomalyThresholds": {
"bandwidthVariation": 0.2,
"latencyVariation": 0.3,
"packetLossThreshold": 0.05,
"dnsResponseTimeVariation": 0.5,
"cpuUtilizationThreshold": 0.8
},
"reporting": {
"outputDirectory": "./reports",
"defaultFilename": "network-analysis-report.md",
"includeSections": [
"executive-summary",
"configuration-overview",
"detailed-tables",
"visualizations",
"anomalies",
"recommendations"
],
"format": "markdown"
},
"plugins": {
"enabled": ["example-analyzer"],
"config": {
"example-analyzer": {
"bandwidthWeight": 0.4,
"latencyWeight": 0.4,
"packetLossWeight": 0.2
}
}
},
"environments": {
"development": {
"analysis": {
"logProgress": true,
"enablePerformanceMonitoring": true
}
},
"production": {
"analysis": {
"logProgress": false,
"enablePerformanceMonitoring": false
}
}
}
}
```
### Creating and Loading Configuration
You can create a default configuration file using the `ConfigurationManager.createDefaultConfig()` method:
```typescript
import { ConfigurationManager } from './config/ConfigurationManager';
// Create default configuration file
await ConfigurationManager.createDefaultConfig('./config.json');
// Load configuration from file
const configManager = new ConfigurationManager();
configManager.loadFromFile('./config.json');
// Get analyzer configuration
const analyzerConfig = configManager.getAnalyzerConfig();
```
### Customizing Configuration
You can customize the configuration programmatically:
```typescript
// Update configuration
configManager.update({
analysis: {
maxParallelTasks: 8
},
anomalyThresholds: {
bandwidthVariation: 0.1
}
});
// Save updated configuration
await configManager.saveToFile('./custom-config.json');
```
## Plugin Architecture
The Network Performance Analyzer includes a plugin architecture that allows you to extend its functionality with custom analysis capabilities.
### Plugin Types
Plugins can be of different types:
- **analyzer**: Adds new analysis capabilities
- **reporter**: Customizes report generation
- **parser**: Adds support for new data formats
- **validator**: Adds custom validation rules
- **utility**: Provides utility functions
### Plugin Interface
All plugins must implement the `Plugin` interface:
```typescript
interface Plugin {
name: string;
description: string;
version: string;
initialize(config: any): Promise<void>;
execute(context: PluginContext): Promise<any>;
}
```
## Creating Custom Plugins
### Basic Plugin Structure
Here's an example of a basic analyzer plugin:
```typescript
import { Plugin, PluginContext } from './PluginManager';
class CustomAnalyzerPlugin implements Plugin {
name = 'custom-analyzer';
description = 'Custom analyzer plugin';
version = '1.0.0';
private config: any = {};
async initialize(config: any): Promise<void> {
this.config = config || {};
}
async execute(context: PluginContext): Promise<any> {
const { datasets } = context;
// Perform custom analysis
const results = {
customMetric: this.calculateCustomMetric(datasets)
};
return results;
}
private calculateCustomMetric(datasets: any[]): any {
// Custom metric calculation logic
return { /* custom metrics */ };
}
}
// Export plugin metadata
export const type = 'analyzer';
export const author = 'Your Name';
// Export plugin as default
export default new CustomAnalyzerPlugin();
```
### Registering and Using Plugins
To use custom plugins:
1. Place your plugin file in a plugin directory (e.g., `./plugins/`)
2. Register the plugin directory with the PluginManager
3. Enable the plugin in the configuration
```typescript
import { PluginManager } from './plugins/PluginManager';
import { ConfigurationManager } from './config/ConfigurationManager';
// Create configuration manager
const configManager = new ConfigurationManager();
// Create plugin manager
const pluginManager = new PluginManager(configManager);
// Add plugin directory
pluginManager.addPluginDirectory('./plugins');
// Discover plugins
await pluginManager.discoverPlugins();
// Enable a plugin
pluginManager.enablePlugin('custom-analyzer');
// Execute plugins of a specific type
const analyzerResults = await pluginManager.executePlugins('analyzer', {
datasets: /* your datasets */
});
```
## Customizing Report Templates
The Network Performance Analyzer uses a template-based report generation system that allows you to customize the format and content of reports.
### Report Template Structure
A report template consists of multiple sections, each with its own template string:
```typescript
interface ReportTemplate {
name: string;
description: string;
format: 'markdown' | 'html' | 'json';
sections: TemplateSection[];
}
interface TemplateSection {
id: string;
name: string;
template: string;
required: boolean;
order: number;
}
```
### Creating Custom Templates
You can create custom templates by modifying the default template:
```typescript
import { ReportTemplateManager } from './services/ReportTemplateManager';
import { ConfigurationManager } from './config/ConfigurationManager';
// Create configuration manager
const configManager = new ConfigurationManager();
// Create template manager
const templateManager = new ReportTemplateManager(configManager);
// Create custom template
const customTemplate = templateManager.createCustomTemplate({
name: 'Custom Template',
description: 'My custom report template',
sections: [
{
id: 'custom-section',
name: 'Custom Analysis',
template: '## Custom Analysis\n\n{{#each customMetrics}}* {{name}}: {{value}}\n{{/each}}\n',
required: false,
order: 5
}
]
});
// Register custom template
templateManager.registerTemplate('custom', customTemplate);
// Set as active template
templateManager.setActiveTemplate('custom');
// Save template to file
await templateManager.saveTemplateToFile('custom', './templates/custom-template.json');
```
### Template Variables and Helpers
Templates support the following syntax:
- `{{variable}}`: Insert a variable value
- `{{#each array}}...{{/each}}`: Loop through an array
- `{{#if condition}}...{{else}}...{{/if}}`: Conditional content
## Environment-Specific Configuration
The Network Performance Analyzer supports environment-specific configuration, allowing you to have different settings for development, testing, and production environments.
### Setting the Environment
```typescript
// Set environment
configManager.setEnvironment('production');
// Get current environment
const env = configManager.getEnvironment();
```
### Environment-Specific Configuration
Define environment-specific settings in the configuration file:
```json
{
"environments": {
"development": {
"analysis": {
"logProgress": true,
"enablePerformanceMonitoring": true
}
},
"production": {
"analysis": {
"logProgress": false,
"enablePerformanceMonitoring": false
}
}
}
}
```
## Best Practices
1. **Use the Configuration System**: Store all configurable parameters in the configuration system rather than hardcoding them.
2. **Create Modular Plugins**: Design plugins to be modular and focused on a specific task.
3. **Follow the Plugin Interface**: Ensure your plugins implement the Plugin interface correctly.
4. **Handle Errors Gracefully**: Add proper error handling in your plugins to prevent failures from affecting the entire analysis process.
5. **Document Your Extensions**: Provide clear documentation for your custom plugins and templates.
6. **Use Environment-Specific Configuration**: Leverage environment-specific configuration for different deployment scenarios.
7. **Test Your Extensions**: Write unit tests for your custom plugins and templates to ensure they work correctly.
## Example: Adding a New Metric
Here's a complete example of adding a new metric to the analyzer:
1. Create a custom analyzer plugin:
```typescript
// plugins/BandwidthStabilityAnalyzer.ts
import { Plugin, PluginContext } from '../src/plugins/PluginManager';
class BandwidthStabilityAnalyzer implements Plugin {
name = 'bandwidth-stability-analyzer';
description = 'Analyzes bandwidth stability across test runs';
version = '1.0.0';
private config: any = {};
async initialize(config: any): Promise<void> {
this.config = config || {};
}
async execute(context: PluginContext): Promise<any> {
const { datasets } = context;
// Group datasets by configuration
const configGroups = this.groupByConfiguration(datasets);
const results = [];
// Calculate stability for each configuration
for (const [config, configDatasets] of Object.entries(configGroups)) {
const bandwidthValues: number[] = [];
// Extract bandwidth values
for (const dataset of configDatasets) {
if (dataset.results && dataset.results.iperfTests) {
for (const test of dataset.results.iperfTests) {
if (test.bandwidthMbps) {
bandwidthValues.push(test.bandwidthMbps);
}
}
}
}
// Calculate stability metrics
const mean = this.calculateMean(bandwidthValues);
const stdDev = this.calculateStdDev(bandwidthValues, mean);
const stabilityScore = this.calculateStabilityScore(stdDev, mean);
results.push({
configuration: config,
mean,
stdDev,
stabilityScore,
variationCoefficient: stdDev / mean
});
}
return {
bandwidthStability: results.sort((a, b) => b.stabilityScore - a.stabilityScore)
};
}
private groupByConfiguration(datasets: any[]): Record<string, any[]> {
const groups: Record<string, any[]> = {};
for (const dataset of datasets) {
if (dataset.configuration) {
const configKey = `mtu${dataset.configuration.mtu}-logs_${dataset.configuration.awsLogging ? 'enabled' : 'disabled'}`;
if (!groups[configKey]) {
groups[configKey] = [];
}
groups[configKey].push(dataset);
}
}
return groups;
}
private calculateMean(values: number[]): number {
if (values.length === 0) return 0;
return values.reduce((sum, val) => sum + val, 0) / values.length;
}
private calculateStdDev(values: number[], mean: number): number {
if (values.length <= 1) return 0;
const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
return Math.sqrt(variance);
}
private calculateStabilityScore(stdDev: number, mean: number): number {
if (mean === 0) return 0;
const coefficientOfVariation = stdDev / mean;
return Math.max(0, Math.min(1, 1 - (coefficientOfVariation / (this.config.maxVariation || 0.5))));
}
}
export const type = 'analyzer';
export const author = 'Your Name';
export default new BandwidthStabilityAnalyzer();
```
2. Add a custom report section template:
```typescript
// Create custom template with bandwidth stability section
const customTemplate = templateManager.createCustomTemplate({
sections: [
{
id: 'bandwidth-stability',
name: 'Bandwidth Stability Analysis',
template: '## Bandwidth Stability Analysis\n\nThe following table shows bandwidth stability metrics across different configurations:\n\n| Configuration | Stability Score | Mean (Mbps) | Std Dev | Variation Coefficient |\n|--------------|----------------|------------|---------|------------------------|\n{{#each bandwidthStability}}| {{configuration}} | {{stabilityScore}} | {{mean}} | {{stdDev}} | {{variationCoefficient}} |\n{{/each}}\n',
required: false,
order: 4
}
]
});
```
3. Update the configuration to enable the plugin:
```json
{
"plugins": {
"enabled": ["bandwidth-stability-analyzer"],
"config": {
"bandwidth-stability-analyzer": {
"maxVariation": 0.5
}
}
},
"reporting": {
"includeSections": [
"executive-summary",
"configuration-overview",
"bandwidth-stability",
"detailed-tables",
"recommendations"
]
}
}
```
4. Integrate the plugin results into the report data:
```typescript
// In your report generation code
const pluginResults = await pluginManager.executePlugins('analyzer', { datasets });
// Merge plugin results with analysis results
const reportData = {
...analysisResults,
...pluginResults.reduce((acc, result) => ({ ...acc, ...result }), {})
};
// Generate report using template
const report = templateManager.applyTemplate(templateManager.getActiveTemplate(), reportData);
```
By following these steps, you can extend the Network Performance Analyzer with custom metrics, analysis capabilities, and report sections.
## Advanced Customization
### Custom Data Validators
You can create custom data validators to ensure your datasets meet specific requirements:
```typescript
import { DataValidator } from '../utils/DataValidator';
class CustomDataValidator extends DataValidator {
constructor() {
super();
// Add custom validation rules
this.addValidationRule('iperfTests', this.validateIperfTests);
this.addValidationRule('dnsResults', this.validateDnsResults);
}
private validateIperfTests(iperfTests: any[]): boolean {
if (!Array.isArray(iperfTests) || iperfTests.length === 0) {
this.addValidationError('iperfTests', 'No iperf test results found');
return false;
}
// Check for required fields in each test
for (let i = 0; i < iperfTests.length; i++) {
const test = iperfTests[i];
if (!test.bandwidthMbps) {
this.addValidationError('iperfTests', `Test ${i} is missing bandwidthMbps`);
return false;
}
if (test.bandwidthMbps <= 0) {
this.addValidationError('iperfTests', `Test ${i} has invalid bandwidthMbps: ${test.bandwidthMbps}`);
return false;
}
}
return true;
}
private validateDnsResults(dnsResults: any[]): boolean {
// Implement custom DNS validation logic
return true;
}
}
```
### Custom Performance Monitoring
You can extend the performance monitoring capabilities:
```typescript
import { PerformanceMonitor } from '../utils/PerformanceMonitor';
class EnhancedPerformanceMonitor extends PerformanceMonitor {
private cpuUsageHistory: number[] = [];
private networkUsageHistory: number[] = [];
constructor(options: any) {
super(options);
// Add additional monitoring metrics
this.monitorCpuUsage();
this.monitorNetworkUsage();
}
private monitorCpuUsage(): void {
const interval = setInterval(() => {
const cpuUsage = this.getCurrentCpuUsage();
this.cpuUsageHistory.push(cpuUsage);
if (cpuUsage > this.options.cpuThresholdPercent) {
this.emit('cpu-threshold-exceeded', {
current: cpuUsage,
threshold: this.options.cpuThresholdPercent
});
}
}, this.options.monitoringInterval);
this.intervals.push(interval);
}
private monitorNetworkUsage(): void {
// Implement network usage monitoring
}
private getCurrentCpuUsage(): number {
// Implement CPU usage calculation
return 0;
}
generateReport(): string {
const baseReport = super.generateReport();
// Add CPU and network usage to the report
let enhancedReport = baseReport + '\n\n## CPU Usage\n\n';
enhancedReport += 'Time | CPU Usage (%)\n';
enhancedReport += '---- | ------------\n';
for (let i = 0; i < this.cpuUsageHistory.length; i++) {
const timestamp = new Date(this.startTime + i * this.options.monitoringInterval).toISOString();
enhancedReport += `${timestamp} | ${this.cpuUsageHistory[i].toFixed(2)}\n`;
}
return enhancedReport;
}
}
```
### Custom Streaming Data Processing
For handling large datasets efficiently:
```typescript
import { StreamingJsonParser } from '../utils/StreamingJsonParser';
import { Transform } from 'stream';
class EnhancedStreamingParser extends StreamingJsonParser {
async parseFileWithTransform(filePath: string, transformFn: (data: any) => any): Promise<any> {
return new Promise((resolve, reject) => {
const results: any = {};
// Create transform stream
const transformStream = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
try {
// Apply transformation function
const transformedChunk = transformFn(chunk);
this.push(transformedChunk);
callback();
} catch (error) {
callback(error);
}
}
});
// Set up pipeline
this.createReadStream(filePath)
.pipe(transformStream)
.on('data', (data) => {
// Process transformed data
if (data.type === 'iperfTest') {
if (!results.iperfTests) results.iperfTests = [];
results.iperfTests.push(data.value);
} else if (data.type === 'dnsResult') {
if (!results.dnsResults) results.dnsResults = [];
results.dnsResults.push(data.value);
}
})
.on('error', (error) => {
reject(error);
})
.on('end', () => {
resolve(results);
});
});
}
}
```
## Integration with External Systems
### Exporting Results to External Systems
You can create plugins to export analysis results to external systems:
```typescript
import { Plugin, PluginContext } from './PluginManager';
import axios from 'axios';
class ExternalSystemExporter implements Plugin {
name = 'external-system-exporter';
description = 'Exports analysis results to an external system';
version = '1.0.0';
private config: any = {};
async initialize(config: any): Promise<void> {
this.config = config || {};
// Validate required configuration
if (!this.config.apiUrl) {
throw new Error('apiUrl is required in plugin configuration');
}
if (!this.config.apiKey) {
throw new Error('apiKey is required in plugin configuration');
}
}
async execute(context: PluginContext): Promise<any> {
const { analysisResults } = context;
if (!analysisResults) {
return { error: 'No analysis results to export' };
}
try {
// Prepare data for export
const exportData = this.prepareExportData(analysisResults);
// Send data to external system
const response = await axios.post(this.config.apiUrl, exportData, {
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json'
}
});
return {
exported: true,
exportId: response.data.id,
timestamp: new Date().toISOString()
};
} catch (error) {
console.error('Error exporting to external system:', error);
return {
exported: false,
error: error.message
};
}
}
private prepareExportData(analysisResults: any): any {
// Extract relevant data for export
return {
summary: analysisResults.summary,
iperfPerformance: analysisResults.iperfAnalysis?.bandwidthComparison || [],
dnsPerformance: analysisResults.dnsAnalysis?.performanceMetrics || [],
anomalies: analysisResults.anomalies || [],
timestamp: new Date().toISOString(),
source: 'network-performance-analyzer'
};
}
}
export const type = 'reporter';
export const author = 'Your Name';
export default new ExternalSystemExporter();
```
### Importing Data from External Sources
You can create plugins to import data from external sources:
```typescript
import { Plugin, PluginContext } from './PluginManager';
import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
class ExternalDataImporter implements Plugin {
name = 'external-data-importer';
description = 'Imports network performance data from external sources';
version = '1.0.0';
private config: any = {};
async initialize(config: any): Promise<void> {
this.config = config || {};
// Validate required configuration
if (!this.config.dataSources || !Array.isArray(this.config.dataSources)) {
throw new Error('dataSources array is required in plugin configuration');
}
}
async execute(context: PluginContext): Promise<any> {
const { datasetPath } = context;
if (!datasetPath) {
return { error: 'No dataset path provided' };
}
try {
const importedDatasets = [];
// Import data from each configured source
for (const source of this.config.dataSources) {
const data = await this.importFromSource(source);
if (data) {
// Save imported data to dataset directory
const datasetName = `imported-${source.name}-${new Date().toISOString().replace(/[:.]/g, '-')}`;
const datasetDir = path.join(datasetPath, datasetName);
await fs.ensureDir(datasetDir);
// Save results file
const resultsFile = path.join(datasetDir, `results_${Date.now()}.json`);
await fs.writeJson(resultsFile, data, { spaces: 2 });
importedDatasets.push({
name: datasetName,
source: source.name,
path: datasetDir,
resultsFile
});
}
}
return {
importedDatasets
};
} catch (error) {
console.error('Error importing external data:', error);
return {
error: error.message
};
}
}
private async importFromSource(source: any): Promise<any> {
switch (source.type) {
case 'api':
return this.importFromApi(source);
case 'file':
return this.importFromFile(source);
default:
throw new Error(`Unsupported source type: ${source.type}`);
}
}
private async importFromApi(source: any): Promise<any> {
const response = await axios.get(source.url, {
headers: source.headers || {}
});
// Transform API response to match expected format
return this.transformData(response.data, source.mapping);
}
private async importFromFile(source: any): Promise<any> {
const data = await fs.readJson(source.path);
// Transform file data to match expected format
return this.transformData(data, source.mapping);
}
private transformData(data: any, mapping: any): any {
if (!mapping) return data;
const result: any = {};
// Map iperf tests
if (mapping.iperfTests && data[mapping.iperfTests.source]) {
result.iperfTests = data[mapping.iperfTests.source].map((item: any) => ({
server: item[mapping.iperfTests.serverField] || 'unknown',
scenario: item[mapping.iperfTests.scenarioField] || 'unknown',
success: item[mapping.iperfTests.successField] !== undefined ? item[mapping.iperfTests.successField] : true,
bandwidthMbps: parseFloat(item[mapping.iperfTests.bandwidthField]) || 0,
jitterMs: parseFloat(item[mapping.iperfTests.jitterField]) || 0,
packetLoss: parseFloat(item[mapping.iperfTests.packetLossField]) || 0
}));
}
// Map DNS results
if (mapping.dnsResults && data[mapping.dnsResults.source]) {
result.dnsResults = data[mapping.dnsResults.source].map((item: any) => ({
domain: item[mapping.dnsResults.domainField] || 'unknown',
dnsServer: item[mapping.dnsResults.serverField] || 'unknown',
success: item[mapping.dnsResults.successField] !== undefined ? item[mapping.dnsResults.successField] : true,
responseTimeMs: parseFloat(item[mapping.dnsResults.responseTimeField]) || 0
}));
}
return result;
}
}
export const type = 'parser';
export const author = 'Your Name';
export default new ExternalDataImporter();
```
### Integration with Monitoring Systems
You can create plugins to integrate with monitoring systems:
```typescript
import { Plugin, PluginContext } from './PluginManager';
class MonitoringSystemIntegration implements Plugin {
name = 'monitoring-system-integration';
description = 'Integrates analysis results with monitoring systems';
version = '1.0.0';
private config: any = {};
async initialize(config: any): Promise<void> {
this.config = config || {};
}
async execute(context: PluginContext): Promise<any> {
const { analysisResults } = context;
if (!analysisResults) {
return { error: 'No analysis results available' };
}
try {
// Generate metrics for monitoring system
const metrics = this.generateMetrics(analysisResults);
// Send metrics to monitoring system
if (this.config.prometheusEndpoint) {
await this.sendToPrometheus(metrics);
}
if (this.config.grafanaEndpoint) {
await this.sendToGrafana(metrics);
}
return {
metricsGenerated: metrics.length,
monitoringSystemsUpdated: [
this.config.prometheusEndpoint ? 'Prometheus' : null,
this.config.grafanaEndpoint ? 'Grafana' : null
].filter(Boolean)
};
} catch (error) {
console.error('Error integrating with monitoring systems:', error);
return {
error: error.message
};
}
}
private generateMetrics(analysisResults: any): any[] {
const metrics = [];
// Generate bandwidth metrics
if (analysisResults.iperfAnalysis && analysisResults.iperfAnalysis.bandwidthComparison) {
for (const item of analysisResults.iperfAnalysis.bandwidthComparison) {
metrics.push({
name: 'network_bandwidth_mbps',
value: item.avgBandwidthMbps,
labels: {
configuration: item.configuration,
mtu: item.configuration.split('-')[0].replace('mtu', ''),
aws_logging: item.configuration.includes('enabled') ? 'enabled' : 'disabled'
}
});
}
}
// Generate DNS metrics
if (analysisResults.dnsAnalysis && analysisResults.dnsAnalysis.performanceMetrics) {
for (const item of analysisResults.dnsAnalysis.performanceMetrics) {
metrics.push({
name: 'dns_response_time_ms',
value: item.avgResponseTimeMs,
labels: {
configuration: item.configuration,
mtu: item.configuration.split('-')[0].replace('mtu', ''),
aws_logging: item.configuration.includes('enabled') ? 'enabled' : 'disabled'
}
});
}
}
return metrics;
}
private async sendToPrometheus(metrics: any[]): Promise<void> {
// Implement Prometheus integration
}
private async sendToGrafana(metrics: any[]): Promise<void> {
// Implement Grafana integration
}
}
export const type = 'reporter';
export const author = 'Your Name';
export default new MonitoringSystemIntegration();
```
By leveraging these advanced customization options and integration capabilities, you can extend the Network Performance Analyzer to fit your specific requirements and integrate it seamlessly with your existing systems and workflows.