synthetic-monitoring
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSynthetic Monitoring
合成监控
Overview
概述
Set up synthetic monitoring to automatically simulate real user journeys, API workflows, and critical business transactions to detect issues and validate performance.
设置合成监控,自动模拟真实用户旅程、API工作流和关键业务交易,以检测问题并验证性能。
When to Use
适用场景
- End-to-end workflow validation
- API flow testing
- User journey simulation
- Transaction monitoring
- Critical path validation
- 端到端工作流验证
- API流程测试
- 用户旅程模拟
- 交易监控
- 关键路径验证
Instructions
操作步骤
1. Synthetic Tests with Playwright
1. 使用Playwright进行合成测试
javascript
// synthetic-tests.js
const { chromium } = require('playwright');
class SyntheticMonitor {
constructor(config = {}) {
this.baseUrl = config.baseUrl || 'https://app.example.com';
this.timeout = config.timeout || 30000;
}
async testUserFlow() {
const browser = await chromium.launch();
const page = await browser.newPage();
const metrics = { steps: {} };
const startTime = Date.now();
try {
// Step 1: Navigate to login
let stepStart = Date.now();
await page.goto(`${this.baseUrl}/login`, { waitUntil: 'networkidle' });
metrics.steps.navigation = Date.now() - stepStart;
// Step 2: Perform login
stepStart = Date.now();
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: 'networkidle' });
metrics.steps.login = Date.now() - stepStart;
// Step 3: Navigate to dashboard
stepStart = Date.now();
await page.goto(`${this.baseUrl}/dashboard`, { waitUntil: 'networkidle' });
metrics.steps.dashboard = Date.now() - stepStart;
// Step 4: Search for products
stepStart = Date.now();
await page.fill('input[placeholder="Search products"]', 'laptop');
await page.waitForSelector('.product-list');
metrics.steps.search = Date.now() - stepStart;
// Step 5: Add to cart
stepStart = Date.now();
const firstProduct = await page.$('.product-item');
if (firstProduct) {
await firstProduct.click();
await page.click('button:has-text("Add to Cart")');
await page.waitForSelector('[data-testid="cart-count"]');
}
metrics.steps.addToCart = Date.now() - stepStart;
metrics.totalTime = Date.now() - startTime;
metrics.status = 'success';
} catch (error) {
metrics.status = 'failed';
metrics.error = error.message;
metrics.totalTime = Date.now() - startTime;
} finally {
await browser.close();
}
return metrics;
}
async testMobileUserFlow() {
const browser = await chromium.launch();
const context = await browser.createBrowserContext({
...chromium.devices['iPhone 12']
});
const page = await context.newPage();
try {
const metrics = { device: 'iPhone 12', steps: {} };
const startTime = Date.now();
let stepStart = Date.now();
await page.goto(this.baseUrl, { waitUntil: 'networkidle' });
metrics.steps.navigation = Date.now() - stepStart;
const viewport = page.viewportSize();
metrics.viewport = viewport;
stepStart = Date.now();
await page.click('.menu-toggle');
await page.waitForSelector('.mobile-menu.open');
metrics.steps.mobileInteraction = Date.now() - stepStart;
metrics.totalTime = Date.now() - startTime;
metrics.status = 'success';
return metrics;
} catch (error) {
return { status: 'failed', error: error.message, device: 'iPhone 12' };
} finally {
await browser.close();
}
}
async testWithPerformanceMetrics() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await page.goto(this.baseUrl, { waitUntil: 'networkidle' });
const perfMetrics = JSON.parse(
await page.evaluate(() => JSON.stringify(window.performance.timing))
);
const metrics = {
navigationTiming: {
domInteractive: perfMetrics.domInteractive - perfMetrics.navigationStart,
domComplete: perfMetrics.domComplete - perfMetrics.navigationStart,
loadComplete: perfMetrics.loadEventEnd - perfMetrics.navigationStart
},
status: 'success'
};
return metrics;
} catch (error) {
return { status: 'failed', error: error.message };
} finally {
await browser.close();
}
}
async recordMetrics(testName, metrics) {
try {
await axios.post('http://monitoring-service/synthetic-results', {
testName,
timestamp: new Date(),
metrics,
passed: metrics.status === 'success'
});
} catch (error) {
console.error('Failed to record metrics:', error);
}
}
}
module.exports = SyntheticMonitor;javascript
// synthetic-tests.js
const { chromium } = require('playwright');
class SyntheticMonitor {
constructor(config = {}) {
this.baseUrl = config.baseUrl || 'https://app.example.com';
this.timeout = config.timeout || 30000;
}
async testUserFlow() {
const browser = await chromium.launch();
const page = await browser.newPage();
const metrics = { steps: {} };
const startTime = Date.now();
try {
// Step 1: Navigate to login
let stepStart = Date.now();
await page.goto(`${this.baseUrl}/login`, { waitUntil: 'networkidle' });
metrics.steps.navigation = Date.now() - stepStart;
// Step 2: Perform login
stepStart = Date.now();
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForNavigation({ waitUntil: 'networkidle' });
metrics.steps.login = Date.now() - stepStart;
// Step 3: Navigate to dashboard
stepStart = Date.now();
await page.goto(`${this.baseUrl}/dashboard`, { waitUntil: 'networkidle' });
metrics.steps.dashboard = Date.now() - stepStart;
// Step 4: Search for products
stepStart = Date.now();
await page.fill('input[placeholder="Search products"]', 'laptop');
await page.waitForSelector('.product-list');
metrics.steps.search = Date.now() - stepStart;
// Step 5: Add to cart
stepStart = Date.now();
const firstProduct = await page.$('.product-item');
if (firstProduct) {
await firstProduct.click();
await page.click('button:has-text("Add to Cart")');
await page.waitForSelector('[data-testid="cart-count"]');
}
metrics.steps.addToCart = Date.now() - stepStart;
metrics.totalTime = Date.now() - startTime;
metrics.status = 'success';
} catch (error) {
metrics.status = 'failed';
metrics.error = error.message;
metrics.totalTime = Date.now() - startTime;
} finally {
await browser.close();
}
return metrics;
}
async testMobileUserFlow() {
const browser = await chromium.launch();
const context = await browser.createBrowserContext({
...chromium.devices['iPhone 12']
});
const page = await context.newPage();
try {
const metrics = { device: 'iPhone 12', steps: {} };
const startTime = Date.now();
let stepStart = Date.now();
await page.goto(this.baseUrl, { waitUntil: 'networkidle' });
metrics.steps.navigation = Date.now() - stepStart;
const viewport = page.viewportSize();
metrics.viewport = viewport;
stepStart = Date.now();
await page.click('.menu-toggle');
await page.waitForSelector('.mobile-menu.open');
metrics.steps.mobileInteraction = Date.now() - stepStart;
metrics.totalTime = Date.now() - startTime;
metrics.status = 'success';
return metrics;
} catch (error) {
return { status: 'failed', error: error.message, device: 'iPhone 12' };
} finally {
await browser.close();
}
}
async testWithPerformanceMetrics() {
const browser = await chromium.launch();
const page = await browser.newPage();
try {
await page.goto(this.baseUrl, { waitUntil: 'networkidle' });
const perfMetrics = JSON.parse(
await page.evaluate(() => JSON.stringify(window.performance.timing))
);
const metrics = {
navigationTiming: {
domInteractive: perfMetrics.domInteractive - perfMetrics.navigationStart,
domComplete: perfMetrics.domComplete - perfMetrics.navigationStart,
loadComplete: perfMetrics.loadEventEnd - perfMetrics.navigationStart
},
status: 'success'
};
return metrics;
} catch (error) {
return { status: 'failed', error: error.message };
} finally {
await browser.close();
}
}
async recordMetrics(testName, metrics) {
try {
await axios.post('http://monitoring-service/synthetic-results', {
testName,
timestamp: new Date(),
metrics,
passed: metrics.status === 'success'
});
} catch (error) {
console.error('Failed to record metrics:', error);
}
}
}
module.exports = SyntheticMonitor;2. API Synthetic Tests
2. API合成测试
javascript
// api-synthetic-tests.js
const axios = require('axios');
class APISyntheticTests {
constructor(config = {}) {
this.baseUrl = config.baseUrl || 'https://api.example.com';
this.client = axios.create({ baseURL: this.baseUrl });
}
async testAuthenticationFlow() {
const results = { steps: {}, status: 'success' };
try {
const registerStart = Date.now();
const registerRes = await this.client.post('/auth/register', {
email: `test-${Date.now()}@example.com`,
password: 'Test@123456'
});
results.steps.register = Date.now() - registerStart;
if (registerRes.status !== 201) throw new Error('Registration failed');
const loginStart = Date.now();
const loginRes = await this.client.post('/auth/login', {
email: registerRes.data.email,
password: 'Test@123456'
});
results.steps.login = Date.now() - loginStart;
const token = loginRes.data.token;
const authStart = Date.now();
await this.client.get('/api/profile', {
headers: { Authorization: `Bearer ${token}` }
});
results.steps.authenticatedRequest = Date.now() - authStart;
const logoutStart = Date.now();
await this.client.post('/auth/logout', {}, {
headers: { Authorization: `Bearer ${token}` }
});
results.steps.logout = Date.now() - logoutStart;
return results;
} catch (error) {
results.status = 'failed';
results.error = error.message;
return results;
}
}
async testTransactionFlow() {
const results = { steps: {}, status: 'success' };
try {
const orderStart = Date.now();
const orderRes = await this.client.post('/api/orders', {
items: [{ sku: 'ITEM-001', quantity: 2 }]
}, {
headers: { 'X-Idempotency-Key': `order-${Date.now()}` }
});
results.steps.createOrder = Date.now() - orderStart;
const getStart = Date.now();
const getRes = await this.client.get(`/api/orders/${orderRes.data.id}`);
results.steps.getOrder = Date.now() - getStart;
const paymentStart = Date.now();
await this.client.post(`/api/orders/${orderRes.data.id}/payment`, {
method: 'credit_card',
amount: getRes.data.total
});
results.steps.processPayment = Date.now() - paymentStart;
return results;
} catch (error) {
results.status = 'failed';
results.error = error.message;
return results;
}
}
async testUnderLoad(concurrentUsers = 10, duration = 60000) {
const startTime = Date.now();
const results = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
p95ResponseTime: 0
};
const responseTimes = [];
const makeRequest = async () => {
const reqStart = Date.now();
try {
await this.client.get('/api/health');
results.successfulRequests++;
responseTimes.push(Date.now() - reqStart);
} catch {
results.failedRequests++;
}
results.totalRequests++;
};
const userSimulations = Array(concurrentUsers).fill(null).map(async () => {
while (Date.now() - startTime < duration) {
await makeRequest();
await new Promise(r => setTimeout(r, Math.random() * 1000));
}
});
await Promise.all(userSimulations);
responseTimes.sort((a, b) => a - b);
results.averageResponseTime =
responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length;
results.p95ResponseTime =
responseTimes[Math.floor(responseTimes.length * 0.95)];
return results;
}
}
module.exports = APISyntheticTests;javascript
// api-synthetic-tests.js
const axios = require('axios');
class APISyntheticTests {
constructor(config = {}) {
this.baseUrl = config.baseUrl || 'https://api.example.com';
this.client = axios.create({ baseURL: this.baseUrl });
}
async testAuthenticationFlow() {
const results = { steps: {}, status: 'success' };
try {
const registerStart = Date.now();
const registerRes = await this.client.post('/auth/register', {
email: `test-${Date.now()}@example.com`,
password: 'Test@123456'
});
results.steps.register = Date.now() - registerStart;
if (registerRes.status !== 201) throw new Error('Registration failed');
const loginStart = Date.now();
const loginRes = await this.client.post('/auth/login', {
email: registerRes.data.email,
password: 'Test@123456'
});
results.steps.login = Date.now() - loginStart;
const token = loginRes.data.token;
const authStart = Date.now();
await this.client.get('/api/profile', {
headers: { Authorization: `Bearer ${token}` }
});
results.steps.authenticatedRequest = Date.now() - authStart;
const logoutStart = Date.now();
await this.client.post('/auth/logout', {}, {
headers: { Authorization: `Bearer ${token}` }
});
results.steps.logout = Date.now() - logoutStart;
return results;
} catch (error) {
results.status = 'failed';
results.error = error.message;
return results;
}
}
async testTransactionFlow() {
const results = { steps: {}, status: 'success' };
try {
const orderStart = Date.now();
const orderRes = await this.client.post('/api/orders', {
items: [{ sku: 'ITEM-001', quantity: 2 }]
}, {
headers: { 'X-Idempotency-Key': `order-${Date.now()}` }
});
results.steps.createOrder = Date.now() - orderStart;
const getStart = Date.now();
const getRes = await this.client.get(`/api/orders/${orderRes.data.id}`);
results.steps.getOrder = Date.now() - getStart;
const paymentStart = Date.now();
await this.client.post(`/api/orders/${orderRes.data.id}/payment`, {
method: 'credit_card',
amount: getRes.data.total
});
results.steps.processPayment = Date.now() - paymentStart;
return results;
} catch (error) {
results.status = 'failed';
results.error = error.message;
return results;
}
}
async testUnderLoad(concurrentUsers = 10, duration = 60000) {
const startTime = Date.now();
const results = {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
averageResponseTime: 0,
p95ResponseTime: 0
};
const responseTimes = [];
const makeRequest = async () => {
const reqStart = Date.now();
try {
await this.client.get('/api/health');
results.successfulRequests++;
responseTimes.push(Date.now() - reqStart);
} catch {
results.failedRequests++;
}
results.totalRequests++;
};
const userSimulations = Array(concurrentUsers).fill(null).map(async () => {
while (Date.now() - startTime < duration) {
await makeRequest();
await new Promise(r => setTimeout(r, Math.random() * 1000));
}
});
await Promise.all(userSimulations);
responseTimes.sort((a, b) => a - b);
results.averageResponseTime =
responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length;
results.p95ResponseTime =
responseTimes[Math.floor(responseTimes.length * 0.95)];
return results;
}
}
module.exports = APISyntheticTests;3. Scheduled Synthetic Monitoring
3. 定时合成监控
javascript
// scheduled-monitor.js
const cron = require('node-cron');
const SyntheticMonitor = require('./synthetic-tests');
const APISyntheticTests = require('./api-synthetic-tests');
const axios = require('axios');
class ScheduledSyntheticMonitor {
constructor(config = {}) {
this.eMonitor = new SyntheticMonitor(config);
this.apiTests = new APISyntheticTests(config);
this.alertThreshold = config.alertThreshold || 5000;
}
start() {
cron.schedule('*/5 * * * *', () => this.runE2ETests());
cron.schedule('*/2 * * * *', () => this.runAPITests());
cron.schedule('0 * * * *', () => this.runLoadTest());
}
async runE2ETests() {
try {
const metrics = await this.eMonitor.testUserFlow();
await this.recordResults('e2e-user-flow', metrics);
if (metrics.totalTime > this.alertThreshold) {
await this.sendAlert('e2e-user-flow', metrics);
}
} catch (error) {
console.error('E2E test failed:', error);
}
}
async runAPITests() {
try {
const authMetrics = await this.apiTests.testAuthenticationFlow();
const transactionMetrics = await this.apiTests.testTransactionFlow();
await this.recordResults('api-auth-flow', authMetrics);
await this.recordResults('api-transaction-flow', transactionMetrics);
if (authMetrics.status === 'failed' || transactionMetrics.status === 'failed') {
await this.sendAlert('api-tests', { authMetrics, transactionMetrics });
}
} catch (error) {
console.error('API test failed:', error);
}
}
async runLoadTest() {
try {
const results = await this.apiTests.testUnderLoad(10, 30000);
await this.recordResults('load-test', results);
if (results.failedRequests > 0) {
await this.sendAlert('load-test', results);
}
} catch (error) {
console.error('Load test failed:', error);
}
}
async recordResults(testName, metrics) {
try {
await axios.post('http://monitoring-service/synthetic-results', {
testName,
timestamp: new Date(),
metrics
});
console.log(`Recorded: ${testName}`, metrics);
} catch (error) {
console.error('Failed to record results:', error);
}
}
async sendAlert(testName, metrics) {
try {
await axios.post('http://alerting-service/alerts', {
type: 'synthetic_monitoring',
testName,
severity: 'warning',
message: `Synthetic test '${testName}' has issues`,
metrics,
timestamp: new Date()
});
console.log(`Alert sent for ${testName}`);
} catch (error) {
console.error('Failed to send alert:', error);
}
}
}
module.exports = ScheduledSyntheticMonitor;javascript
// scheduled-monitor.js
const cron = require('node-cron');
const SyntheticMonitor = require('./synthetic-tests');
const APISyntheticTests = require('./api-synthetic-tests');
const axios = require('axios');
class ScheduledSyntheticMonitor {
constructor(config = {}) {
this.eMonitor = new SyntheticMonitor(config);
this.apiTests = new APISyntheticTests(config);
this.alertThreshold = config.alertThreshold || 5000;
}
start() {
cron.schedule('*/5 * * * *', () => this.runE2ETests());
cron.schedule('*/2 * * * *', () => this.runAPITests());
cron.schedule('0 * * * *', () => this.runLoadTest());
}
async runE2ETests() {
try {
const metrics = await this.eMonitor.testUserFlow();
await this.recordResults('e2e-user-flow', metrics);
if (metrics.totalTime > this.alertThreshold) {
await this.sendAlert('e2e-user-flow', metrics);
}
} catch (error) {
console.error('E2E test failed:', error);
}
}
async runAPITests() {
try {
const authMetrics = await this.apiTests.testAuthenticationFlow();
const transactionMetrics = await this.apiTests.testTransactionFlow();
await this.recordResults('api-auth-flow', authMetrics);
await this.recordResults('api-transaction-flow', transactionMetrics);
if (authMetrics.status === 'failed' || transactionMetrics.status === 'failed') {
await this.sendAlert('api-tests', { authMetrics, transactionMetrics });
}
} catch (error) {
console.error('API test failed:', error);
}
}
async runLoadTest() {
try {
const results = await this.apiTests.testUnderLoad(10, 30000);
await this.recordResults('load-test', results);
if (results.failedRequests > 0) {
await this.sendAlert('load-test', results);
}
} catch (error) {
console.error('Load test failed:', error);
}
}
async recordResults(testName, metrics) {
try {
await axios.post('http://monitoring-service/synthetic-results', {
testName,
timestamp: new Date(),
metrics
});
console.log(`Recorded: ${testName}`, metrics);
} catch (error) {
console.error('Failed to record results:', error);
}
}
async sendAlert(testName, metrics) {
try {
await axios.post('http://alerting-service/alerts', {
type: 'synthetic_monitoring',
testName,
severity: 'warning',
message: `Synthetic test '${testName}' has issues`,
metrics,
timestamp: new Date()
});
console.log(`Alert sent for ${testName}`);
} catch (error) {
console.error('Failed to send alert:', error);
}
}
}
module.exports = ScheduledSyntheticMonitor;Best Practices
最佳实践
✅ DO
✅ 建议
- Test critical user journeys
- Simulate real browser conditions
- Monitor from multiple locations
- Track response times
- Alert on test failures
- Rotate test data
- Test mobile and desktop
- Include error scenarios
- 测试关键用户旅程
- 模拟真实浏览器环境
- 从多个位置进行监控
- 跟踪响应时间
- 测试失败时触发告警
- 轮换测试数据
- 测试移动端和桌面端
- 包含错误场景
❌ DON'T
❌ 避免
- Test with production data
- Reuse test accounts
- Skip timeout configurations
- Ignore test maintenance
- Test too frequently
- Hard-code credentials
- Ignore geographic variations
- Test only happy paths
- 使用生产数据进行测试
- 重复使用测试账号
- 跳过超时配置
- 忽略测试维护
- 测试过于频繁
- 硬编码凭证
- 忽略地域差异
- 仅测试正常路径
Key Metrics
核心指标
- Response time
- Success rate
- Availability
- Core Web Vitals
- Error rate
- 响应时间
- 成功率
- 可用性
- 核心Web指标(Core Web Vitals)
- 错误率