Skip to content

Commit

Permalink
Merge pull request #201 from MindscapeHQ/develop
Browse files Browse the repository at this point in the history
chore: Release 0.15.0-0 🚀
  • Loading branch information
miquelbeltran authored May 9, 2024
2 parents da8b899 + ad719b4 commit 28abe41
Show file tree
Hide file tree
Showing 28 changed files with 533 additions and 204 deletions.
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)
4 changes: 3 additions & 1 deletion examples/express-sample/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
var config = require("config");

if (config.Raygun.Key === "YOUR_API_KEY") {
console.error("You need to set your Raygun API key in the config file");
console.error(
`[Raygun4Node-Express-Sample] You need to set your Raygun API key in the config file`,
);
process.exit(1);
}

Expand Down
4 changes: 2 additions & 2 deletions examples/express-sample/bin/www
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,11 @@ function onError(error) {
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
console.error(`[Raygun4Node-Express-Sample] ` + bind + ` requires elevated privileges`);
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
console.error(`[Raygun4Node-Express-Sample] ` + bind + ` is already in use`);
process.exit(1);
break;
default:
Expand Down
2 changes: 1 addition & 1 deletion examples/express-sample/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/express-sample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"scripts": {
"start": "DEBUG=express-sample:server node ./bin/www"
"start": "DEBUG=raygun,express-sample:server node ./bin/www"
},
"dependencies": {
"body-parser": "^1.20.2",
Expand Down
24 changes: 0 additions & 24 deletions examples/express-sample/readme.md

This file was deleted.

File renamed without changes.
31 changes: 18 additions & 13 deletions examples/using-domains/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
var config = require("config");

if (config.Raygun.Key === "YOUR_API_KEY") {
console.error("You need to set your Raygun API key in the config file");
console.error(
`[Raygun4Node-Domains-Sample] You need to set your Raygun API key in the config file`,
);
process.exit(1);
}

Expand All @@ -15,27 +17,30 @@ 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(`[Raygun4Node-Domains-Sample] 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");
console.log(
`[Raygun4Node-Domains-Sample] 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
appDomain.run(function () {
var fs = require("fs");

console.log("Running example app");
console.log(`[Raygun4Node-Domains-Sample] Running example app`);

// Try and read a file that doesn't exist
fs.readFile("badfile.json", "utf8", function (err, file) {
Expand Down
16 changes: 11 additions & 5 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 All @@ -56,7 +61,7 @@ export class RaygunBatchTransport {

stopProcessing() {
if (this.timerId) {
debug("batch transport - stopping");
debug(`[raygun.batch.ts] Batch transport - stopping`);
clearInterval(this.timerId);

this.timerId = null;
Expand Down Expand Up @@ -123,7 +128,7 @@ export class RaygunBatchTransport {
const { payload, messageCount, callbacks } = batch;

debug(
`batch transport - processing ( ${messageCount} message(s) in batch)`,
`[raygun.batch.ts] Batch transport - processing (${messageCount} message(s) in batch)`,
);

const batchId = this.batchId;
Expand All @@ -137,14 +142,15 @@ export class RaygunBatchTransport {
const durationInMs = stopTimer();
if (err) {
debug(
`batch transport - error sending batch (id=${batchId}, duration=${durationInMs}ms): ${err}`,
`[raygun.batch.ts] Batch transport - error sending batch (id=${batchId}, duration=${durationInMs}ms): ${err}`,
);
} else {
debug(
`batch transport - successfully sent batch (id=${batchId}, duration=${durationInMs}ms)`,
`[raygun.batch.ts] Batch transport - successfully sent batch (id=${batchId}, duration=${durationInMs}ms)`,
);
}

// 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 All @@ -153,7 +159,7 @@ export class RaygunBatchTransport {
};

debug(
`batch transport - sending batch (id=${batchId}) (${messageCount} messages, ${payload.length} bytes)`,
`[raygun.batch.ts] Batch transport - sending batch (id=${batchId}, ${messageCount} messages, ${payload.length} bytes)`,
);

const stopTimer = startTimer();
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];
} else {
_obj[i] = filterKeys(_obj[i], filters);
_obj[key] = filterKeys(_obj[key], filters, _explored);
}
});
return _obj;
Expand Down
Loading

0 comments on commit 28abe41

Please sign in to comment.