Appearance
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/logsAuthentication 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 entrycreated_at(string): Timestamp when the log entry was created (ISO 8601 format)text(string): Human-readable log messagetype(number): Log entry type/category identifierdata(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_IN403 Forbidden
FORBIDDEN503 Service Unavailable
Database is not readyLog Types
The type field categorizes log entries using the following enum values:
| Type | Description | Example |
|---|---|---|
| 0 | Informational messages | System startup, shutdown, normal operations |
| 1 | Warning messages | Non-critical issues, alerts |
| 2 | Error messages | System errors, failures, exceptions |
| 3 | Settings changes | Configuration 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
datafield contains JSON-encoded additional information (optional - may not be present) - Data Sensitivity: Logs may contain sensitive system information - implement appropriate access controls