log-vista
Version:
LogVista Agent - Lightweight system monitoring and log collection for any project/language
210 lines (176 loc) • 6.31 kB
JavaScript
const axios = require('axios');
const logger = require('./logger');
class Uploader {
constructor(config) {
this.config = config;
this.axios = axios.create({
baseURL: config.central_system.url,
headers: {
'Authorization': `Bearer ${config.central_system.token}`,
'Content-Type': 'application/json'
},
timeout: 30000
});
}
async uploadMetrics(metrics) {
try {
if (!metrics || metrics.length === 0) return;
const response = await this.axios.post('/api/metrics', {
metrics: metrics,
agent: {
version: require('../package.json').version,
timestamp: new Date().toISOString()
}
});
logger.info(`Successfully uploaded ${metrics.length} metric records`);
return response.data;
} catch (error) {
await this.handleUploadError('metrics', error, metrics);
throw error;
}
}
async uploadLogs(logs) {
try {
if (!logs || logs.length === 0) return;
const response = await this.axios.post('/api/logs', {
logs: logs,
agent: {
version: require('../package.json').version,
timestamp: new Date().toISOString()
}
});
logger.info(`Successfully uploaded ${logs.length} log records`);
return response.data;
} catch (error) {
await this.handleUploadError('logs', error, logs);
throw error;
}
}
async uploadServerInfo(serverInfo) {
try {
const response = await this.axios.post('/api/servers/register', {
...serverInfo,
agent: {
version: require('../package.json').version,
timestamp: new Date().toISOString()
}
});
logger.info('Successfully registered/updated server info');
return response.data;
} catch (error) {
await this.handleUploadError('server-info', error, serverInfo);
throw error;
}
}
async testConnection() {
try {
const response = await this.axios.get('/api/health');
logger.info('Connection test successful:', response.data);
return true;
} catch (error) {
logger.error('Connection test failed:', error.message);
return false;
}
}
async handleUploadError(type, error, data) {
const errorInfo = {
type: type,
timestamp: new Date().toISOString(),
error: {
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText
},
dataCount: Array.isArray(data) ? data.length : 1
};
if (error.response) {
// Server responded with error status
logger.error(`Upload failed for ${type} - Server Error:`, {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data
});
} else if (error.request) {
// Request was made but no response received
logger.error(`Upload failed for ${type} - Network Error:`, {
message: error.message,
code: error.code
});
// Save data locally for retry
await this.saveOfflineData(type, data);
} else {
// Something else happened
logger.error(`Upload failed for ${type} - Unknown Error:`, error.message);
}
}
async saveOfflineData(type, data) {
try {
const fs = require('fs').promises;
const path = require('path');
const offlineDir = path.join(__dirname, '../logs/offline');
await fs.mkdir(offlineDir, { recursive: true });
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `${type}-${timestamp}.json`;
const filepath = path.join(offlineDir, filename);
await fs.writeFile(filepath, JSON.stringify({
type: type,
timestamp: new Date().toISOString(),
data: data
}, null, 2));
logger.info(`Saved ${type} data offline: ${filename}`);
} catch (saveError) {
logger.error('Failed to save offline data:', saveError);
}
}
async retryOfflineData() {
try {
const fs = require('fs').promises;
const path = require('path');
const offlineDir = path.join(__dirname, '../logs/offline');
try {
const files = await fs.readdir(offlineDir);
for (const file of files) {
if (!file.endsWith('.json')) continue;
const filepath = path.join(offlineDir, file);
const content = await fs.readFile(filepath, 'utf8');
const offlineData = JSON.parse(content);
try {
if (offlineData.type === 'metrics') {
await this.uploadMetrics(offlineData.data);
} else if (offlineData.type === 'logs') {
await this.uploadLogs(offlineData.data);
} else if (offlineData.type === 'server-info') {
await this.uploadServerInfo(offlineData.data);
}
// Delete file after successful upload
await fs.unlink(filepath);
logger.info(`Successfully retried offline data: ${file}`);
} catch (retryError) {
logger.warn(`Failed to retry offline data ${file}:`, retryError.message);
}
}
} catch (dirError) {
// Offline directory doesn't exist yet
logger.debug('No offline data directory found');
}
} catch (error) {
logger.error('Error retrying offline data:', error);
}
}
async withRetry(operation, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
logger.warn(`Attempt ${attempt} failed, retrying in ${delay}ms:`, error.message);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
}
module.exports = Uploader;