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

feat: Add configurable timeouts #320

Merged
merged 10 commits into from
Oct 3, 2024
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,25 @@ The `send()` function is asynchronous and returns a `Promise` of type `IncomingM

Note that `IncomingMessage` can be `null` if the request was stored because the application was offline.

`IncomingMessage` is the response from the Raygun API - there's nothing in the body, it's just a status code response.
If everything went ok, you'll get a 202 response code.
Otherwise, we throw 401 for incorrect API keys, 403 if you're over your plan limits, or anything in the 500+ range for internal errors.
`IncomingMessage` is the response from the Raygun API - there's nothing in the body, it's just a status code response. If everything went ok, you'll get a 202 response code.
Otherwise, we return 401 for incorrect API keys, 403 if you're over your plan limits, or anything in the 500+ range for internal errors.

We use the nodejs http/https library to make the POST to Raygun, you can see more documentation about that callback here: https://nodejs.org/api/http.html#http_http_request_options_callback
We use the nodejs http/https library to make the POST to Raygun, you can see more documentation about that callback here: https://nodejs.org/api/http.html#http_http_request_options_callback .

You can `await` the call to obtain the result, or use `then/catch`.

The default timeout for the transport layer is 5000ms. You can override this value by setting a custom `timeout` (also in ms) when you initialize the Raygun client:

```typescript
import * as Raygun from 'raygun';

const raygunClient = new Raygun.Client().init({
apiKey: 'YOUR_API_KEY',
...
timeout: 3000 // defaults to 5000ms
});
```

#### Using `await`

Use `await` to obtain the `IncomingMessage`, remember to `catch` any possible thrown errors from the `send()` method.
Expand Down Expand Up @@ -418,7 +429,18 @@ If your application generates and reports large volumes of errors, especially in

You can control how often batches are processed and sent by providing a `batchFrequency` option, which is a number in milliseconds.

In a future version the batch transport will likely be enabled by default.
The default timeout for batch transport calls is 5000ms. You can override this value by setting a custom `timeout` (also in ms) when you initialize the Raygun client:

```typescript
import * as Raygun from 'raygun';

const raygunClient = new Raygun.Client().init({
apiKey: 'YOUR_API_KEY',
batch: true,
batchFrequency: 5000, // defaults to 1000ms (every second)
timeout: 10000 // defaults to 5000ms
});
```

### Offline caching

Expand Down
2 changes: 1 addition & 1 deletion lib/raygun.breadcrumbs.express.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function addRequestBreadcrumb(request: Request) {
};

debug(
`[raygun.breadcrumbs.express.ts] recorded request breadcrumb: ${internalCrumb}`,
`[raygun.breadcrumbs.express.ts] recorded request breadcrumb: ${JSON.stringify(internalCrumb, undefined, 2)}`,
);

crumbs.push(internalCrumb);
Expand Down
21 changes: 19 additions & 2 deletions lib/raygun.transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const API_HOST = "api.raygun.io";
const DEFAULT_ENDPOINT = "/entries";
const BATCH_ENDPOINT = "/entries/bulk";

const debug = require("debug")("raygun");

export function sendBatch(options: SendOptions): Promise<IncomingMessage> {
return send(options, BATCH_ENDPOINT);
}
Expand Down Expand Up @@ -57,12 +59,27 @@ export function send(
(response: IncomingMessage) => {
// request completed successfully
resolve(response);
// destroy the request after successful completion
request.destroy();
debug(
`[raygun.transport.ts] Request destroyed for message: ${options.message}`,
);
},
);

if (options.http.timeout) {
debug(`[raygun.transport.ts] Timeout set: ${options.http.timeout}ms`);
request.setTimeout(options.http.timeout, () => {
console.error(
`[Raygun4Node] request timed out while attempting to send error with message: ${options.message}`,
);
request.destroy(new Error("Request timed out"));
});
}

request.on("error", function (e) {
console.error(
`[Raygun4Node] error ${e.message} occurred while attempting to send error with message: ${options.message}`,
`[Raygun4Node] Error with details "${e.message}" occurred while attempting to send error with message: ${options.message}`,
);

// request failed
Expand All @@ -74,7 +91,7 @@ export function send(
});
} catch (e) {
console.error(
`[Raygun4Node] error ${e} occurred while attempting to send error with message: ${options.message}`,
`[Raygun4Node] Error "${e}" occurred while attempting to send error with message: ${options.message}`,
);
return Promise.reject(e);
}
Expand Down
9 changes: 8 additions & 1 deletion lib/raygun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ try {
type SendCB = (error: Error | null, items: string[] | undefined) => void;

const DEFAULT_BATCH_FREQUENCY = 1000; // ms
const DEFAULT_TIMEOUT = 5000; // ms

function emptyCallback() {}

Expand All @@ -79,6 +80,8 @@ class Raygun {

_useSSL: boolean | undefined;

_timeout: number | undefined;

_onBeforeSend: Hook<Message | null> | undefined;

_offlineStorage: IOfflineStorage | undefined;
Expand Down Expand Up @@ -118,6 +121,7 @@ class Raygun {
this._port = options.port;
this._useSSL = options.useSSL !== false;
this._onBeforeSend = options.onBeforeSend;
this._timeout = options.timeout;
this._isOffline = options.isOffline;
this._groupingKey = options.groupingKey;
this._tags = options.tags;
Expand All @@ -144,6 +148,7 @@ class Raygun {
port: this._port,
useSSL: !!this._useSSL,
apiKey: this._apiKey,
timeout: this._timeout || DEFAULT_TIMEOUT,
},
});
}
Expand Down Expand Up @@ -570,7 +575,8 @@ class Raygun {
host: this._host,
port: this._port,
useSSL: !!this._useSSL,
apiKey,
apiKey: apiKey,
timeout: this._timeout || DEFAULT_TIMEOUT,
},
},
};
Expand All @@ -583,6 +589,7 @@ class Raygun {
port: this._port,
useSSL: this._useSSL || false,
apiKey: this._apiKey || "",
timeout: this._timeout || DEFAULT_TIMEOUT,
};

return {
Expand Down
2 changes: 2 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export type HTTPOptions = {
host: string | undefined;
port: number | undefined;
apiKey: string;
timeout?: number;
};

// Allow any because users are free to set anything as CustomData
Expand Down Expand Up @@ -179,6 +180,7 @@ export type RaygunOptions = {
host?: string;
port?: number;
useSSL?: boolean;
timeout?: number;
onBeforeSend?: Hook<Message>;
offlineStorage?: IOfflineStorage;
offlineStorageOptions?: OfflineStorageOptions;
Expand Down
18 changes: 18 additions & 0 deletions test/raygun_async_send_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ test("async send complex", {}, function (t) {
});
});

test("async send complex with timeout", {}, function (t) {
t.plan(1);

let client = new Raygun.Client()
.init({ apiKey: API_KEY, timeout: 5000 })
.setVersion("1.0.0.0");

client
.send(new Error())
.then((response) => {
t.equal(response.statusCode, 202);
t.end();
})
.catch((err) => {
t.fail(err);
});
});

test("async send with inner error", {}, function (t) {
t.plan(1);

Expand Down