Skip to content

Commit 5bb7426

Browse files
committed
new client version based on oop paradigm
1 parent 0439977 commit 5bb7426

15 files changed

Lines changed: 418 additions & 405 deletions

File tree

lib/core/global.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
import { TraceoGlobal } from "../transport/base";
1+
import { Client } from "../node/client";
22

3-
export const getGlobalClientData = (): TraceoGlobal =>
4-
global["__TRACEO__"] || {};
5-
6-
export const setGlobalClientData = (data: TraceoGlobal): void => {
7-
global.__TRACEO__ = { ...data };
8-
};
3+
export const getGlobalClientData = (): Client => global["__TRACEO__"];

lib/core/http.ts

Lines changed: 63 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,80 @@
11
import * as http from "http";
2-
import { EventResponse, Incident } from "../transport/events";
3-
import {
4-
TraceoIncomingMessage,
5-
RequestOptions,
6-
HTTP_ENDPOINT,
7-
} from "../transport/http";
8-
import { TraceoLog } from "../transport/logger";
9-
import { Metrics } from "../transport/metrics";
2+
import * as https from "https";
3+
import { Client } from "../node/client";
104
import { RequestType } from "../transport/types";
11-
import { getGlobalClientData } from "./global";
12-
import { TRACEO_SDK_VERSION } from "./version";
135

