Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
ref(node): Refactor node source fetching into integration#3729
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
31935f0781312ef143c10c8908098b30a9af1e3f316a03a2c7c164ddf362589da256ee04db3fb3324578443ea06dcd287d84155de285a6436a8a48bc4f48ad7f5793aac4318e82573f486e2d4e2ff7efd012eff3647999d2fe44fcaf9b1d8270e13e76d8998ddb840683f859b8bc20b018c57802266ac5bb37b6c03cb320965d48fe08bcbb7cd79aff278e8ba7d61dfef7d423a172765bd85ce9d7952eaFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| import { getCurrentHub } from '@sentry/core'; | ||
| import { Event, EventProcessor, Integration } from '@sentry/types'; | ||
| import { addContextToFrame } from '@sentry/utils'; | ||
| import { readFile } from 'fs'; | ||
| import { LRUMap } from 'lru_map'; | ||
| import { NodeClient } from '../client'; | ||
| const FILE_CONTENT_CACHE = new LRUMap<string, string | null>(100); | ||
| const DEFAULT_LINES_OF_CONTEXT = 7; | ||
| // TODO: Replace with promisify when minimum supported node >= v8 | ||
| function readTextFileAsync(path: string): Promise<string> { | ||
timfish marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return new Promise((resolve, reject) => { | ||
| readFile(path, 'utf8', (err, data) => { | ||
| if (err) reject(err); | ||
| else resolve(data); | ||
| }); | ||
| }); | ||
| } | ||
AbhiPrasad marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /** | ||
| * Resets the file cache. Exists for testing purposes. | ||
| * @hidden | ||
| */ | ||
| export function resetFileContentCache(): void { | ||
| FILE_CONTENT_CACHE.clear(); | ||
| } | ||
| interface ContextLinesOptions { | ||
| /** | ||
| * Sets the number of context lines for each frame when loading a file. | ||
| * Defaults to 7. | ||
| * | ||
| * Set to 0 to disable loading and inclusion of source files. | ||
| **/ | ||
| frameContextLines?: number; | ||
| } | ||
| /** Add node modules / packages to the event */ | ||
| export class ContextLines implements Integration { | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| public static id: string = 'ContextLines'; | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| public name: string = ContextLines.id; | ||
| public constructor(private readonly _options: ContextLinesOptions = {}) {} | ||
| /** | ||
| * @inheritDoc | ||
| */ | ||
| public setupOnce(addGlobalEventProcessor: (callback: EventProcessor) => void): void { | ||
| // This is only here to copy frameContextLines from init options if it hasn't | ||
| // been set via this integrations constructor. | ||
| // | ||
| // TODO: Remove on next major! | ||
| if (this._options.frameContextLines === undefined) { | ||
| const initOptions = getCurrentHub().getClient<NodeClient>()?.getOptions(); | ||
| // eslint-disable-next-line deprecation/deprecation | ||
| this._options.frameContextLines = initOptions?.frameContextLines; | ||
| } | ||
| const contextLines = | ||
| this._options.frameContextLines !== undefined ? this._options.frameContextLines : DEFAULT_LINES_OF_CONTEXT; | ||
timfish marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| addGlobalEventProcessor(event => this.addSourceContext(event, contextLines)); | ||
| } | ||
| /** Processes an event and adds context lines */ | ||
| public async addSourceContext(event: Event, contextLines: number): Promise<Event> { | ||
| const frames = event.exception?.values?.[0].stacktrace?.frames; | ||
| if (frames && contextLines > 0) { | ||
| const filenames: Set<string> = new Set(); | ||
| for (const frame of frames) { | ||
| if (frame.filename) { | ||
| filenames.add(frame.filename); | ||
| } | ||
| } | ||
| const sourceFiles = await readSourceFiles(filenames); | ||
| for (const frame of frames) { | ||
| if (frame.filename && sourceFiles[frame.filename]) { | ||
| try { | ||
| const lines = (sourceFiles[frame.filename] as string).split('\n'); | ||
timfish marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| addContextToFrame(lines, frame, contextLines); | ||
| } catch (e) { | ||
| // anomaly, being defensive in case | ||
| // unlikely to ever happen in practice but can definitely happen in theory | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return event; | ||
| } | ||
| } | ||
| /** | ||
| * This function reads file contents and caches them in a global LRU cache. | ||
| * | ||
| * @param filenames Array of filepaths to read content from. | ||
| */ | ||
| async function readSourceFiles(filenames: Set<string>): Promise<Record<string, string | null>> { | ||
| const sourceFiles: Record<string, string | null> = {}; | ||
| for (const filename of filenames) { | ||
| const cachedFile = FILE_CONTENT_CACHE.get(filename); | ||
| // We have a cache hit | ||
| if (cachedFile !== undefined) { | ||
| // If stored value is null, it means that we already tried, but couldn't read the content of the file. Skip. | ||
| if (cachedFile === null) { | ||
| continue; | ||
| } | ||
| // Otherwise content is there, so reuse cached value. | ||
| sourceFiles[filename] = cachedFile; | ||
| continue; | ||
| } | ||
| let content: string | null = null; | ||
| try { | ||
| content = await readTextFileAsync(filename); | ||
timfish marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } catch (_) { | ||
| // | ||
| } | ||
| FILE_CONTENT_CACHE.set(filename, content); | ||
| sourceFiles[filename] = content; | ||
| } | ||
| return sourceFiles; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.