Skip to content

Commit 9282d45

Browse files
committed
parse stack moved to sdk from worker, using asynchronously fethcing context from internal files with fs
1 parent 5b56e49 commit 9282d45

7 files changed

Lines changed: 176 additions & 33 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "klepper",
3-
"version": "0.0.47-alpha",
3+
"version": "0.0.60-alpha",
44
"license": "MIT",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/core/http.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ const createHttpOptions = ({
4444
const statusFromCode = (code: number) =>
4545
code >= 200 && code <= 299 ? RequestStatus.SUCCESS : RequestStatus.ERROR;
4646

47-
export const sendConnection = (
48-
connectionData: KlepperReleaseEvent
49-
): void => {
47+
export const sendConnection = (connectionData: KlepperReleaseEvent): void => {
5048
const httpOptions = createHttpOptions({
5149
event: connectionData,
5250
api: "/sdk/release",

src/node/middlewares.ts

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { getGlobalClientData } from "../core/global";
21
import { sendEvent } from "../core/http";
32
import { isClientConnected, isLocalhost } from "../core/is";
43
import { KlepperError } from "../transport/base";
5-
import { CatchType } from "../transport/enums";
64
import { KlepperEvent } from "../transport/events";
75
import {
86
KlepperIncomingMessage,
@@ -12,7 +10,8 @@ import {
1210
CatchExceptionsOptions,
1311
ErrorMiddlewareOptions,
1412
} from "../transport/options";
15-
import { getIp, getProtocol, mapRequestData } from "./helpers";
13+
import { getIp, getProtocol } from "./helpers";
14+
import { prepareException } from "./parse";
1615

1716
/**
1817
* Base middleware to catch and intercept error across the express app.
@@ -38,19 +37,19 @@ import { getIp, getProtocol, mapRequestData } from "./helpers";
3837
*
3938
*/
4039
const errorMiddleware = (options: ErrorMiddlewareOptions = {}) => {
41-
return function errorMiddleware(
40+
return async function errorMiddleware(
4241
error: KlepperError,
4342
req: KlepperIncomingMessage,
4443
_res: KlepperServerResponse,
4544
next: (error: KlepperError) => void
46-
): void {
45+
): Promise<void> {
4746
if (!isClientConnected()) {
4847
next(error);
4948
return;
5049
}
5150

5251
if (isToCatch(req, options)) {
53-
handleException(error, req);
52+
await handleException(error, req);
5453
}
5554

5655
next(error);
@@ -125,22 +124,12 @@ const handleException = async (
125124
req?: KlepperIncomingMessage,
126125
options?: CatchExceptionsOptions
127126
) => {
128-
const { message, name } = error;
129-
130-
const event: KlepperEvent = {
131-
date: Date.now(),
132-
type: name,
133-
message,
134-
stack: error.stack as string,
135-
catchType: req ? CatchType.MIDDLEWARE : CatchType.INTERNAL,
136-
options,
137-
};
138-
139-
if (req !== undefined) {
140-
event.requestData = mapRequestData(req);
127+
try {
128+
const event: KlepperEvent = await prepareException(error, options, req);
129+
await sendEvent(event);
130+
} catch (err) {
131+
//
141132
}
142-
143-
await sendEvent(event);
144133
};
145134

146135
export const Middleware = {

src/node/parse.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { Trace } from "../transport/trace";
2+
import { promises, existsSync } from "fs";
3+
import { KlepperError } from "../transport/base";
4+
import { CatchExceptionsOptions, KlepperOptions } from "../transport/options";
5+
import { KlepperEvent } from "../transport/events";
6+
import { KlepperIncomingMessage } from "../transport/http";
7+
import { mapRequestData } from "./helpers";
8+
9+
const FULL_MATCH =
10+
/at (?:async )?(?:(.+?)\s+\()?(?:(.+?):(\d+)(?::(\d+))?|([^)]+))\)?/;
11+
12+
export const parseStackTraces = async (stack: string): Promise<Trace[]> => {
13+
const frames: Trace[] = [];
14+
15+
if (!stack.length) {
16+
return [];
17+
}
18+
19+
for (const line of stack.split("\n").slice(1)) {
20+
const frame = await createTrace(line);
21+
if (frame) {
22+
frames.push(frame);
23+
}
24+
}
25+
26+
return (
27+
frames.slice(0, 25).map((frame) => ({
28+
...frame,
29+
})) || []
30+
);
31+
};
32+
33+
export const createTrace = async (line: string): Promise<Trace | undefined> => {
34+
const lineMatch = line.match(FULL_MATCH);
35+
if (!lineMatch || lineMatch[0].includes("<anonymous>")) {
36+
return undefined;
37+
}
38+
39+
let functionName: string | undefined;
40+
let typeName: string | undefined;
41+
let methodName: string | undefined;
42+
let splitedPath: string[] | undefined;
43+
44+
if (lineMatch[1]) {
45+
functionName = lineMatch[1];
46+
}
47+
48+
if (functionName === undefined) {
49+
functionName = typeName ? `${typeName}.${methodName}` : "<anonymous>";
50+
}
51+
52+
const path = lineMatch[2]?.startsWith("file://")
53+
? lineMatch[2].substr(7)
54+
: lineMatch[2];
55+
const internal =
56+
path !== undefined &&
57+
!path.includes("node_modules/") &&
58+
!path.includes("node_modules\\") &&
59+
!path.includes("internal/");
60+
61+
const isNodeProcess = path?.includes("internal") || path?.includes("process");
62+
isNodeProcess
63+
? (splitedPath = path?.split("/"))
64+
: (splitedPath = path?.split("\\"));
65+
66+
const fileName = splitedPath[splitedPath?.length - 1];
67+
68+
const splitedFilename = fileName.split(".");
69+
const extension = splitedFilename[splitedFilename.length - 1];
70+
const lineNo = parseInt(lineMatch[3], 10);
71+
72+
const { code, preCode, postCode } = await getCodeFromFs(path, lineNo);
73+
74+
return {
75+
filename: fileName,
76+
function: functionName,
77+
absPath: path,
78+
lineNo,
79+
columnNo: parseInt(lineMatch[4], 10) || undefined,
80+
internal,
81+
extension,
82+
code,
83+
postCode,
84+
preCode,
85+
};
86+
};
87+
88+
const getCodeFromFs = async (
89+
path: string,
90+
codeLine: number
91+
): Promise<{ code: string; preCode: string[]; postCode: string[] }> => {
92+
let code: string = "";
93+
let preCode: string[] = [];
94+
let postCode: string[] = [];
95+
let linesOfCode: string[] = [];
96+
97+
const context = await readFileAsync(path);
98+
99+
if (context) {
100+
linesOfCode = context?.split("\n");
101+
102+
code = linesOfCode[codeLine - 1];
103+
preCode = linesOfCode.slice(codeLine - 6, codeLine - 1);
104+
postCode = linesOfCode.slice(codeLine + 1, codeLine + 6);
105+
}
106+
107+
return {
108+
code,
109+
preCode,
110+
postCode,
111+
};
112+
};
113+
114+
const readFileAsync = async (path: string): Promise<string> => {
115+
let context = "";
116+
117+
//check if file with this path exist
118+
const isFileExists = existsSync(path);
119+
if (isFileExists) {
120+
context = await promises.readFile(path, "utf8");
121+
}
122+
123+
return context;
124+
};
125+
126+
export const prepareException = async (error: KlepperError, options?: CatchExceptionsOptions, req?: KlepperIncomingMessage): Promise<KlepperEvent> => {
127+
const { message, name } = error;
128+
129+
const traces = await parseStackTraces(String(error?.stack));
130+
const event: KlepperEvent = {
131+
type: name,
132+
message,
133+
traces,
134+
stack: String(error.stack),
135+
options
136+
};
137+
138+
if (req !== undefined) {
139+
event.requestData = mapRequestData(req);
140+
}
141+
142+
return event;
143+
}

src/node/sdk.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ export const init = (
5454
env: options?.environment,
5555
version: options?.version,
5656
os: {
57-
arch: os.arch(),
57+
arch: os.arch(),
5858
platform: os.platform(),
5959
release: os.release(),
6060
version: os.version(),
61-
}
61+
},
6262
};
6363

6464
sendConnection(conn);

src/transport/events.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { CatchType, ExceptionPriority } from "./enums";
22
import { KlepperRequest } from "./http";
3+
import { Trace } from "./trace";
34
import { Environment } from "./types";
45

56
export interface EventResponse {
@@ -25,8 +26,8 @@ export interface KlepperEvent {
2526
projectId?: string;
2627
type: string;
2728
message: string;
28-
date: number;
2929
stack: string;
30+
traces: Trace[];
3031
requestData?: KlepperRequest;
3132
catchType?: CatchType;
3233
options?: {
@@ -42,9 +43,9 @@ export interface KlepperReleaseEvent {
4243
version?: string;
4344
env: Environment;
4445
os: {
45-
arch: string,
46-
platform: string,
47-
release: string,
48-
version: string,
49-
}
46+
arch: string;
47+
platform: string;
48+
release: string;
49+
version: string;
50+
};
5051
}

src/transport/trace.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface Trace {
2+
filename?: string;
3+
function?: string;
4+
lineNo?: number;
5+
columnNo?: number;
6+
internal?: boolean;
7+
absPath?: string;
8+
extension?: string;
9+
preCode?: string[];
10+
code?: string;
11+
postCode?: string[];
12+
}

0 commit comments

Comments
 (0)