Skip to content

Commit

Permalink
Centralize log file and refactor desktop logging
Browse files Browse the repository at this point in the history
- Migrate to `electron-log` v5.X.X.
- Save critical electron events to the log file.
- Replaced remove `ElectronLog` type to `LogFunctions` type.
- Remove `renderer.log` handling from desktop-runtime-error because
  since `electron-log` v5, it only writes to `main.log`. It's also
  best-practices to centralize logs for main and renderer process in
  Electron application for easier tracking and management.
- Remove `I` prefix from related interfaces.
- Move Logger interfaces to application layer as it's cross-cutting
  concern, keep the implementations in infrastructure layer.
- Add all common log levels to Logger interface and implementations.
  This allows using this interface and reference to it in rest of
  Electron main/preloader processes, abstracting `electron-log`
  completely.
- Add `useLogger` compositional hook to make it easier to consume logger
  in Vue components with unified and Vue idiomatic way.
- Refactor `WindowVariables` to remove unnecessarily nullable properties.
- Add documentation to clarify differences between the desktop and web
  versions, listing features available in each distribution to help in
  decision-making.
  • Loading branch information
undergroundwires committed Dec 1, 2023
1 parent 8f5d7ed commit 67ccd73
Show file tree
Hide file tree
Showing 40 changed files with 347 additions and 191 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,9 @@
- 🌍️ **Online**: [https://privacy.sexy](https://privacy.sexy).
- 🖥️ **Offline**: Download directly for: [Windows](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.8/privacy.sexy-Setup-0.12.8.exe), [macOS](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.8/privacy.sexy-0.12.8.dmg), [Linux](https://github.com/undergroundwires/privacy.sexy/releases/download/0.12.8/privacy.sexy-0.12.8.AppImage). For more options, see [here](#additional-install-options).

Online version does not require to run any software on your computer. Offline version has more functions such as running the scripts directly.
For a detailed comparison of features between the desktop and web versions of privacy.sexy, see [Desktop vs. Web Features](./docs/desktop-vs-web-features.md).

💡 You should apply your configuration from time to time (more than once). It would strengthen your privacy and security control because privacy.sexy and its scripts get better and stronger in every new version.
💡 Regularly applying your configuration with privacy.sexy is recommended, especially after each new release and major operating system updates. Each version updates scripts to enhance stability, privacy, and security.

[![privacy.sexy application](img/screenshot.png?raw=true )](https://privacy.sexy)

Expand Down
36 changes: 36 additions & 0 deletions docs/desktop-vs-web-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Desktop vs. Web Features

This table outlines the differences between the desktop and web versions of `privacy.sexy`.

| Feature | Desktop | Web |
| ------- |---------|-----|
| [Usage without installation](#usage-without-installation) | 🔴 Not available | 🟢 Available |
| [Offline usage](#offline-usage) | 🟢 Available | 🟡 Partially available |
| [Auto-updates](#auto-updates) | 🟢 Available | 🟢 Available |
| [Logging](#logging) | 🟢 Available | 🔴 Not available |
| [Script execution](#script-execution) | 🟢 Available | 🔴 Not available |

## Feature Descriptions

### Usage without installation

The web version can be used directly in a browser without any installation, whereas the desktop version requires downloading and installing the software.

> **Note for Linux:** For Linux users, privacy.sexy is available as an AppImage, which is a portable format that does not require traditional installation. This means Linux users can use the desktop version without installation, similar to the web version.
### Offline usage

Once loaded, the web version can be used offline. The desktop version inherently supports offline usage.

### Auto-updates

Both versions automatically update to ensure you have the latest features and security enhancements.

### Logging

The desktop version supports logging of activities to aid in troubleshooting. This feature is not available in the web version.

### Script execution

Direct execution of scripts is possible in the desktop version, offering a more integrated experience.
This functionality is not present in the web version due to browser limitations.
19 changes: 11 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@juggle/resize-observer": "^3.4.0",
"ace-builds": "^1.30.0",
"cross-fetch": "^4.0.0",
"electron-log": "^4.4.8",
"electron-log": "^5.0.0",
"electron-progressbar": "^2.1.0",
"electron-updater": "^6.1.4",
"file-saver": "^2.0.5",
Expand Down
6 changes: 6 additions & 0 deletions src/application/Common/Log/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Logger {
info(...params: unknown[]): void;
warn(...params: unknown[]): void;
error(...params: unknown[]): void;
debug(...params: unknown[]): void;
}
5 changes: 5 additions & 0 deletions src/application/Common/Log/LoggerFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Logger } from '@/application/Common/Log/Logger';

export interface LoggerFactory {
readonly logger: Logger;
}
31 changes: 23 additions & 8 deletions src/infrastructure/Log/ConsoleLogger.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
import { ILogger } from './ILogger';
import { Logger } from '@/application/Common/Log/Logger';

export class ConsoleLogger implements ILogger {
constructor(private readonly consoleProxy: Partial<Console> = console) {
export class ConsoleLogger implements Logger {
constructor(private readonly consoleProxy: ConsoleLogFunctions = globalThis.console) {
if (!consoleProxy) { // do not trust strictNullChecks for global objects
throw new Error('missing console');
}
}

public info(...params: unknown[]): void {
const logFunction = this.consoleProxy?.info;
if (!logFunction) {
throw new Error('missing "info" function');
}
logFunction.call(this.consoleProxy, ...params);
this.consoleProxy.info(...params);
}

public warn(...params: unknown[]): void {
this.consoleProxy.warn(...params);
}

public error(...params: unknown[]): void {
this.consoleProxy.error(...params);
}

public debug(...params: unknown[]): void {
this.consoleProxy.debug(...params);
}
}

interface ConsoleLogFunctions extends Partial<Console> {
readonly info: Console['info'];
readonly warn: Console['warn'];
readonly error: Console['error'];
readonly debug: Console['debug'];
}
22 changes: 10 additions & 12 deletions src/infrastructure/Log/ElectronLogger.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ElectronLog } from 'electron-log';
import { ILogger } from './ILogger';
import log from 'electron-log/main';
import { Logger } from '@/application/Common/Log/Logger';
import type { LogFunctions } from 'electron-log';

// Using plain-function rather than class so it can be used in Electron's context-bridging.
export function createElectronLogger(logger: Partial<ElectronLog>): ILogger {
if (!logger) {
throw new Error('missing logger');
}
export function createElectronLogger(logger: LogFunctions = log): Logger {
return {
info: (...params) => {
if (!logger.info) {
throw new Error('missing "info" function');
}
logger.info(...params);
},
info: (...params) => logger.info(...params),
debug: (...params) => logger.debug(...params),
warn: (...params) => logger.warn(...params),
error: (...params) => logger.error(...params),
};
}

export const ElectronLogger = createElectronLogger();
3 changes: 0 additions & 3 deletions src/infrastructure/Log/ILogger.ts

This file was deleted.

5 changes: 0 additions & 5 deletions src/infrastructure/Log/ILoggerFactory.ts

This file was deleted.

10 changes: 8 additions & 2 deletions src/infrastructure/Log/NoopLogger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ILogger } from './ILogger';
import { Logger } from '@/application/Common/Log/Logger';

export class NoopLogger implements ILogger {
export class NoopLogger implements Logger {
public info(): void { /* NOOP */ }

public warn(): void { /* NOOP */ }

public error(): void { /* NOOP */ }

public debug(): void { /* NOOP */ }
}
18 changes: 15 additions & 3 deletions src/infrastructure/Log/WindowInjectedLogger.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Logger } from '@/application/Common/Log/Logger';
import { WindowVariables } from '../WindowVariables/WindowVariables';
import { ILogger } from './ILogger';

export class WindowInjectedLogger implements ILogger {
private readonly logger: ILogger;
export class WindowInjectedLogger implements Logger {
private readonly logger: Logger;

constructor(windowVariables: WindowVariables | undefined | null = window) {
if (!windowVariables) { // do not trust strict null checks for global objects
Expand All @@ -17,4 +17,16 @@ export class WindowInjectedLogger implements ILogger {
public info(...params: unknown[]): void {
this.logger.info(...params);
}

public warn(...params: unknown[]): void {
this.logger.warn(...params);
}

public debug(...params: unknown[]): void {
this.logger.debug(...params);
}

public error(...params: unknown[]): void {
this.logger.error(...params);
}
}
6 changes: 3 additions & 3 deletions src/infrastructure/WindowVariables/WindowVariables.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { OperatingSystem } from '@/domain/OperatingSystem';
import { ISystemOperations } from '@/infrastructure/SystemOperations/ISystemOperations';
import { ILogger } from '@/infrastructure/Log/ILogger';
import { Logger } from '@/application/Common/Log/Logger';

/* Primary entry point for platform-specific injections */
export interface WindowVariables {
readonly isDesktop?: boolean;
readonly isDesktop: boolean;
readonly system?: ISystemOperations;
readonly os?: OperatingSystem;
readonly log?: ILogger;
readonly log: Logger;
}
10 changes: 5 additions & 5 deletions src/presentation/bootstrapping/ClientLoggerFactory.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { RuntimeEnvironment } from '@/infrastructure/RuntimeEnvironment/RuntimeEnvironment';
import { IRuntimeEnvironment } from '@/infrastructure/RuntimeEnvironment/IRuntimeEnvironment';
import { ConsoleLogger } from '@/infrastructure/Log/ConsoleLogger';
import { ILogger } from '@/infrastructure/Log/ILogger';
import { ILoggerFactory } from '@/infrastructure/Log/ILoggerFactory';
import { Logger } from '@/application/Common/Log/Logger';
import { LoggerFactory } from '@/application/Common/Log/LoggerFactory';
import { NoopLogger } from '@/infrastructure/Log/NoopLogger';
import { WindowInjectedLogger } from '@/infrastructure/Log/WindowInjectedLogger';

export class ClientLoggerFactory implements ILoggerFactory {
public static readonly Current: ILoggerFactory = new ClientLoggerFactory();
export class ClientLoggerFactory implements LoggerFactory {
public static readonly Current: LoggerFactory = new ClientLoggerFactory();

public readonly logger: ILogger;
public readonly logger: Logger;

protected constructor(environment: IRuntimeEnvironment = RuntimeEnvironment.CurrentEnvironment) {
if (environment.isDesktop) {
Expand Down
5 changes: 5 additions & 0 deletions src/presentation/bootstrapping/DependencyProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '@/presentation/injectionSymbols';
import { PropertyKeys } from '@/TypeHelpers';
import { useUserSelectionState } from '@/presentation/components/Shared/Hooks/UseUserSelectionState';
import { useLogger } from '@/presentation/components/Shared/Hooks/UseLogger';

export function provideDependencies(
context: IApplicationContext,
Expand Down Expand Up @@ -57,6 +58,10 @@ export function provideDependencies(
return useUserSelectionState(state, events);
},
),
useLogger: (di) => di.provide(
InjectionKeys.useLogger,
useLogger,
),
};
registerAll(Object.values(resolvers), api);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ILogger } from '@/infrastructure/Log/ILogger';
import { Logger } from '@/application/Common/Log/Logger';
import { Bootstrapper } from '../Bootstrapper';
import { ClientLoggerFactory } from '../ClientLoggerFactory';

export class AppInitializationLogger implements Bootstrapper {
constructor(
private readonly logger: ILogger = ClientLoggerFactory.Current.logger,
private readonly logger: Logger = ClientLoggerFactory.Current.logger,
) { }

public async bootstrap(): Promise<void> {
Expand Down
4 changes: 3 additions & 1 deletion src/presentation/components/DevToolkit/DevToolkit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@

<script lang="ts">
import { defineComponent } from 'vue';
import { injectKey } from '@/presentation/injectionSymbols';
import { dumpNames } from './DumpNames';
export default defineComponent({
setup() {
const { log } = injectKey((keys) => keys.useLogger);
const devActions: readonly DevAction[] = [
{
name: 'Log script/category names',
handler: async () => {
const names = await dumpNames();
console.log(names);
log.info(names);
},
},
];
Expand Down
8 changes: 8 additions & 0 deletions src/presentation/components/Shared/Hooks/UseLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { LoggerFactory } from '@/application/Common/Log/LoggerFactory';
import { ClientLoggerFactory } from '@/presentation/bootstrapping/ClientLoggerFactory';

export function useLogger(factory: LoggerFactory = ClientLoggerFactory.Current) {
return {
log: factory.logger,
};
}
6 changes: 3 additions & 3 deletions src/presentation/electron/main/Update/AutoUpdater.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { app, dialog } from 'electron';
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { ProgressInfo } from 'electron-builder';
import log from 'electron-log';
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
import { UpdateProgressBar } from './UpdateProgressBar';

export async function handleAutoUpdate() {
Expand All @@ -23,11 +23,11 @@ function startHandlingUpdateProgress() {
On macOS, download-progress event is not called.
So the indeterminate progress will continue until download is finished.
*/
log.debug('@download-progress@\n', progress);
ElectronLogger.debug('@download-progress@\n', progress);
progressBar.showProgress(progress);
});
autoUpdater.on('update-downloaded', async (info: UpdateInfo) => {
log.info('@update-downloaded@\n', info);
ElectronLogger.info('@update-downloaded@\n', info);
progressBar.close();
await handleUpdateDownloaded();
});
Expand Down
Loading

0 comments on commit 67ccd73

Please sign in to comment.