Skip to content

sdk-core, browser, node: add BacktraceApi #335

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

Merged
merged 10 commits into from
Feb 7, 2025
15 changes: 15 additions & 0 deletions packages/browser/src/BacktraceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { BacktraceCoreApi, BacktraceCoreApiOptions } from '@backtrace/sdk-core';
import {
BacktraceBrowserRequestHandler,
BacktraceBrowserRequestHandlerOptions,
} from './BacktraceBrowserRequestHandler.js';

export interface BacktraceApiOptions extends BacktraceCoreApiOptions {
readonly requestHandlerOptions?: BacktraceBrowserRequestHandlerOptions;
}

export class BacktraceApi extends BacktraceCoreApi {
constructor(options: BacktraceApiOptions) {
super(options, options.requestHandler ?? new BacktraceBrowserRequestHandler(options.requestHandlerOptions));
}
}
14 changes: 6 additions & 8 deletions packages/browser/src/BacktraceBrowserRequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
DEFAULT_TIMEOUT,
} from '@backtrace/sdk-core';

export interface BacktraceBrowserRequestHandlerOptions {
readonly timeout?: number;
}

export class BacktraceBrowserRequestHandler implements BacktraceRequestHandler {
private readonly UPLOAD_FILE_NAME = 'upload_file';
private readonly _timeout: number;
Expand All @@ -18,14 +22,8 @@ export class BacktraceBrowserRequestHandler implements BacktraceRequestHandler {
private readonly MULTIPART_HEADERS = {
'Transfer-Encoding': 'chunked',
};
constructor(
private readonly _options: {
url: string;
token?: string;
timeout?: number;
},
) {
this._timeout = this._options.timeout ?? DEFAULT_TIMEOUT;
constructor(private readonly _options?: BacktraceBrowserRequestHandlerOptions) {
this._timeout = this._options?.timeout ?? DEFAULT_TIMEOUT;
}
public async postError<T>(
submissionUrl: string,
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ export {
BacktraceStringAttachment,
BacktraceUint8ArrayAttachment,
BreadcrumbLogLevel,
BreadcrumbType,
BreadcrumbsEventSubscriber,
BreadcrumbsManager,
BreadcrumbType,
JavaScriptEngine,
RawBreadcrumb,
SingleSessionProvider,
V8StackTraceConverter,
} from '@backtrace/sdk-core';
export * from './agentDefinition.js';
export * from './BacktraceApi.js';
export * from './BacktraceBrowserRequestHandler.js';
export * from './BacktraceClient.js';
export * from './BacktraceConfiguration.js';
Expand Down
12 changes: 12 additions & 0 deletions packages/node/src/BacktraceApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BacktraceCoreApi, BacktraceCoreApiOptions } from '@backtrace/sdk-core';
import { BacktraceNodeRequestHandler, BacktraceNodeRequestHandlerOptions } from './BacktraceNodeRequestHandler.js';

export interface BacktraceApiOptions extends BacktraceCoreApiOptions {
readonly requestHandlerOptions?: BacktraceNodeRequestHandlerOptions;
}

export class BacktraceApi extends BacktraceCoreApi {
constructor(options: BacktraceApiOptions) {
super(options, options.requestHandler ?? new BacktraceNodeRequestHandler(options.requestHandlerOptions));
}
}
21 changes: 10 additions & 11 deletions packages/node/src/BacktraceNodeRequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import FormData from 'form-data';
import http, { ClientRequest, IncomingMessage } from 'http';
import https from 'https';
import { Readable } from 'stream';

export interface BacktraceNodeRequestHandlerOptions {
readonly timeout?: number;
readonly ignoreSslCertificate?: boolean;
}

export class BacktraceNodeRequestHandler implements BacktraceRequestHandler {
private readonly UPLOAD_FILE_NAME = 'upload_file';
private readonly _timeout: number;
Expand All @@ -24,15 +30,8 @@ export class BacktraceNodeRequestHandler implements BacktraceRequestHandler {
'Transfer-Encoding': 'chunked',
};

constructor(
private readonly _options: {
url: string;
token?: string;
timeout?: number;
ignoreSslCertificate?: boolean;
},
) {
this._timeout = this._options.timeout ?? DEFAULT_TIMEOUT;
constructor(private readonly _options?: BacktraceNodeRequestHandlerOptions) {
this._timeout = this._options?.timeout ?? DEFAULT_TIMEOUT;
}

public async postError(
Expand Down Expand Up @@ -71,7 +70,7 @@ export class BacktraceNodeRequestHandler implements BacktraceRequestHandler {
const request = httpClient.request(
url,
{
rejectUnauthorized: this._options.ignoreSslCertificate === true,
rejectUnauthorized: this._options?.ignoreSslCertificate === true,
timeout: this._timeout,
method: 'POST',
},
Expand Down Expand Up @@ -129,7 +128,7 @@ export class BacktraceNodeRequestHandler implements BacktraceRequestHandler {
const request = httpClient.request(
url,
{
rejectUnauthorized: this._options.ignoreSslCertificate === true,
rejectUnauthorized: this._options?.ignoreSslCertificate === true,
timeout: this._timeout,
method: 'POST',
headers:
Expand Down
1 change: 1 addition & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
RawBreadcrumb,
} from '@backtrace/sdk-core';
export * from './attachment/index.js';
export * from './BacktraceApi.js';
export * from './BacktraceClient.js';
export * from './BacktraceConfiguration.js';
export * from './BacktraceNodeRequestHandler.js';
Expand Down
97 changes: 97 additions & 0 deletions packages/sdk-core/src/BacktraceCoreApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { BacktraceAttachment } from './model/attachment/BacktraceAttachment.js';
import {
BacktraceAttachmentResponse,
BacktraceReportSubmissionResult,
BacktraceRequestHandler,
BacktraceSubmitBody,
BacktraceSubmitResponse,
RequestBacktraceReportSubmission,
} from './model/http/index.js';
import {
BacktraceSubmitSummedMetricsBody,
BacktraceSubmitUniqueMetricsBody,
} from './model/http/model/metric/request/BacktraceSubmitMetricsBody.js';
import { MetricsUrlInformation } from './modules/metrics/MetricsUrlInformation.js';

export interface BacktraceCoreApiOptions {
readonly url: string;
readonly token?: string;

readonly metrics?: {
readonly url?: string;
};

readonly requestHandler?: BacktraceRequestHandler;
readonly requestBacktraceReportSubmission?: RequestBacktraceReportSubmission;
}

export class BacktraceCoreApi {
private readonly _summedMetricsSubmissionUrl?: string;
private readonly _uniqueMetricsSubmissionUrl?: string;

private readonly _requestHandler: BacktraceRequestHandler;
private readonly _requestBacktraceReportSubmission: RequestBacktraceReportSubmission;

constructor(options: BacktraceCoreApiOptions, requestHandler: BacktraceRequestHandler) {
this._summedMetricsSubmissionUrl = MetricsUrlInformation.generateSummedEventsUrl(
options.metrics?.url ?? 'https://events.backtrace.io',
options.url,
options.token,
);

this._uniqueMetricsSubmissionUrl = MetricsUrlInformation.generateUniqueEventsUrl(
options.metrics?.url ?? 'https://events.backtrace.io',
options.url,
options.token,
);

this._requestHandler = options.requestHandler ?? requestHandler;

this._requestBacktraceReportSubmission =
options.requestBacktraceReportSubmission ??
new RequestBacktraceReportSubmission(
{
url: options.url,
},
this._requestHandler,
);
}

public sendReport(
data: BacktraceSubmitBody,
attachments: BacktraceAttachment[],
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<BacktraceSubmitResponse>> {
return this._requestBacktraceReportSubmission.send(data, attachments, abortSignal);
}

public sendAttachment(
rxid: string,
attachment: BacktraceAttachment,
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<BacktraceAttachmentResponse>> {
return this._requestBacktraceReportSubmission.sendAttachment(rxid, attachment, abortSignal);
}

public sendUniqueMetrics(
metrics: BacktraceSubmitUniqueMetricsBody,
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<unknown>> {
if (!this._uniqueMetricsSubmissionUrl) {
throw new Error('Unique metrics URL is not available.');
}

return this._requestHandler.post(this._uniqueMetricsSubmissionUrl, JSON.stringify(metrics), abortSignal);
}

public sendSummedMetrics(
metrics: BacktraceSubmitSummedMetricsBody,
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<unknown>> {
if (!this._summedMetricsSubmissionUrl) {
throw new Error('Summed metrics URL is not available.');
}

return this._requestHandler.post(this._summedMetricsSubmissionUrl, JSON.stringify(metrics), abortSignal);
}
}
1 change: 1 addition & 0 deletions packages/sdk-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './BacktraceCoreApi.js';
export * from './BacktraceCoreClient.js';
export * from './builder/BacktraceCoreClientBuilder.js';
export * from './builder/CoreClientSetup.js';
Expand Down
13 changes: 6 additions & 7 deletions packages/sdk-core/src/model/http/BacktraceReportSubmission.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { jsonEscaper } from '../../common/jsonEscaper.js';
import { BacktraceAttachment } from '../attachment/index.js';
import { BacktraceConfiguration } from '../configuration/BacktraceConfiguration.js';
import { BacktraceData } from '../data/BacktraceData.js';
import { BacktraceReportSubmissionResult } from '../data/BacktraceSubmissionResult.js';
import { BacktraceRequestHandler } from './BacktraceRequestHandler.js';
import { BacktraceAttachmentResponse } from './model/BacktraceAttachmentResponse.js';
import { BacktraceSubmissionResponse } from './model/BacktraceSubmissionResponse.js';
import { BacktraceAttachmentResponse } from './model/attachment/response/BacktraceAttachmentResponse.js';
import { BacktraceSubmissionResponse } from './model/submit/index.js';
import { BacktraceSubmitBody } from './model/submit/request/BacktraceSubmitBody.js';
import { SubmissionUrlInformation } from './SubmissionUrlInformation.js';

export interface BacktraceReportSubmission {
send(
data: BacktraceData,
data: BacktraceSubmitBody,
attachments: BacktraceAttachment[],
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<BacktraceSubmissionResponse>>;
Expand All @@ -25,13 +24,13 @@ export interface BacktraceReportSubmission {
export class RequestBacktraceReportSubmission implements BacktraceReportSubmission {
private readonly _submissionUrl: string;
constructor(
options: BacktraceConfiguration,
options: { url: string; token?: string },
private readonly _requestHandler: BacktraceRequestHandler,
) {
this._submissionUrl = SubmissionUrlInformation.toJsonReportSubmissionUrl(options.url, options.token);
}

public send(data: BacktraceData, attachments: BacktraceAttachment[], abortSignal?: AbortSignal) {
public send(data: BacktraceSubmitBody, attachments: BacktraceAttachment[], abortSignal?: AbortSignal) {
const json = JSON.stringify(data, jsonEscaper());
return this._requestHandler.postError(this._submissionUrl, json, attachments, abortSignal);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/sdk-core/src/model/http/BacktraceRequestHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BacktraceAttachment } from '../attachment/index.js';
import { BacktraceReportSubmissionResult } from '../data/BacktraceSubmissionResult.js';
import { BacktraceAttachmentResponse } from './model/BacktraceAttachmentResponse.js';
import { BacktraceSubmissionResponse } from './model/BacktraceSubmissionResponse.js';
import { BacktraceAttachmentResponse } from './model/attachment/response/BacktraceAttachmentResponse.js';
import { BacktraceSubmitResponse } from './model/submit/index.js';
export const DEFAULT_TIMEOUT = 15_000;
export interface BacktraceRequestHandler {
/**
Expand All @@ -17,7 +17,7 @@ export interface BacktraceRequestHandler {
dataJson: string,
attachments: BacktraceAttachment[],
abortSignal?: AbortSignal,
): Promise<BacktraceReportSubmissionResult<BacktraceSubmissionResponse>>;
): Promise<BacktraceReportSubmissionResult<BacktraceSubmitResponse>>;

/**
* Post data to Backtrace API
Expand Down
5 changes: 3 additions & 2 deletions packages/sdk-core/src/model/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from '../data/BacktraceSubmissionResult.js';
export * from './BacktraceReportSubmission.js';
export * from './BacktraceRequestHandler.js';
export * from './common/ConnectionError.js';
export * from './model/BacktraceAttachmentResponse.js';
export * from './model/BacktraceSubmissionResponse.js';
export * from './model/attachment/index.js';
export * from './model/metric/index.js';
export * from './model/submit/index.js';
export * from './SubmissionUrlInformation.js';
1 change: 1 addition & 0 deletions packages/sdk-core/src/model/http/model/attachment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './response/BacktraceAttachmentResponse.js';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BacktraceSubmissionResponse } from './BacktraceSubmissionResponse.js';
import { BacktraceSubmitResponse } from '../../submit/index.js';

export interface BacktraceAttachmentResponse extends BacktraceSubmissionResponse {
export interface BacktraceAttachmentResponse extends BacktraceSubmitResponse {
attachment_name: string;
attachment_id: string;
object: string;
Expand Down
2 changes: 2 additions & 0 deletions packages/sdk-core/src/model/http/model/metric/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './request/BacktraceSubmitMetricEventBody.js';
export * from './request/BacktraceSubmitMetricsBody.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { AttributeType } from '../../../../data/BacktraceData.js';

export interface BacktraceSubmitSummedMetricEventBody {
timestamp: number;
attributes: Record<string, AttributeType>;
metric_group: string;
}

export interface BacktraceSubmitUniqueMetricEventBody {
timestamp: number;
attributes: Record<string, AttributeType>;
unique: string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {
BacktraceSubmitSummedMetricEventBody,
BacktraceSubmitUniqueMetricEventBody,
} from './BacktraceSubmitMetricEventBody.js';

export interface BacktraceSubmitMetricsMetadataBody {
dropped_events?: number;
}

export type BacktraceSubmitSummedMetricsBody = {
application: string;
appversion: string;
metadata?: BacktraceSubmitMetricsMetadataBody;
summed_events: BacktraceSubmitSummedMetricEventBody[];
};

export type BacktraceSubmitUniqueMetricsBody = {
application: string;
appversion: string;
metadata?: BacktraceSubmitMetricsMetadataBody;
unique_events: BacktraceSubmitUniqueMetricEventBody[];
};
10 changes: 10 additions & 0 deletions packages/sdk-core/src/model/http/model/submit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export * from './request/BacktraceSubmitArch.js';
export * from './request/BacktraceSubmitAttributeType.js';
export * from './request/BacktraceSubmitBody.js';
export * from './request/BacktraceSubmitMemory.js';
export * from './request/BacktraceSubmitModule.js';
export * from './request/BacktraceSubmitSourceCode.js';
export * from './request/BacktraceSubmitStackFrame.js';
export * from './request/BacktraceSubmitThread.js';

export * from './response/BacktraceSubmitResponse.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface BacktraceSubmitArch {
/**
* On some systems the running program can be run with a different arch than the system itself.
* `attributes.uname.machine` has to do with the system arch;
* this field has to do with the running process arch.
*/
name: string;

/**
* It corresponds with registers in the stack frame. Specifies the names of the registers for this arch.
* The values are the types.
*
* If you use `string`, you can format the value as you want.
*/
registers: 'i32' | 'u32' | 'i64' | 'u64' | 'f32' | 'string';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type BacktraceSubmitAttributeType = string | number | boolean | undefined | null;
Loading