2023-01-03 19:55:46 +00:00
|
|
|
// Copyright 2018 Signal Messenger, LLC
|
2021-01-06 17:23:15 +00:00
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2021-09-17 18:27:53 +00:00
|
|
|
import { memoize, sortBy } from 'lodash';
|
|
|
|
import { reallyJsonStringify } from '../util/reallyJsonStringify';
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { FetchLogIpcData, LogEntryType } from './shared';
|
2021-09-17 18:27:53 +00:00
|
|
|
import {
|
|
|
|
LogLevel,
|
|
|
|
getLogLevelString,
|
|
|
|
isFetchLogIpcData,
|
|
|
|
isLogEntry,
|
|
|
|
levelMaxLength,
|
|
|
|
} from './shared';
|
|
|
|
import { redactAll } from '../util/privacy';
|
|
|
|
import { getEnvironment } from '../environment';
|
2021-01-06 17:23:15 +00:00
|
|
|
|
2021-09-17 18:27:53 +00:00
|
|
|
// The mechanics of preparing a log for publish
|
|
|
|
|
|
|
|
const headerSectionTitle = (title: string) => `========= ${title} =========`;
|
|
|
|
|
|
|
|
const headerSection = (
|
|
|
|
title: string,
|
|
|
|
data: Readonly<Record<string, unknown>>
|
|
|
|
): string => {
|
|
|
|
const sortedEntries = sortBy(Object.entries(data), ([key]) => key);
|
|
|
|
return [
|
|
|
|
headerSectionTitle(title),
|
|
|
|
...sortedEntries.map(
|
|
|
|
([key, value]) => `${key}: ${redactAll(String(value))}`
|
|
|
|
),
|
|
|
|
'',
|
|
|
|
].join('\n');
|
|
|
|
};
|
|
|
|
|
2021-10-06 16:16:51 +00:00
|
|
|
const getHeader = (
|
|
|
|
{
|
|
|
|
capabilities,
|
|
|
|
remoteConfig,
|
|
|
|
statistics,
|
|
|
|
user,
|
|
|
|
}: Omit<FetchLogIpcData, 'logEntries'>,
|
|
|
|
nodeVersion: string,
|
2023-04-20 21:23:19 +00:00
|
|
|
appVersion: string,
|
|
|
|
osVersion: string,
|
2023-09-06 01:03:30 +00:00
|
|
|
userAgent: string,
|
|
|
|
linuxVersion?: string
|
2021-10-06 16:16:51 +00:00
|
|
|
): string =>
|
2021-09-17 18:27:53 +00:00
|
|
|
[
|
|
|
|
headerSection('System info', {
|
|
|
|
Time: Date.now(),
|
2023-04-20 21:23:19 +00:00
|
|
|
'User agent': userAgent,
|
2021-10-06 16:16:51 +00:00
|
|
|
'Node version': nodeVersion,
|
2021-09-17 18:27:53 +00:00
|
|
|
Environment: getEnvironment(),
|
2021-10-06 16:16:51 +00:00
|
|
|
'App version': appVersion,
|
2023-04-20 21:23:19 +00:00
|
|
|
'OS version': osVersion,
|
2023-09-06 01:03:30 +00:00
|
|
|
...(linuxVersion && { 'Linux version': linuxVersion }),
|
2021-09-17 18:27:53 +00:00
|
|
|
}),
|
|
|
|
headerSection('User info', user),
|
|
|
|
headerSection('Capabilities', capabilities),
|
|
|
|
headerSection('Remote config', remoteConfig),
|
|
|
|
headerSection('Statistics', statistics),
|
|
|
|
headerSectionTitle('Logs'),
|
|
|
|
].join('\n');
|
|
|
|
|
|
|
|
const getLevel = memoize((level: LogLevel): string => {
|
|
|
|
const text = getLogLevelString(level);
|
|
|
|
return text.toUpperCase().padEnd(levelMaxLength, ' ');
|
|
|
|
});
|
|
|
|
|
|
|
|
function formatLine(mightBeEntry: unknown): string {
|
|
|
|
const entry: LogEntryType = isLogEntry(mightBeEntry)
|
|
|
|
? mightBeEntry
|
|
|
|
: {
|
|
|
|
level: LogLevel.Error,
|
|
|
|
msg: `Invalid IPC data when fetching logs. Here's what we could recover: ${reallyJsonStringify(
|
|
|
|
mightBeEntry
|
|
|
|
)}`,
|
|
|
|
time: new Date().toISOString(),
|
|
|
|
};
|
|
|
|
|
|
|
|
return `${getLevel(entry.level)} ${entry.time} ${entry.msg}`;
|
|
|
|
}
|
|
|
|
|
2023-04-20 21:23:19 +00:00
|
|
|
export function getLog(
|
|
|
|
data: unknown,
|
2021-10-06 16:16:51 +00:00
|
|
|
nodeVersion: string,
|
2023-04-20 21:23:19 +00:00
|
|
|
appVersion: string,
|
|
|
|
osVersion: string,
|
2023-09-06 01:03:30 +00:00
|
|
|
userAgent: string,
|
|
|
|
linuxVersion?: string
|
2023-04-20 21:23:19 +00:00
|
|
|
): string {
|
2022-05-10 01:12:04 +00:00
|
|
|
let header: string;
|
|
|
|
let body: string;
|
|
|
|
if (isFetchLogIpcData(data)) {
|
|
|
|
const { logEntries } = data;
|
2023-09-06 01:03:30 +00:00
|
|
|
header = getHeader(
|
|
|
|
data,
|
|
|
|
nodeVersion,
|
|
|
|
appVersion,
|
|
|
|
osVersion,
|
|
|
|
userAgent,
|
|
|
|
linuxVersion
|
|
|
|
);
|
2022-05-10 01:12:04 +00:00
|
|
|
body = logEntries.map(formatLine).join('\n');
|
|
|
|
} else {
|
|
|
|
header = headerSectionTitle('Partial logs');
|
|
|
|
const entry: LogEntryType = {
|
|
|
|
level: LogLevel.Error,
|
|
|
|
msg: 'Invalid IPC data when fetching logs; dropping all logs',
|
|
|
|
time: new Date().toISOString(),
|
|
|
|
};
|
|
|
|
body = formatLine(entry);
|
|
|
|
}
|
2021-09-17 18:27:53 +00:00
|
|
|
|
2022-05-10 01:12:04 +00:00
|
|
|
return `${header}\n${body}`;
|
2021-09-17 18:27:53 +00:00
|
|
|
}
|