summaryrefslogtreecommitdiffhomepage
path: root/gui/src/main
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-01-12 13:30:46 +0100
committerOskar Nyberg <oskar@mullvad.net>2021-01-15 13:32:09 +0100
commitfd02a14f3fad35f49cfd7b9deb3aae02fcf9dda2 (patch)
tree1515c4cef215db8bc1487c50ccf30de06182eff3 /gui/src/main
parente0e9fe815b047fbc75e3bf01eea95133ae7c035f (diff)
downloadmullvadvpn-fd02a14f3fad35f49cfd7b9deb3aae02fcf9dda2.tar.xz
mullvadvpn-fd02a14f3fad35f49cfd7b9deb3aae02fcf9dda2.zip
Implement logging functionality
Diffstat (limited to 'gui/src/main')
-rw-r--r--gui/src/main/logging.ts103
1 files changed, 103 insertions, 0 deletions
diff --git a/gui/src/main/logging.ts b/gui/src/main/logging.ts
new file mode 100644
index 0000000000..fb21c54b48
--- /dev/null
+++ b/gui/src/main/logging.ts
@@ -0,0 +1,103 @@
+import { app } from 'electron';
+import fs from 'fs';
+import path from 'path';
+import { IpcMainEventChannel } from '../shared/ipc-event-channel';
+import { LogLevel, ILogInput, ILogOutput } from '../shared/logging-types';
+
+export const OLD_LOG_FILES = ['frontend-renderer.log'];
+
+export class FileOutput implements ILogOutput {
+ private fileDescriptor?: number;
+
+ constructor(public level: LogLevel, private filePath: string) {
+ try {
+ this.fileDescriptor = fs.openSync(filePath, fs.constants.O_CREAT | fs.constants.O_WRONLY);
+ } catch (e) {
+ console.error(`Failed to open ${this.filePath}`);
+ }
+ }
+
+ public dispose() {
+ if (this.fileDescriptor) {
+ try {
+ fs.closeSync(this.fileDescriptor);
+ } catch (e) {
+ console.error(`Failed to close ${this.filePath}`);
+ }
+ }
+ }
+
+ public write(_level: LogLevel, message: string) {
+ if (this.fileDescriptor) {
+ fs.write(this.fileDescriptor, `${message}\n`, (err) => {
+ if (err) {
+ console.error(`Failed to log to ${this.filePath}`);
+ }
+ });
+ }
+ }
+}
+
+export class IpcInput implements ILogInput {
+ public on(handler: (level: LogLevel, message: string) => void) {
+ IpcMainEventChannel.logging.handleLog(({ level, message }) => handler(level, message));
+ }
+}
+
+export function getMainLogPath() {
+ return path.join(getLogDirectoryDir(), 'main.log');
+}
+
+export function getRendererLogPath() {
+ return path.join(getLogDirectoryDir(), 'renderer.log');
+}
+
+export function createLoggingDirectory(): void {
+ try {
+ fs.mkdirSync(getLogDirectoryDir(), { recursive: true });
+ } catch (e) {
+ console.error('Failed to create logging directory');
+ }
+}
+
+// When cleaning up old log files they are first backed up and the next time removed.
+export function cleanUpLogDirectory(fileNames: string[]): void {
+ fileNames.forEach((fileName) => {
+ const filePath = path.join(getLogDirectoryDir(), fileName);
+ rotateOrDeleteFile(filePath);
+ });
+}
+
+export function backupLogFile(filePath: string): boolean {
+ const backupFilePath = getBackupFilePath(filePath);
+ try {
+ fs.accessSync(filePath);
+ fs.renameSync(filePath, backupFilePath);
+ return true;
+ } catch (e) {
+ console.error(`Failed to backup ${filePath}`);
+ return false;
+ }
+}
+
+export function rotateOrDeleteFile(filePath: string): void {
+ if (!backupLogFile(filePath)) {
+ const backupFilePath = getBackupFilePath(filePath);
+ try {
+ fs.accessSync(backupFilePath);
+ fs.unlinkSync(backupFilePath);
+ } catch (e) {
+ console.error(`Failed to delete ${filePath}`);
+ }
+ }
+}
+
+function getBackupFilePath(filePath: string): string {
+ const parsedPath = path.parse(filePath);
+ parsedPath.base = parsedPath.name + '.old' + parsedPath.ext;
+ return path.normalize(path.format(parsedPath));
+}
+
+function getLogDirectoryDir() {
+ return app.getPath('logs');
+}