Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | 1x 1x 1x 1x 1x 13x 13x 13x 13x 8x 1x 7x 7x 7x 1x 1x 6x 6x 5x 2x 2x 3x 3x 1x 1x 2x 2x 1x 2x 2x 1x 1x 3x 3x 3x 3x 4x 4x 3x 16x 2x 14x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x | import { LogDispatcher } from './log-dispatcher.service';
import { LogEntry } from './log-provider.interface';
import { NotificationService } from '../services/notification.service';
import { isPlatformBrowser } from '@angular/common';
import { inject, Injectable, PLATFORM_ID } from '@angular/core';
/**
* Service for exporting logs as a downloadable CSV file
*/
@Injectable({ providedIn: 'root' })
export class LogExportService {
private readonly platformId = inject(PLATFORM_ID);
private readonly isBrowser = isPlatformBrowser(this.platformId);
private readonly dispatcher = inject(LogDispatcher);
private readonly notification = inject(NotificationService);
/**
* Download all logs as a CSV file
* File format: semicolon-delimited CSV with headers
*/
async downloadLogs(): Promise<void> {
if (!this.isBrowser) {
return;
}
try {
const storageProvider = this.dispatcher.getStorageProvider();
if (!storageProvider) {
this.notification.info('Хранилище логов недоступно');
return;
}
// Flush any buffered logs first
await this.dispatcher.flush();
// Get all logs
const logs = await storageProvider.getLogs();
if (logs.length === 0) {
this.notification.info('Нет логов для экспорта');
return;
}
// Generate CSV content
const csv = this.generateCSV(logs);
// Trigger download
this.downloadFile(csv, this.generateFilename());
} catch (error) {
console.error('LogExportService: Failed to download logs', error);
this.notification.error('Не удалось скачать логи');
}
}
/**
* Clear all stored logs
*/
async clearLogs(): Promise<void> {
const storageProvider = this.dispatcher.getStorageProvider();
if (storageProvider) {
await storageProvider.clearLogs();
}
}
/**
* Get current storage size in bytes
*/
async getStorageSize(): Promise<number> {
const storageProvider = this.dispatcher.getStorageProvider();
if (storageProvider) {
return storageProvider.getStorageSize();
}
return 0;
}
private generateCSV(logs: LogEntry[]): string {
// CSV header
const headers = ['timestamp', 'level', 'context', 'message', 'data', 'url'];
const rows: string[] = [headers.join(';')];
// Add data rows (oldest first for chronological order)
const sortedLogs = [...logs].reverse();
for (const log of sortedLogs) {
const row = [
log.timestamp.toISOString(),
log.level.toUpperCase(),
this.escapeCSV(log.context ?? ''),
this.escapeCSV(log.message),
this.escapeCSV(log.data !== undefined ? JSON.stringify(log.data) : ''),
this.escapeCSV(log.url ?? ''),
];
rows.push(row.join(';'));
}
return rows.join('\n');
}
/**
* Escape a value for CSV
* - Wrap in quotes if contains semicolon, quote, or newline
* - Double quotes are escaped as ""
*/
private escapeCSV(value: string): string {
if (value.includes(';') || value.includes('"') || value.includes('\n')) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
}
private generateFilename(): string {
const date = new Date().toISOString().split('T')[0];
return `drevo-logs-${date}.csv`;
}
private downloadFile(content: string, filename: string): void {
const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
}
|