14-
const createHttpOptions = (
15-
path: string,
16-
method: RequestType = "POST"
17-
): http.RequestOptions => {
18-
const client = getGlobalClientData();
19-
20-
const { host, port } = client.connection;
21-
const baseOptions: RequestOptions = {
22-
hostname: host,
23-
port,
24-
method,
25-
headers: {
26-
"Content-Type": "application/json",
27-
},
28-
};
29-
30-
return {
31-
path,
32-
...baseOptions,
33-
};
34-
};
35-
36-
const sendLog = async (log: TraceoLog) => {
37-
const httpOptions = createHttpOptions(HTTP_ENDPOINT.LOG);
38-
await sendEvent(log, httpOptions);
39-
};
40-
41-
const sendRuntimeMetrics = async (data: {}) => {
42-
const httpOptions = createHttpOptions(HTTP_ENDPOINT.RUNTIME);
43-
await sendEvent(data, httpOptions);
6+
type HttpRequestOptions = {
7+
callback?: (stream: http.IncomingMessage) => void;
8+
onError?: (error: Error) => void;
449
};
10+
export class HttpModule {
11+
host: string;
12+
url: URL;
13+
body: string;
14+
method: RequestType;
15+
16+
constructor(url: string, body?: {}, method: RequestType = "POST") {
17+
this.host = Client.config.url;
18+
19+
this.url = new URL(url, this.host);
20+
this.body = JSON.stringify(body);
21+
this.method = method;
22+
}
4523

46-
const sendMetrics = async (data: Metrics) => {
47-
const httpOptions = createHttpOptions(HTTP_ENDPOINT.METRICS);
48-
await sendEvent(data, httpOptions);
49-
};
24+
// TODO: implement callbacks from options later
25+
public request(_options?: HttpRequestOptions) {
26+
const requestOptions = {
27+
...this.requestHeaders(),
28+
...this.requestOptions(),
29+
};
5030

51-
const sendIncident = async (incident: Incident) => {
52-
const version = TRACEO_SDK_VERSION;
53-
const baseData = { version };
31+
const httpModule = this.requestModule();
5432

55-
const payload = Object.assign(incident, baseData);
33+
const request = httpModule.request(requestOptions);
34+
request.on("error", () => {});
5635

57-
const httpOptions = createHttpOptions(HTTP_ENDPOINT.INCIDENT);
58-
await sendEvent(payload, httpOptions);
59-
};
36+
this.requestWriteBody(request);
6037

61-
const sendEvent = async (
62-
payload: any,
63-
httpOptions: http.RequestOptions
64-
): Promise<EventResponse | void> => {
65-
const { connection, appId } = getGlobalClientData();
38+
request.end();
39+
}
6640

67-
if (!connection || !appId) {
68-
return;
41+
private requestWriteBody(request: http.ClientRequest) {
42+
if (this.method === "POST") {
43+
request.write(this.body);
44+
}
6945
}
7046

71-
return new Promise<EventResponse>((_, reject) => {
72-
const options: http.RequestOptions = {
73-
...httpOptions,
74-
path: `${httpOptions.path}/${appId}`,
75-
};
47+
private requestModule(): typeof http | typeof https {
48+
return this.clientURL.protocol == "http:" ? http : https;
49+
}
7650

77-
const request = http.request(options, (res: TraceoIncomingMessage) => {
78-
res.setEncoding("utf8");
79-
res.on("error", reject);
80-
});
51+
private get clientURL(): URL {
52+
return new URL(this.host);
53+
}
8154

82-
request.on("error", () => {});
55+
private requestOptions(): http.RequestOptions {
56+
const reqUrl = this.clientURL;
8357

84-
request.on("timeout", () => {
85-
request.destroy();
86-
reject({
87-
statusCode: 400,
88-
statusMessage:
89-
"[Traceo] Error during sending event to Traceo. Connection timeout.",
90-
});
91-
});
58+
return {
59+
protocol: reqUrl.protocol,
60+
port: reqUrl.port,
61+
host: reqUrl.hostname,
62+
method: this.method,
63+
path: this.path,
64+
};
65+
}
9266

93-
request.write(JSON.stringify(payload));
94-
request.end();
95-
});
96-
};
67+
private get path() {
68+
return `${this.url.pathname}/${Client.config.appId}`;
69+
}
9770

98-
export const httpService = {
99-
sendLog,
100-
sendIncident,
101-
sendRuntimeMetrics,
102-
sendMetrics,
103-
};
71+
private requestHeaders() {
72+
if (this.method != "POST") return {};
73+
return {
74+
headers: {
75+
"Content-Type": "application/json",
76+
"Content-Length": this.body.length,
77+
},
78+
};
79+
}
80+
}

lib/core/types/global.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { TraceoGlobal } from "../../transport/base";
1+
import { Client } from "../../node/client";
22

33
declare global {
4-
var __TRACEO__: TraceoGlobal;
4+
var __TRACEO__: Client;
55
}
66
export {};

lib/core/version.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
export const TRACEO_SDK_VERSION = "0.0.84";
1+
const pkg = require("../package.json");
2+
3+
export const TRACEO_SDK_VERSION = pkg.version;

lib/node/client.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { isEmpty } from "../core/is";
2+
import { TraceoOptions } from "../transport/options";
3+
import { Logger } from "./logger";
4+
import { metrics } from "./metrics";
5+
import { loadRuntimeMetrics } from "./metrics/runtime-data";
6+
7+
export class Client {
8+
options: TraceoOptions;
9+
readonly logger: Logger;
10+
11+
constructor(options: TraceoOptions) {
12+
this.configGlobalClient();
13+
14+
this.options = options;
15+
this.logger = new Logger();
16+
17+
if (!this.isOffline) {
18+
this.initSDK();
19+
}
20+
}
21+
22+
public static get client(): Client {
23+
return global.__TRACEO__;
24+
}
25+
26+
public static get logger(): Logger {
27+
return this.client.logger;
28+
}
29+
30+
public static get config(): TraceoOptions {
31+
return this.client.options;
32+
}
33+
34+
get isOffline(): boolean {
35+
return this.options.offline;
36+
}
37+
38+
get isCollectMetrics(): boolean {
39+
return this.options?.metrics?.collect;
40+
}
41+
42+
get isConnected(): boolean {
43+
return !isEmpty(global.__TRACEO__);
44+
}
45+
46+
private initSDK(): void {
47+
if (this.options.metrics.collect) {
48+
metrics.collectMetrics(this.options);
49+
}
50+
51+
loadRuntimeMetrics();
52+
}
53+
54+
private configGlobalClient(): void {
55+
global.__TRACEO__ = this;
56+
}
57+
}

lib/node/exceptions/handler.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { stacktrace } from "stacktrace-parser-node";
2+
import { HttpModule } from "../../core/http";
3+
import { isClientConnected } from "../../core/is";
4+
import { TraceoError } from "../../transport/base";
5+
import { Incident } from "../../transport/events";
6+
import { getOsPlatform } from "../helpers";
7+
8+
type Catch = {
9+
shouldBeCatched?: (error: any) => boolean;
10+
};
11+
12+
/**
13+
* For using in middleware and as an function in try/catch
14+
*
15+
* Base usage:
16+
* @example
17+
*
18+
* ```
19+
* } catch (error) {
20+
* catchException(error);
21+
* }
22+
* ```
23+
*
24+
* Usage with `shouldBeCatched`:
25+
* @example
26+
*
27+
* ```
28+
* catchException(error, {
29+
* shouldBeCatched: () => {
30+
* return true;
31+
* }
32+
* });
33+
* ```
34+
*
35+
*/
36+
export const catchException = async (error: any, catchOptions?: Catch) => {
37+
if (catchOptions?.shouldBeCatched !== undefined) {
38+
if (!catchOptions?.shouldBeCatched(error)) {
39+
return;
40+
}
41+
}
42+
43+
if (isClientConnected()) {
44+
await handleException(error);
45+
}
46+
47+
return;
48+
};
49+
50+
const handleException = async (error: TraceoError) => {
51+
const event: Incident = await prepareException(error);
52+
const httpModule = new HttpModule("/api/worker/incident", event);
53+
httpModule.request();
54+
};
55+
56+
const prepareException = async (error: TraceoError): Promise<Incident> => {
57+
const { stack } = error;
58+
const platform = getOsPlatform();
59+
60+
const { message, name, traces } = await stacktrace.parse(error);
61+
const event: Incident = {
62+
type: name,
63+
message,
64+
traces,
65+
stack: String(stack),
66+
platform,
67+
};
68+
69+
return event;
70+
};

lib/node/exceptions/middleware.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { isClientConnected, isLocalhost } from "../../core/is";
2+
import { TraceoError } from "../../transport/base";
3+
import {
4+
TraceoIncomingMessage,
5+
TraceoServerResponse,
6+
} from "../../transport/http";
7+
import { ErrorMiddlewareOptions } from "../../transport/options";
8+
import { getProtocol, getIp } from "../helpers";
9+
import { catchException } from "./handler";
10+
/**
11+
* Base middleware to catch and intercept error across the express app.
12+
* This middleware catch errors only from the no-async methods.
13+
*
14+
* To catch errors from async functions, we suggest to use `catchException` instead.
15+
*
16+
* The simplest way to use it in Express.js and JavaScript:
17+
*
18+
* @example
19+
* ```
20+
*
21+
* app.use(Middleware.errorMiddleware());
22+
* ```
23+
*
24+
* Using Express.js with Typescript, middleware must be cast to express.ErrorRequestHandler.
25+
*
26+
* @example
27+
* ```
28+
*
29+
* app.use(Middleware.errorMiddleware() as express.ErrorRequestHandler);
30+
* ```
31+
*
32+
*/
33+
export const errorMiddleware = (options: ErrorMiddlewareOptions = {}) => {
34+
return async function errorMiddleware(
35+
error: TraceoError,
36+
req: TraceoIncomingMessage,
37+
_res: TraceoServerResponse,
38+
next: (error: TraceoError) => void
39+
): Promise<void> {
40+
if (!isClientConnected()) {
41+
next(error);
42+
return;
43+
}
44+
45+
if (isToCatch(req, options)) {
46+
await catchException(error);
47+
}
48+
49+
next(error);
50+
};
51+
};
52+
53+
const isToCatch = (
54+
req: TraceoIncomingMessage,
55+
options: ErrorMiddlewareOptions = {}
56+
): boolean => {
57+
if (options.allowHttp !== undefined && !options.allowHttp) {
58+
const isSecure = getProtocol(req) === "https" ? true : false;
59+
if (!isSecure) {
60+
return false;
61+
}
62+
}
63+
64+
if (options.allowLocalhost !== undefined && !options.allowLocalhost) {
65+
if (isLocalhost(String(getIp(req)))) {
66+
return false;
67+
}
68+
}
69+
70+
return true;
71+
};

0 commit comments

Comments
 (0)