Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/apps/main/logging/setup-app-log-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ describe('setup-app-log-routing', () => {
expect(result).toBe('/tmp/internxt-logs/drive-important.log');
});

it('should keep info level logs in the important file to match core logger behavior', () => {
// When
const result = resolveAppLogFilePath({
logsPath,
message: {
level: 'info',
data: [createSerializedLogMessage({ header: 'E - b - anti', msg: '[CLAM_AVD] clamd process exited' })],
},
});

// Then
expect(result).toBe('/tmp/internxt-logs/drive-important.log');
});

it('should keep non-antivirus logs in the main log file', () => {
// When
const result = resolveAppLogFilePath({
Expand Down Expand Up @@ -72,5 +86,24 @@ describe('setup-app-log-routing', () => {
// Then
expect(result).toBe('/tmp/internxt-logs/drive-antivirus.log');
});

it('should route structured antivirus logs when message data is an object', () => {
// When
const result = resolveAppLogFilePath({
logsPath,
message: {
level: 'debug',
data: [
{
tag: 'ANTIVIRUS',
msg: 'ClamAV initialized successfully',
},
],
},
});

// Then
expect(result).toBe('/tmp/internxt-logs/drive-antivirus.log');
});
});
});
78 changes: 51 additions & 27 deletions src/apps/main/logging/setup-app-log-routing.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createRequire } from 'node:module';
import { join } from 'node:path';

type Pops = {
Expand All @@ -10,6 +9,13 @@
level?: string;
};

type StructuredLogEntry = {
tag?: unknown;
header?: unknown;
msg?: unknown;
message?: unknown;
};

type ElectronLogModule = {
transports: {
file: {
Expand All @@ -23,15 +29,28 @@
const IMPORTANT_LOG_FILE_NAME = 'drive-important.log';
const ANTIVIRUS_LOG_FILE_NAME = 'drive-antivirus.log';
const ANTIVIRUS_HEADER_PATTERN = /header:\s'[^']*-\santi'/;
const ANTIVIRUS_STRUCTURED_HEADER_PATTERN = /-\s*anti\b/i;
const ANTIVIRUS_MESSAGE_PATTERNS = [
/\[CLAM_AVD\]/,
/\[freshclam/i,
/\[ANTIVIRUS_MANAGER\]/,
/window\.electron\.antivirus/i,
/\bclamd?\b/i,
/\bantivirus\b/i,
];
const ELECTRON_LOG_MODULE_IDS = ['electron-log', '@internxt/drive-desktop-core/node_modules/electron-log'];
const moduleRequire = createRequire(__filename);

/**
* Esteban Galvis Triana
* v2.6.0
* Import the electron-log module that @internxt/drive-desktop-core
* bundles (nested node_modules). When webpack processes this file, it resolves
* this path to the same module instance used by setup-electron-log.js from the
* core package. Using createRequire() at runtime would load a DIFFERENT native
* instance that bypasses webpack's module registry, so patching it has no
* effect on the instance the logger actually uses.
*/
// eslint-disable-next-line @typescript-eslint/no-var-requires
const coreElectronLog = require('@internxt/drive-desktop-core/node_modules/electron-log') as ElectronLogModule;

Check warning on line 53 in src/apps/main/logging/setup-app-log-routing.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not use internal APIs of your dependencies

See more on https://sonarcloud.io/project/issues?id=internxt_drive-desktop-linux&issues=AZ5wUQVBzsg_dqnhifxf&open=AZ5wUQVBzsg_dqnhifxf&pullRequest=349

function isSerializedAntivirusLogEntry({ value }: { value: unknown }) {
if (typeof value !== 'string') {
Expand All @@ -41,12 +60,35 @@
return ANTIVIRUS_HEADER_PATTERN.test(value) || ANTIVIRUS_MESSAGE_PATTERNS.some((pattern) => pattern.test(value));
}

function isStructuredLogEntry(value: unknown): value is StructuredLogEntry {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function isStructuredAntivirusLogEntry({ value }: { value: unknown }) {
if (!isStructuredLogEntry(value)) {
return false;
}

const { tag, header, msg, message } = value;
const hasAntivirusTag = typeof tag === 'string' && (tag === 'ANTIVIRUS' || tag === 'anti');
const hasAntivirusHeader = typeof header === 'string' && ANTIVIRUS_STRUCTURED_HEADER_PATTERN.test(header);
const hasAntivirusMessage =
(typeof msg === 'string' && ANTIVIRUS_MESSAGE_PATTERNS.some((pattern) => pattern.test(msg))) ||
(typeof message === 'string' && ANTIVIRUS_MESSAGE_PATTERNS.some((pattern) => pattern.test(message)));

return hasAntivirusTag || hasAntivirusHeader || hasAntivirusMessage;
}

function isAntivirusLogMessage({ message }: { message?: LogMessage }) {
return message?.data?.some((value) => isSerializedAntivirusLogEntry({ value })) ?? false;
return (
message?.data?.some((value) => {
return isSerializedAntivirusLogEntry({ value }) || isStructuredAntivirusLogEntry({ value });
}) ?? false
);
}

export function resolveAppLogFilePath({ logsPath, message }: Pops & { message?: LogMessage }) {
if (message?.level === 'error') {
if (message?.level === 'error' || message?.level === 'info') {
return join(logsPath, IMPORTANT_LOG_FILE_NAME);
}

Expand All @@ -57,28 +99,10 @@
return join(logsPath, DEFAULT_LOG_FILE_NAME);
}

function getElectronLogModules() {
const modules = new Map<string, ElectronLogModule>();

for (const moduleId of ELECTRON_LOG_MODULE_IDS) {
try {
const electronLog = moduleRequire(moduleId) as ElectronLogModule;
const resolvedModulePath = moduleRequire.resolve(moduleId);
modules.set(resolvedModulePath, electronLog);
} catch {
continue;
}
}

return [...modules.values()];
}

export function setupAppLogRouting({ logsPath }: Pops) {
for (const electronLog of getElectronLogModules()) {
electronLog.transports.file.resolvePathFn = (_, message) => {
return resolveAppLogFilePath({ logsPath, message });
};
coreElectronLog.transports.file.resolvePathFn = (_, message) => {
return resolveAppLogFilePath({ logsPath, message });
};

electronLog.transports.file.resolvePath = electronLog.transports.file.resolvePathFn;
}
coreElectronLog.transports.file.resolvePath = coreElectronLog.transports.file.resolvePathFn;
}
Loading