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

chore: Release 0.15.0-0 🚀 #201

Merged
merged 11 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# Make sure the actual branch is checked out when running on pull requests
ref: ${{ github.head_ref }}
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.15.0-0
- async/await `send()` support
- Upgrade dependencies
- Improvements in filter method
- Improvements in documentation

## 0.14.0
- Upgrade dependencies
- Support for Node v20
Expand Down
64 changes: 52 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,55 @@ Note that the Express middleware handler will pick up and transmit any `err` obj

## Documentation

### Callbacks
### Send

The `send()` function is asynchronous and returns a `Promise` of type `IncomingMessage`.

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.

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`.

#### Using `await`

Use `await` to obtain the `IncomingMessage`, remember to `catch` any possible thrown errors from the `send()` method.

```js
try {
let message = await client.send(error);
} catch (e) {
// error sending message
}
```

#### Using `then/catch`

You can also use `then()` to obtain the `IncomingMessage`, as well, use `catch()` to catch any possible thrown errors from the `send()` method.

```js
client.send(error)
.then((message) => {
// message sent to Raygun
})
.catch((error) => {
// error sending message
});
```

### Legacy `sendWithCallback`

```javascript
client.sendWithCallback(new Error(), {}, function (response){ });
```

The client still provides a legacy `send()` method that supports callbacks instead of `Promises`.

**This method is deprecated and will be removed in the future.**

The callback should be a node-style callback: `function(err, response) { /*...*/ }`.
*Note*: If the callback only takes one parameter (`function(response){ /*...*/ }`)
Expand All @@ -98,7 +146,7 @@ backwards compatibility; the Node-style callback should be preferred.
You can pass custom data in on the Send() function, as the second parameter. For instance (based off the call in test/raygun_test.js):

```javascript
client.send(new Error(), { 'mykey': 'beta' }, function (response){ });
client.send(new Error(), { 'mykey': 'beta' });
```

#### Sending custom data with Expressjs
Expand All @@ -113,19 +161,11 @@ raygunClient.expressCustomData = function (err, req) {
};
```

### Callback

```javascript
client.send(new Error(), {}, function (response){ });
```

The argument to the 3rd argument callback 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. 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

### Sending request data

You can send the request data in the Send() function, as the fourth parameter. For example:
```javascript
client.send(new Error(), {}, function () {}, request);
client.send(new Error(), {}, request);
```

