Skip to content

Get Logs

Retrieves a list of log entries from the database. This endpoint provides access to system logs for monitoring, debugging, and audit purposes.

Request

http
GET /api/logs

Authentication Required: Must include JWT token in Authorization header.

Required Scope: None (endpoint is protected but no specific scope is required)

Content-Type: Not required (no request body)

Request Examples

Basic Logs Request

bash
curl -X GET "http://your-server-ip:port/api/logs" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"

Using JavaScript

javascript
const response = await fetch('/api/logs', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

Response

Success (200 OK)

Returns a JSON array of log entries:

json
[
  {
    "id": 1,
    "created_at": "2025-08-22T14:45:49Z",
    "text": "IPCom started. v5.0.0 B4",
    "type": 0
  },
  {
    "id": 2,
    "created_at": "2025-08-22T15:00:07Z",
    "text": "IPCom shutdown initiated by termination signal.",
    "type": 1
  },
  {
    "id": 3,
    "created_at": "2025-08-22T16:26:39Z",
    "text": "Settings updated by administrator",
    "type": 3,
    "data": "[{\"path\":\"general.generate_restore_on_new_session\",\"oldValue\":true,\"newValue\":false}]"
  }
]

Log Entry Fields:

  • id (number): Unique identifier for the log entry
  • created_at (string): Timestamp when the log entry was created (ISO 8601 format)
  • text (string): Human-readable log message
  • type (number): Log entry type/category identifier
  • data (string, optional): Additional structured data in JSON format (only present when extra data is available)

Error Responses

Note: Error responses are returned as plain text, not JSON.

401 Unauthorized

NOT_LOGGED_IN

403 Forbidden

FORBIDDEN

503 Service Unavailable

Database is not ready

Log Types

The type field categorizes log entries using the following enum values:

TypeDescriptionExample
0Informational messagesSystem startup, shutdown, normal operations
1Warning messagesNon-critical issues, alerts
2Error messagesSystem errors, failures, exceptions
3Settings changesConfiguration updates, administrative actions

Usage Examples

Basic Log Retrieval

javascript
async function getLogs(token) {
  try {
    const response = await fetch('/api/logs', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    
    if (!response.ok) {
      // Handle plain text error responses
      const errorText = await response.text();
      throw new Error(`HTTP ${response.status}: ${errorText}`);
    }
    
    const logs = await response.json();
    console.log(`Retrieved ${logs.length} log entries`);
    
    return logs;
  } catch (error) {
    console.error('Failed to retrieve logs:', error);
    throw error;
  }
}

Log Manager Class

javascript
class LogManager {
  constructor(token) {
    this.token = token;
  }
  
  async fetchLogs() {
    const response = await fetch('/api/logs', {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${this.token}`
      }
    });
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Failed to fetch logs: ${errorText}`);
    }
    
    return await response.json();
  }
  
  async getLogsByType(type) {
    const allLogs = await this.fetchLogs();
    return allLogs.filter(log => log.type === type);
  }
  
  async getLogsAfter(timestamp) {
    const allLogs = await this.fetchLogs();
    const targetTime = new Date(timestamp);
    
    return allLogs.filter(log => {
      const logTime = new Date(log.created_at);
      return logTime > targetTime;
    });
  }
  
  async getLogsBefore(timestamp) {
    const allLogs = await this.fetchLogs();
    const targetTime = new Date(timestamp);
    
    return allLogs.filter(log => {
      const logTime = new Date(log.created_at);
      return logTime < targetTime;
    });
  }
  
  async getLogsInRange(startTime, endTime) {
    const allLogs = await this.fetchLogs();
    const start = new Date(startTime);
    const end = new Date(endTime);
    
    return allLogs.filter(log => {
      const logTime = new Date(log.created_at);
      return logTime >= start && logTime <= end;
    });
  }
  
  parseLogData(log) {
    try {
      // Check if data field exists before parsing
      if (log.data) {
        return JSON.parse(log.data);
      }
      return null; // No data field present
    } catch (error) {
      console.warn(`Could not parse log data for entry ${log.id}:`, error);
      return null;
    }
  }
  
  formatLogEntry(log) {
    const parsedData = this.parseLogData(log);
    
    return {
      id: log.id,
      timestamp: new Date(log.created_at),
      message: log.text,
      type: log.type,
      typeDescription: this.getLogTypeDescription(log.type),
      data: parsedData,
      formattedTime: new Date(log.created_at).toLocaleString()
    };
  }
  
  getLogTypeDescription(type) {
    const typeMap = {
      0: 'Info',
      1: 'Warning',
      2: 'Error',
      3: 'Settings'
    };
    
    return typeMap[type] || `Unknown Type (${type})`;
  }
}

// Usage
const logManager = new LogManager(authToken);

// Get all logs
const allLogs = await logManager.fetchLogs();

// Get system event logs only
const infoLogs = await logManager.getLogsByType(0);

// Get logs from the last hour
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
const recentLogs = await logManager.getLogsAfter(oneHourAgo);

// Format logs for display
const formattedLogs = allLogs.map(log => logManager.formatLogEntry(log));

Log Filtering and Analysis

javascript
class LogAnalyzer {
  constructor(token) {
    this.logManager = new LogManager(token);
  }
  
  async analyzeLogsByType() {
    const logs = await this.logManager.fetchLogs();
    
    const analysis = {
      total: logs.length,
      byType: {},
      timeRange: null
    };
    
    // Count by type
    logs.forEach(log => {
      const typeDesc = this.logManager.getLogTypeDescription(log.type);
      analysis.byType[typeDesc] = (analysis.byType[typeDesc] || 0) + 1;
    });
    
    // Determine time range
    if (logs.length > 0) {
      const times = logs.map(log => new Date(log.created_at));
      analysis.timeRange = {
        earliest: new Date(Math.min(...times)),
        latest: new Date(Math.max(...times))
      };
    }
    
    return analysis;
  }
  
  async findErrorLogs() {
    const errorLogs = await this.logManager.getLogsByType(2);
    
    return errorLogs.map(log => {
      const formatted = this.logManager.formatLogEntry(log);
      
      // Try to extract error details from data
      if (formatted.data) {
        formatted.errorDetails = {
          errorType: formatted.data.error_type || 'Unknown',
          component: formatted.data.component || 'Unknown',
          severity: formatted.data.severity || 'Unknown'
        };
      }
      
      return formatted;
    });
  }
  
  async findWarningLogs() {
    const warningLogs = await this.logManager.getLogsByType(1);
    
    return warningLogs.map(log => {
      const formatted = this.logManager.formatLogEntry(log);
      
      // Try to extract warning details from data
      if (formatted.data) {
        formatted.warningDetails = {
          warningType: formatted.data.warning_type || 'Unknown',
          component: formatted.data.component || 'Unknown',
          severity: formatted.data.severity || 'Low'
        };
      }
      
      return formatted;
    });
  }
  
  async findSettingsChanges() {
    const settingsLogs = await this.logManager.getLogsByType(3);
    
    return settingsLogs.map(log => {
      const formatted = this.logManager.formatLogEntry(log);
      
      // Try to extract settings change details from data
      if (formatted.data) {
        // Handle both array and object formats
        let changes = formatted.data;
        if (Array.isArray(formatted.data)) {
          changes = formatted.data[0] || {};
        }
        
        formatted.settingsDetails = {
          path: changes.path || 'Unknown',
          oldValue: changes.oldValue !== undefined ? changes.oldValue : 'Unknown',
          newValue: changes.newValue !== undefined ? changes.newValue : 'Unknown',
          user: changes.user || 'Unknown'
        };
      }
      
      return formatted;
    });
  }
  
  async generateLogSummary(timeWindow = '1h') {
    const logs = await this.logManager.fetchLogs();
    
    // Calculate time threshold
    let threshold;
    switch (timeWindow) {
      case '1h':
        threshold = new Date(Date.now() - 60 * 60 * 1000);
        break;
      case '24h':
        threshold = new Date(Date.now() - 24 * 60 * 60 * 1000);
        break;
      case '7d':
        threshold = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
        break;
      default:
        threshold = new Date(0); // All time
    }
    
    const filteredLogs = logs.filter(log => new Date(log.created_at) > threshold);
    
    const summary = {
      timeWindow: timeWindow,
      totalLogs: filteredLogs.length,
      logTypes: {},
      errors: 0,
      warnings: 0,
      infoMessages: 0,
      settingsChanges: 0
    };
    
    filteredLogs.forEach(log => {
      const typeDesc = this.logManager.getLogTypeDescription(log.type);
      summary.logTypes[typeDesc] = (summary.logTypes[typeDesc] || 0) + 1;
      
      // Count specific categories
      switch (log.type) {
        case 0: summary.infoMessages++; break;
        case 1: summary.warnings++; break;
        case 2: summary.errors++; break;
        case 3: summary.settingsChanges++; break;
      }
    });
    
    return summary;
  }
}

// Usage
const analyzer = new LogAnalyzer(authToken);

// Analyze all logs
const analysis = await analyzer.analyzeLogsByType();
console.log('Log Analysis:', analysis);

// Find recent errors
const errors = await analyzer.findErrorLogs();
console.log('Error logs:', errors);

// Find recent warnings
const warnings = await analyzer.findWarningLogs();
console.log('Warning logs:', warnings);

// Find recent settings changes
const settingsChanges = await analyzer.findSettingsChanges();
console.log('Settings changes:', settingsChanges);

Real-time Log Monitoring

javascript
class LogMonitor {
  constructor(token, refreshInterval = 30000) {
    this.logManager = new LogManager(token);
    this.refreshInterval = refreshInterval;
    this.lastLogId = 0;
    this.isMonitoring = false;
    this.listeners = [];
  }
  
  async startMonitoring() {
    if (this.isMonitoring) {
      console.warn('Monitoring already started');
      return;
    }
    
    this.isMonitoring = true;
    console.log('Starting log monitoring...');
    
    // Get initial logs
    await this.checkForNewLogs();
    
    // Set up polling
    this.monitoringInterval = setInterval(async () => {
      if (this.isMonitoring) {
        await this.checkForNewLogs();
      }
    }, this.refreshInterval);
  }
  
  stopMonitoring() {
    if (!this.isMonitoring) {
      return;
    }
    
    this.isMonitoring = false;
    
    if (this.monitoringInterval) {
      clearInterval(this.monitoringInterval);
      this.monitoringInterval = null;
    }
    
    console.log('Log monitoring stopped');
  }
  
  async checkForNewLogs() {
    try {
      const allLogs = await this.logManager.fetchLogs();
      
      // Find new logs (logs with ID greater than last seen)
      const newLogs = allLogs.filter(log => log.id > this.lastLogId);
      
      if (newLogs.length > 0) {
        // Update last log ID
        this.lastLogId = Math.max(...newLogs.map(log => log.id));
        
        // Notify listeners
        this.notifyListeners(newLogs);
      }
    } catch (error) {
      console.error('Error checking for new logs:', error);
    }
  }
  
  addListener(callback) {
    this.listeners.push(callback);
  }
  
  removeListener(callback) {
    const index = this.listeners.indexOf(callback);
    if (index > -1) {
      this.listeners.splice(index, 1);
    }
  }
  
  notifyListeners(newLogs) {
    this.listeners.forEach(callback => {
      try {
        callback(newLogs);
      } catch (error) {
        console.error('Error in log listener:', error);
      }
    });
  }
}

// Usage
const monitor = new LogMonitor(authToken, 15000); // Check every 15 seconds

// Add listener for new logs
monitor.addListener((newLogs) => {
  console.log(`Received ${newLogs.length} new log entries:`);
  newLogs.forEach(log => {
    const formatted = monitor.logManager.formatLogEntry(log);
    console.log(`[${formatted.formattedTime}] ${formatted.typeDescription}: ${formatted.message}`);
  });
});

// Add listener for error logs
monitor.addListener((newLogs) => {
  const errors = newLogs.filter(log => log.type === 2);
  if (errors.length > 0) {
    console.warn(`🚨 ${errors.length} new error(s) detected!`);
    errors.forEach(error => {
      console.warn(`Error: ${error.text}`);
    });
  }
});

// Add listener for warning logs
monitor.addListener((newLogs) => {
  const warnings = newLogs.filter(log => log.type === 1);
  if (warnings.length > 0) {
    console.warn(`⚠️ ${warnings.length} new warning(s) detected!`);
    warnings.forEach(warning => {
      console.warn(`Warning: ${warning.text}`);
    });
  }
});

// Add listener for settings changes
monitor.addListener((newLogs) => {
  const settingsChanges = newLogs.filter(log => log.type === 3);
  if (settingsChanges.length > 0) {
    console.info(`⚙️ ${settingsChanges.length} settings change(s) detected!`);
    settingsChanges.forEach(change => {
      console.info(`Settings: ${change.text}`);
    });
  }
});

// Start monitoring
await monitor.startMonitoring();

// Stop monitoring when needed
// monitor.stopMonitoring();

Log Export and Reporting

javascript
class LogExporter {
  constructor(token) {
    this.logManager = new LogManager(token);
  }
  
  async exportToCSV(logs = null) {
    if (!logs) {
      logs = await this.logManager.fetchLogs();
    }
    
    const headers = ['ID', 'Timestamp', 'Type', 'Message', 'Data'];
    const csvRows = [headers.join(',')];
    
    logs.forEach(log => {
      const formatted = this.logManager.formatLogEntry(log);
      const row = [
        log.id,
        `"${formatted.formattedTime}"`,
        `"${formatted.typeDescription}"`,
        `"${log.text.replace(/"/g, '""')}"`, // Escape quotes
        `"${(log.data || '').replace(/"/g, '""')}"` // Handle missing data field
      ];
      csvRows.push(row.join(','));
    });
    
    return csvRows.join('\n');
  }
  
  async exportToJSON(logs = null) {
    if (!logs) {
      logs = await this.logManager.fetchLogs();
    }
    
    const formattedLogs = logs.map(log => this.logManager.formatLogEntry(log));
    
    return JSON.stringify({
      exportTime: new Date().toISOString(),
      totalEntries: formattedLogs.length,
      logs: formattedLogs
    }, null, 2);
  }
  
  async generateReport(timeWindow = '24h') {
    const analyzer = new LogAnalyzer(this.logManager.token);
    const summary = await analyzer.generateLogSummary(timeWindow);
    const errors = await analyzer.findErrorLogs();
    const warnings = await analyzer.findWarningLogs();
    const settingsChanges = await analyzer.findSettingsChanges();
    
    const report = {
      reportTime: new Date().toISOString(),
      timeWindow: timeWindow,
      summary: summary,
      recentErrors: errors.slice(-10), // Last 10 errors
      recentWarnings: warnings.slice(-10), // Last 10 warnings
      recentSettingsChanges: settingsChanges.slice(-5), // Last 5 settings changes
      recommendations: this.generateRecommendations(summary, errors, warnings)
    };
    
    return report;
  }
  
  generateRecommendations(summary, errors, warnings) {
    const recommendations = [];
    
    if (summary.errors > 10) {
      recommendations.push('High error count detected. Review system health and investigate recurring errors.');
    }
    
    if (summary.warnings > 20) {
      recommendations.push('High warning count detected. Review system configuration and address potential issues.');
    }
    
    if (summary.settingsChanges > 10 && summary.timeWindow === '1h') {
      recommendations.push('Frequent settings changes detected. Review administrative activity.');
    }
    
    if (summary.infoMessages < 5 && summary.timeWindow === '24h') {
      recommendations.push('Low system activity. Verify system operation and service status.');
    }
    
    const uniqueErrors = new Set(errors.map(e => e.message)).size;
    if (uniqueErrors > 5) {
      recommendations.push('Multiple different error types detected. Consider system maintenance.');
    }
    
    if (recommendations.length === 0) {
      recommendations.push('System logs appear normal. Continue regular monitoring.');
    }
    
    return recommendations;
  }
  
  downloadFile(content, filename, contentType = 'text/plain') {
    const blob = new Blob([content], { type: contentType });
    const url = window.URL.createObjectURL(blob);
    
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    
    document.body.removeChild(link);
    window.URL.revokeObjectURL(url);
  }
}

// Usage
const exporter = new LogExporter(authToken);

// Export all logs to CSV
const csvData = await exporter.exportToCSV();
exporter.downloadFile(csvData, 'system-logs.csv', 'text/csv');

// Export recent logs to JSON
const recentLogs = await logManager.getLogsAfter(new Date(Date.now() - 24 * 60 * 60 * 1000));
const jsonData = await exporter.exportToJSON(recentLogs);
exporter.downloadFile(jsonData, 'recent-logs.json', 'application/json');

// Generate and download report
const report = await exporter.generateReport('7d');
const reportJson = JSON.stringify(report, null, 2);
exporter.downloadFile(reportJson, 'weekly-log-report.json', 'application/json');

Security Considerations

Access Control

  • Authentication Required: Users must be logged in to access logs
  • No Specific Scope: While no specific scope is required, authentication is mandatory
  • Sensitive Information: Logs may contain sensitive system information

Data Privacy

javascript
class SecureLogViewer {
  constructor(token) {
    this.logManager = new LogManager(token);
  }
  
  sanitizeLogData(log) {
    const sanitized = { ...log };
    
    // Remove or mask sensitive information only if data field exists
    if (sanitized.data) {
      try {
        const data = JSON.parse(sanitized.data);
        
        // Mask IP addresses
        if (data.ip_address) {
          data.ip_address = this.maskIP(data.ip_address);
        }
        
        // Mask passwords or tokens
        if (data.password) {
          data.password = '***masked***';
        }
        
        if (data.token) {
          data.token = data.token.substring(0, 8) + '...***masked***';
        }
        
        sanitized.data = JSON.stringify(data);
      } catch (error) {
        // If parsing fails, mask the entire data field
        sanitized.data = '***masked***';
      }
    }
    // If no data field, leave as is
    
    return sanitized;
  }
  
  maskIP(ip) {
    const parts = ip.split('.');
    if (parts.length === 4) {
      return `${parts[0]}.${parts[1]}.***.***.***`;
    }
    return '***masked***';
  }
  
  async getFilteredLogs(userRole = 'user') {
    const logs = await this.logManager.fetchLogs();
    
    // Filter based on user role
    let filteredLogs = logs;
    
    if (userRole !== 'admin') {
      // Non-admin users don't see settings change logs
      filteredLogs = logs.filter(log => log.type !== 3);
    }
    
    // Sanitize all logs
    return filteredLogs.map(log => this.sanitizeLogData(log));
  }
}

Important Notes

  • Authentication Required: Users must be authenticated but no specific scope is needed
  • Plain Text Errors: Error responses are plain text, not JSON (like restart-services endpoint)
  • Full Log Set: Returns all available logs (no pagination or filtering at API level)
  • Client-side Filtering: Implement filtering, pagination, and searching on the client side
  • JSON Data Field: The data field contains JSON-encoded additional information (optional - may not be present)
  • Data Sensitivity: Logs may contain sensitive system information - implement appropriate access controls

Released under the MIT License.