Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telemetry: Add platform info to telemetry event #24081

Merged
merged 7 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
10 changes: 7 additions & 3 deletions code/lib/core-events/src/errors/manager-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ export class UncaughtManagerError extends StorybookError {

readonly code = 1;

constructor(public error: Error) {
super(error.message);
this.stack = error.stack;
constructor(
public data: {
error: Error;
}
) {
super(data.error.message);
this.stack = data.error.stack;
}

template() {
Expand Down
4 changes: 2 additions & 2 deletions code/lib/core-server/src/withTelemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ describe('sendTelemetryError', () => {
error: mockError,
eventType,
isErrorInstance: false,
errorHash: 'no-message',
errorHash: 'NO_MESSAGE',
}),
expect.any(Object)
);
Expand All @@ -338,7 +338,7 @@ describe('sendTelemetryError', () => {
error: mockError,
eventType,
isErrorInstance: true,
errorHash: 'empty-message',
errorHash: 'EMPTY_MESSAGE',
}),
expect.any(Object)
);
Expand Down
14 changes: 4 additions & 10 deletions code/lib/core-server/src/withTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,28 +83,22 @@ export async function sendTelemetryError(
const error = _error as Error | Record<string, any>;

let storybookErrorProperties = {};
yannbf marked this conversation as resolved.
Show resolved Hide resolved
// if it's an UNCATEGORIZED error, it won't have a coded name, so we just pass the category and source
if ((error as any).category) {
const { category } = error as any;
storybookErrorProperties = {
category,
};
}

if ((error as any).fromStorybook) {
const { code, name } = error as any;
const { code, name, category } = error as any;
storybookErrorProperties = {
...storybookErrorProperties,
code,
name,
category,
};
}

let errorHash;
if ('message' in error) {
errorHash = error.message ? oneWayHash(error.message) : 'empty-message';
errorHash = error.message ? oneWayHash(error.message) : 'EMPTY_MESSAGE';
} else {
errorHash = 'no-message';
errorHash = 'NO_MESSAGE';
}

await telemetry(
Expand Down
1 change: 1 addition & 0 deletions code/lib/preview/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@storybook/core-events": "workspace:*",
"@storybook/global": "^5.0.0",
"@storybook/preview-api": "workspace:*",
"browser-dtector": "^3.4.0",
yannbf marked this conversation as resolved.
Show resolved Hide resolved
"typescript": "~4.9.3"
},
"publishConfig": {
Expand Down
3 changes: 2 additions & 1 deletion code/lib/preview/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { global } from '@storybook/global';

import { values } from './globals/runtime';
import { globals } from './globals/types';
import { preprocessError } from './utils';

const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;

Expand All @@ -13,7 +14,7 @@ getKeys(globals).forEach((key) => {

global.sendTelemetryError = (error: any) => {
const channel = global.__STORYBOOK_ADDONS_CHANNEL__;
channel.emit(TELEMETRY_ERROR, error);
channel.emit(TELEMETRY_ERROR, preprocessError(error));
};

// handle all uncaught StorybookError at the root of the application and log to telemetry if applicable
Expand Down
29 changes: 29 additions & 0 deletions code/lib/preview/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { global } from '@storybook/global';
import type { BrowserInfo } from 'browser-dtector';
import BrowserDetector from 'browser-dtector';

let browserInfo: BrowserInfo | undefined;

function getBrowserInfo() {
if (!browserInfo) {
browserInfo = new BrowserDetector(global.navigator?.userAgent).getBrowserInfo();
}

return browserInfo;
}

export function preprocessError(
yannbf marked this conversation as resolved.
Show resolved Hide resolved
error: Error & {
fromStorybook?: boolean;
category?: string;
target?: any;
currentTarget?: any;
srcElement?: any;
browserInfo?: BrowserInfo;
}
) {
// eslint-disable-next-line no-param-reassign
error.browserInfo = getBrowserInfo();

return error;
}
22 changes: 22 additions & 0 deletions code/lib/telemetry/src/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="node" />

import * as os from 'os';
import originalFetch from 'node-fetch';
import retry from 'fetch-retry';
import { nanoid } from 'nanoid';
Expand All @@ -18,12 +19,33 @@ export const addToGlobalContext = (key: string, value: any) => {
globalContext[key] = value;
};

const getOperatingSystem = (): 'Windows' | 'macOS' | 'Linux' | `Other: ${string}` | 'Unknown' => {
try {
const platform = os.platform();

if (platform === 'win32') {
return 'Windows';
}
if (platform === 'darwin') {
return 'macOS';
}
if (platform === 'linux') {
return 'Linux';
}

return `Other: ${platform}`;
} catch (_err) {
return 'Unknown';
}
};

// context info sent with all events, provided
// by the app. currently:
// - cliVersion
const globalContext = {
inCI: Boolean(process.env.CI),
isTTY: process.stdout.isTTY,
platform: getOperatingSystem(),
} as Record<string, any>;

const prepareRequest = async (data: TelemetryData, context: Record<string, any>, options: any) => {
Expand Down
1 change: 1 addition & 0 deletions code/ui/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@storybook/types": "workspace:*",
"@testing-library/react": "^11.2.2",
"@types/semver": "^7.3.4",
"browser-dtector": "^3.4.0",
"copy-to-clipboard": "^3.3.1",
"downshift": "^6.0.15",
"fs-extra": "^11.1.0",
Expand Down
30 changes: 1 addition & 29 deletions code/ui/manager/src/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable local-rules/no-uncategorized-errors */

import { global } from '@storybook/global';

import type { Channel } from '@storybook/channels';
Expand All @@ -8,12 +6,12 @@ import { addons } from '@storybook/manager-api';
import type { Addon_Types, Addon_Config } from '@storybook/types';
import { createBrowserChannel } from '@storybook/channels';
import { CHANNEL_CREATED, TELEMETRY_ERROR } from '@storybook/core-events';
import { UncaughtManagerError } from '@storybook/core-events/manager-errors';
import Provider from './provider';
import { renderStorybookUI } from './index';

import { values } from './globals/runtime';
import { Keys } from './globals/types';
import { preprocessError } from './utils/preprocessError';

const { FEATURES, CONFIG_TYPE } = global;

Expand Down Expand Up @@ -64,32 +62,6 @@ Object.keys(Keys).forEach((key: keyof typeof Keys) => {
global[Keys[key]] = values[key];
});

function preprocessError(
originalError: Error & {
fromStorybook?: boolean;
category?: string;
target?: any;
currentTarget?: any;
srcElement?: any;
}
) {
let error = originalError;

if (!originalError.fromStorybook) {
error = new UncaughtManagerError(originalError);
}

// DOM manipulation errors and other similar errors are not serializable as they contain
// circular references to the window object. If that's the case, we make a simplified copy
if (error.target === window || error.currentTarget === window || error.srcElement === window) {
error = new Error(originalError.message);
error.name = originalError.name || error.name;
error.category = originalError.category;
}

return error;
}

global.sendTelemetryError = (error) => {
const channel = global.__STORYBOOK_ADDONS_CHANNEL__;
channel.emit(TELEMETRY_ERROR, preprocessError(error));
Expand Down
47 changes: 47 additions & 0 deletions code/ui/manager/src/utils/preprocessError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable local-rules/no-uncategorized-errors */
import { UncaughtManagerError } from '@storybook/core-events/manager-errors';
import { global } from '@storybook/global';
import type { BrowserInfo } from 'browser-dtector';
import BrowserDetector from 'browser-dtector';

let browserInfo: BrowserInfo | undefined;

function getBrowserInfo() {
if (!browserInfo) {
browserInfo = new BrowserDetector(global.navigator?.userAgent).getBrowserInfo();
}

return browserInfo;
}

export function preprocessError(
originalError: Error & {
fromStorybook?: boolean;
category?: string;
target?: any;
currentTarget?: any;
srcElement?: any;
browserInfo?: BrowserInfo;
}
) {
let error = originalError;

// DOM manipulation errors and other similar errors are not serializable as they contain
// circular references to the window object. If that's the case, we make a simplified copy
if (
originalError.target === global ||
originalError.currentTarget === global ||
originalError.srcElement === global
) {
error = new Error(originalError.message);
error.name = originalError.name || error.name;
}

if (!originalError.fromStorybook) {
error = new UncaughtManagerError({ error });
}

error.browserInfo = getBrowserInfo();

return error;
}
9 changes: 9 additions & 0 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7249,6 +7249,7 @@ __metadata:
"@storybook/types": "workspace:*"
"@testing-library/react": ^11.2.2
"@types/semver": ^7.3.4
browser-dtector: ^3.4.0
copy-to-clipboard: ^3.3.1
downshift: ^6.0.15
fs-extra: ^11.1.0
Expand Down Expand Up @@ -7650,6 +7651,7 @@ __metadata:
"@storybook/core-events": "workspace:*"
"@storybook/global": ^5.0.0
"@storybook/preview-api": "workspace:*"
browser-dtector: ^3.4.0
typescript: ~4.9.3
languageName: unknown
linkType: soft
Expand Down Expand Up @@ -12284,6 +12286,13 @@ __metadata:
languageName: node
linkType: hard

"browser-dtector@npm:^3.4.0":
version: 3.4.0
resolution: "browser-dtector@npm:3.4.0"
checksum: b2586d2fdccd9ab992b6cc254a65f10d54137b50edfd70bf80ecf80e8e7761e78482e10d7c3874609ab9b602bc6da7466a01b254d40ec721d341c723589aa288
languageName: node
linkType: hard

"browser-process-hrtime@npm:^1.0.0":
version: 1.0.0
resolution: "browser-process-hrtime@npm:1.0.0"
Expand Down