If you want to filter any of the request data then you can pass in an array of keys to filter when
Expand All @@ -139,7 +179,7 @@ const raygunClient = new raygun.Client().init({ apiKey: 'YOUR_API_KEY', filters:

You can add tags to your error in the Send() function, as the fifth parameter. For example:
```javascript
client.send(new Error(), {}, function () {}, {}, ['Custom Tag 1', 'Important Error']);
client.send(new Error(), {}, {}, ['Custom Tag 1', 'Important Error']);
```

Tags can also be set globally using setTags
Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions examples/express-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Raygun + ExpressJS sample

This is a sample Express application to show how to use Raygun4Node and ExpressJS together.

This example uses the local `raygun4node` package in the project root directory by simply pointing to the root directory as a dependency in package.json:

```
"raygun": "file:../..",
```

## Run the sample

First, install the `raygun4node` package.

To do so, navigate to the project root directory, then:

npm install

Once the package is installed, set your API key in the sample's `config/default.json` and run:

npm install && npm start

in the subdirectory where you found this README.md file.

## Interesting files to look

- `app.js`
- Setup of Raygun (lines 9-12)
- Sets the user (lines 27-29)
- Attaches Raygun to Express (line 60)
- `routes/index.js`
- Tries to use a fake object, which bounces up to the Express handler (lines 11-15)
24 changes: 0 additions & 24 deletions examples/express-sample/readme.md

This file was deleted.

File renamed without changes.
21 changes: 11 additions & 10 deletions examples/using-domains/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@ var appDomain = require("domain").create();
// Add the error handler so we can pass errors to Raygun when the domain
// crashes
appDomain.on("error", function (err) {
try {
console.log(`Domain error caught: ${err}`);
// Try send data to Raygun
raygunClient.send(err, {}, function () {
console.log(`Domain error caught: ${err}`);
// Try send data to Raygun
raygunClient
.send(err)
.then((message) => {
// Exit the process once the error has been sent
console.log("Error sent to Raygun, exiting process");
process.exit(1);
})
.catch((error) => {
// If there was an error sending to Raygun, log it out and end the process.
// Could possibly log out to a text file here
console.log(error);
process.exit(1);
});
} catch (e) {
// If there was an error sending to Raygun, log it out and end the process.
// Could possibly log out to a text file here
console.log(e);
process.exit(1);
}
});

// Run the domain
Expand Down
6 changes: 6 additions & 0 deletions lib/raygun.batch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export class RaygunBatchTransport {
this.httpOptions = options.httpOptions;
}

/**
* Enqueues send request to batch processor.
* Callback in SendOptions is called when the message is eventually processed.
* @param options
*/
send(options: SendOptions) {
this.onIncomingMessage({
serializedMessage: options.message,
Expand Down Expand Up @@ -145,6 +150,7 @@ export class RaygunBatchTransport {
);
}

// TODO: Callbacks are processed in batch, see how can this be implemented with Promises
for (const callback of callbacks) {
if (callback) {
callVariadicCallback(callback, err, response);
Expand Down
29 changes: 24 additions & 5 deletions lib/raygun.messageBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,37 @@ type UserMessageData = RawUserData | string | undefined;
const humanString = require("object-to-human-string");
const packageDetails = require("../package.json");

function filterKeys(obj: object, filters: string[]): object {
/**
* Filter properties in obj according to provided filters.
* Also removes any recursive self-referencing object.
* @param obj object to apply filter
* @param filters list of keys to filter
* @param explored Set that contains already explored nodes, used internally
*/
function filterKeys(
obj: object,
filters: string[],
explored: Set<object> | null = null,
): object {
if (!obj || !filters || typeof obj !== "object") {
return obj;
}

// create or update the explored set with the incoming object
const _explored = explored?.add(obj) || new Set([obj]);

// Make temporary copy of the object to avoid mutating the original
// Cast to Record<string, object> to enforce type check and avoid using any
const _obj = { ...obj } as Record<string, object>;
Object.keys(obj).forEach(function (i) {
if (filters.indexOf(i) > -1) {
delete _obj[i];

Object.keys(obj).forEach(function (key) {
// Remove child if:
// - the key is in the filter array
// - the value is already in the explored Set
if (filters.indexOf(key) > -1 || _explored.has(_obj[key])) {
delete _obj[key];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we delete from the set? It could happen that a parent reference has two backwards references to it? I think the if side here should be a nop, so just execute the else part with the condition negated.

In other words, to avoid recursive cycles, an ancestor object could be referenced by more than one descend objects. So, always keep the ancestor object in the explored set

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, looking back into this, I see now that we do not delete the _explored set. All good, I will approve

} else {
_obj[i] = filterKeys(_obj[i], filters);
_obj[key] = filterKeys(_obj[key], filters, _explored);
}
});
return _obj;
Expand Down
1 change: 1 addition & 0 deletions lib/raygun.offline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class OfflineStorage implements IOfflineStorage {
path.join(this.cachePath, item),
"utf8",
(err, cacheContents) => {
// TODO: MessageTransport ignores any errors from the send callback, could this be improved?
this.transport.send(cacheContents);
fs.unlink(path.join(this.cachePath, item), () => {});
},
Expand Down
7 changes: 7 additions & 0 deletions lib/raygun.sync.transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@ function syncRequest(httpOptions: SendOptionsWithoutCB) {
console.log(requestProcess.stdout.toString());
}

/**
* Spawns a synchronous send request.
* Errors are not returned and callback is ignored.
* Only used to report uncaught exceptions.
* @param options
*/
export function send(options: SendOptionsWithoutCB) {
try {
syncRequest(options);
} catch (e) {
// TODO: Is there a reason we ignore errors here?
console.log(
`Raygun: error ${e} occurred while attempting to send error with message: ${options.message}`,
);
Expand Down
7 changes: 7 additions & 0 deletions lib/raygun.transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export function sendBatch(options: SendOptions) {
return send(options, BATCH_ENDPOINT);
}

// TODO: Convert this method callbacks to Promise.
/**
* Transport implementation that sends error to Raygun.
* Errors are reported back via callback.
* @param options
*/
export function send(options: SendOptions, path = DEFAULT_ENDPOINT) {
try {
const data = Buffer.from(options.message);
Expand Down Expand Up @@ -66,6 +72,7 @@ export function send(options: SendOptions, path = DEFAULT_ENDPOINT) {
request.write(data);
request.end();
} catch (e) {
// TODO: Non-HTTP errors are being ignored, should be better pass them up?
console.log(
`Raygun: error ${e} occurred while attempting to send error with message: ${options.message}`,
);
Expand Down
Loading