UNPKG

log-vista

Version:

LogVista Agent - Lightweight system monitoring and log collection for any project/language

210 lines (176 loc) 6.31 kB
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;