Skip to content

Commit

Permalink
refactor: DH-14692 Stop exporting client from support logs (#2368)
Browse files Browse the repository at this point in the history
Cherry-pick #2279 
- Move `exportLogs` and `logInit` to the `log` package so they could be
reused in Enterprise
- Remove `@deephaven/redux` and `@deephaven/jsapi-shim` dependencies
from `LogExport.ts`
- Serialize Maps in redux data
- Unit tests for `getReduxDataString`
  • Loading branch information
ericlln authored Feb 14, 2025
1 parent 52a4728 commit fd17510
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 43 deletions.
6 changes: 4 additions & 2 deletions package-lock.json

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

7 changes: 5 additions & 2 deletions packages/code-studio/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { Provider } from 'react-redux';
import { LoadingOverlay, preloadTheme } from '@deephaven/components';
import { ApiBootstrap } from '@deephaven/jsapi-bootstrap';
import { store } from '@deephaven/redux';
import logInit from './log/LogInit';
import { logInit } from '@deephaven/log';

logInit();
logInit(
parseInt(import.meta.env.VITE_LOG_LEVEL ?? '', 10),
import.meta.env.VITE_ENABLE_LOG_PROXY === 'true'
);

preloadTheme();

Expand Down
18 changes: 12 additions & 6 deletions packages/code-studio/src/settings/SettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ import {
Logo,
Tooltip,
} from '@deephaven/components';
import { ServerConfigValues, User } from '@deephaven/redux';
import { ServerConfigValues, User, store } from '@deephaven/redux';
import {
BROADCAST_CHANNEL_NAME,
BROADCAST_LOGOUT_MESSAGE,
makeMessage,
} from '@deephaven/jsapi-utils';
import { PluginModuleMap } from '@deephaven/plugin';
import { exportLogs, logHistory } from '@deephaven/log';
import FormattingSectionContent from './FormattingSectionContent';
import LegalNotice from './LegalNotice';
import SettingsMenuSection from './SettingsMenuSection';
import ShortcutSectionContent from './ShortcutsSectionContent';
import { exportLogs } from '../log/LogExport';
import './SettingsMenu.scss';
import ColumnSpecificSectionContent from './ColumnSpecificSectionContent';
import {
Expand Down Expand Up @@ -134,10 +134,16 @@ export class SettingsMenu extends Component<
handleExportSupportLogs(): void {
const { serverConfigValues, pluginData } = this.props;
const pluginInfo = getFormattedPluginInfo(pluginData);
exportLogs(undefined, {
...Object.fromEntries(serverConfigValues),
pluginInfo,
});
exportLogs(
logHistory,
{
uiVersion: import.meta.env.npm_package_version,
userAgent: navigator.userAgent,
...Object.fromEntries(serverConfigValues),
pluginInfo,
},
store.getState()
);
}

render(): ReactElement {
Expand Down
3 changes: 2 additions & 1 deletion packages/log/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"build:babel": "babel ./src --out-dir ./dist --extensions \".ts,.tsx,.js,.jsx\" --source-maps --root-mode upward"
},
"dependencies": {
"event-target-shim": "^6.0.2"
"event-target-shim": "^6.0.2",
"jszip": "^3.10.1"
},
"files": [
"dist"
Expand Down
90 changes: 90 additions & 0 deletions packages/log/src/LogExport.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { getReduxDataString } from './LogExport';

describe('getReduxDataString', () => {
it('should return a JSON string of the redux data', () => {
const reduxData = {
key1: 'value1',
key2: 2,
key3: true,
};
const result = getReduxDataString(reduxData);
const expected = JSON.stringify(reduxData, null, 2);
expect(result).toBe(expected);
});

it('should handle circular references', () => {
const reduxData: Record<string, unknown> = {
key1: 'value1',
};
reduxData.key2 = reduxData;
const result = getReduxDataString(reduxData);
const expected = JSON.stringify(
{
key1: 'value1',
key2: 'Circular ref to root',
},
null,
2
);
expect(result).toBe(expected);
});

it('should handle BigInt values', () => {
const reduxData = {
key1: BigInt('12345678901234567890'),
};
const result = getReduxDataString(reduxData);
const expected = JSON.stringify(
{
key1: '12345678901234567890',
},
null,
2
);
expect(result).toBe(expected);
});

it('should apply blacklist paths', () => {
const reduxData = {
key1: 'should be blacklisted',
key2: {
'key2.1': 'should also be blacklisted',
},
key3: 'value',
};
const result = getReduxDataString(reduxData, [
['key1'],
['key2', 'key2.1'],
]);
const expected = JSON.stringify(
{
key2: {},
key3: 'value',
},
null,
2
);
expect(result).toBe(expected);
});

it('should stringify Maps', () => {
const reduxData = {
key1: new Map([
['key1.1', 'value1.1'],
['key1.2', 'value1.2'],
]),
};
const result = getReduxDataString(reduxData);
const expected = JSON.stringify(
{
key1: [
['key1.1', 'value1.1'],
['key1.2', 'value1.2'],
],
},
null,
2
);
expect(result).toBe(expected);
});
});
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
/* eslint-disable import/prefer-default-export */
import JSZip from 'jszip';
import dh from '@deephaven/jsapi-shim';
import { store } from '@deephaven/redux';
import { logHistory } from './LogInit';

const FILENAME_DATE_FORMAT = 'yyyy-MM-dd-HHmmss';
import type LogHistory from './LogHistory';

// List of objects to blacklist
// '' represents the root object
Expand Down Expand Up @@ -41,6 +36,10 @@ function stringifyReplacer(blacklist: string[][]) {
}
}

if (value instanceof Map) {
return Array.from(value.entries());
}

// not in blacklist, return value
return value;
};
Expand All @@ -66,7 +65,7 @@ function makeSafeToStringify(
blacklist: string[][],
path = 'root',
potentiallyCircularValues: Map<Record<string, unknown>, string> = new Map([
[obj, ''],
[obj, 'root'],
])
): Record<string, unknown> {
const output: Record<string, unknown> = {};
Expand Down Expand Up @@ -104,48 +103,61 @@ function makeSafeToStringify(
return output;
}

function getReduxDataString(blacklist: string[][]): string {
const reduxData = store.getState();
export function getReduxDataString(
reduxData: Record<string, unknown>,
blacklist: string[][] = []
): string {
return JSON.stringify(
makeSafeToStringify(reduxData, blacklist),
stringifyReplacer(blacklist),
2 // Indent w/ 2 spaces
);
}

function getMetadata(
blacklist: string[][],
meta?: Record<string, unknown>
): string {
const metadata = {
uiVersion: import.meta.env.npm_package_version,
userAgent: navigator.userAgent,
...meta,
};
function getFormattedMetadata(metadata?: Record<string, unknown>): string {
return JSON.stringify(metadata, null, 2);
}

/** Format a date to a string that can be used as a file name
* @param date Date to format
* @returns A string formatted as YYYY-MM-DD-HHMMSS
*/
function formatDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const h = String(date.getHours()).padStart(2, '0');
const m = String(date.getMinutes()).padStart(2, '0');
const s = String(date.getSeconds()).padStart(2, '0');

return JSON.stringify(metadata, stringifyReplacer(blacklist), 2);
return `${year}-${month}-${day}-${h}${m}${s}`;
}

/**
* Export support logs with the given name.
* @param fileNamePrefix The zip file name without the .zip extension. Ex: test will be saved as test.zip
* @param logHistory Log history to include in the console.txt file
* @param metadata Additional metadata to include in the metadata.json file
* @param blacklist List of JSON paths to blacklist. A JSON path is a list representing the path to that value (e.g. client.data would be `['client', 'data']`)
* @param reduxData Redux data to include in the redux.json file
* @param blacklist List of JSON paths to blacklist in redux data. A JSON path is a list representing the path to that value (e.g. client.data would be `['client', 'data']`)
* @param fileNamePrefix The zip file name without the .zip extension. Ex: test will be saved as test.zip
* @returns A promise that resolves successfully if the log archive is created and downloaded successfully, rejected if there's an error
*/
export async function exportLogs(
fileNamePrefix = `${dh.i18n.DateTimeFormat.format(
FILENAME_DATE_FORMAT,
new Date()
)}_support_logs`,
logHistory: LogHistory,
metadata?: Record<string, unknown>,
blacklist: string[][] = DEFAULT_PATH_BLACKLIST
reduxData?: Record<string, unknown>,
blacklist: string[][] = DEFAULT_PATH_BLACKLIST,
fileNamePrefix = `${formatDate(new Date())}_support_logs`
): Promise<void> {
const zip = new JSZip();
const folder = zip.folder(fileNamePrefix) as JSZip;
folder.file('console.txt', logHistory.getFormattedHistory());
folder.file('redux.json', getReduxDataString(blacklist));
folder.file('metadata.json', getMetadata(blacklist, metadata));
if (metadata != null) {
folder.file('metadata.json', getFormattedMetadata(metadata));
}
if (reduxData != null) {
folder.file('redux.json', getReduxDataString(reduxData, blacklist));
}

const blob = await zip.generateAsync({ type: 'blob' });
const link = document.createElement('a');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { LogProxy, LogHistory, Logger, Log } from '@deephaven/log';
import Log from './Log';
import type Logger from './Logger';
import LogHistory from './LogHistory';
import LogProxy from './LogProxy';

declare global {
interface Window {
Expand All @@ -11,10 +14,10 @@ declare global {
export const logProxy = new LogProxy();
export const logHistory = new LogHistory(logProxy);

export default function logInit(): void {
Log.setLogLevel(parseInt(import.meta.env.VITE_LOG_LEVEL ?? '', 10));
export function logInit(logLevel = 2, enableProxy = true): void {
Log.setLogLevel(logLevel);

if (import.meta.env.VITE_ENABLE_LOG_PROXY === 'true') {
if (enableProxy) {
logProxy.enable();
logHistory.enable();
}
Expand All @@ -26,3 +29,5 @@ export default function logInit(): void {
window.DHLogHistory = logHistory;
}
}

export default logInit;
2 changes: 2 additions & 0 deletions packages/log/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export { default as Logger } from './Logger';
export { default as LogHistory } from './LogHistory';
export { default as LogProxy } from './LogProxy';
export { default as LoggerLevel } from './LoggerLevel';
export * from './LogExport';
export * from './LogInit';

0 comments on commit fd17510

Please sign in to comment.