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: Breadcrumbs #210

Merged
merged 38 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
66aa32e
setup eslint style
miquelbeltran May 9, 2024
3051584
rules for quotes
miquelbeltran May 9, 2024
3e96332
add eslint plugin ts
miquelbeltran May 9, 2024
b13402b
wip configs
miquelbeltran May 9, 2024
397d2dc
wip
miquelbeltran May 9, 2024
093e314
add rules
miquelbeltran May 9, 2024
0aec067
define rules
miquelbeltran May 9, 2024
ebe0b55
add comment
miquelbeltran May 9, 2024
76c3bc0
add implementation from old breadcrumbs branch
miquelbeltran May 9, 2024
18790c5
tests passing
miquelbeltran May 9, 2024
276f9c0
add todo
miquelbeltran May 9, 2024
3fc2324
Merge branch 'develop' into miquelbeltran/breadcrumbs
miquelbeltran May 10, 2024
346dadc
WIP
miquelbeltran May 10, 2024
28f2dc9
cleaning up and adding docs
miquelbeltran May 10, 2024
5420798
scoping breadcrumbs in error handler
miquelbeltran May 13, 2024
34751e0
improving tests
miquelbeltran May 13, 2024
bdfe7c8
cleanup tests
miquelbeltran May 13, 2024
06127fe
fix eslint
miquelbeltran May 13, 2024
ffe86ba
prettier
miquelbeltran May 13, 2024
15f4120
moved method
miquelbeltran May 13, 2024
62b342d
add Breadcrumb object test
miquelbeltran May 14, 2024
03a6ec1
add Breadcrumb object test
miquelbeltran May 14, 2024
5fee12a
fix test
miquelbeltran May 14, 2024
fb7c68f
expanded the express-example
miquelbeltran May 14, 2024
7021803
prettier
miquelbeltran May 14, 2024
a92b586
prettier
miquelbeltran May 14, 2024
73ae88d
add breadcrumbs to domains app
miquelbeltran May 14, 2024
fe75b49
cleanup and test for clear breadcrumbs
miquelbeltran May 14, 2024
b2def16
prettier
miquelbeltran May 14, 2024
89c39af
rename
miquelbeltran May 14, 2024
61a5570
refactor debug statements
miquelbeltran May 14, 2024
9d6fab3
rename logger.js to raygun.client.js
miquelbeltran May 14, 2024
4a4543a
prettier
miquelbeltran May 14, 2024
a7c9e85
rename
miquelbeltran May 14, 2024
937615e
document Breadcrumbs and rename Breadcrumb type
miquelbeltran May 14, 2024
2284214
improve docs
miquelbeltran May 14, 2024
5db3977
add word
miquelbeltran May 14, 2024
add1c11
removed commented out code
miquelbeltran May 14, 2024
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
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,60 @@ const myBeforeSend = function (payload, exception, customData, request, tags) {
Raygun.onBeforeSend(myBeforeSend);
```

### Breadcrumbs

Breadcrumbs can be sent to Raygun to provide additional information to look into and debug issues stemming from crash reports.

Breadcrumbs can be created in two ways.

#### Simple string:

Call `client.addBreadcrumb(message)`, where message is just a string:

```js
client.addBreadcrumb('test breadcrumb');
```

#### Using `BreadcrumbMessage`:

Create your own `BreadcrumbMessage` object and send more than just a message with `client.addBreadcrumb(BreadcrumbMessage)`.

The structure of the type `BreadcrumbMessage` is as shown here:

```js
BreadcrumbMessage: {
level: "debug" | "info" | "warning" | "error";
category: string;
message: string;
customData?: CustomData;
}
```

Breadcrumbs can be cleared with `client.clearBreadcrumbs()`.

#### Breadcrumbs and ExpressJS

Raygun4Node provides a custom ExpressJS middleware that helps to scope Breadcrumbs to a specific request.
As well, this middleware will add a Breadcrumb with information about the performed request.

To set up, add the Raygun Breadcrumbs ExpressJS handler before configuring any endpoints.

```js
// Add the Raygun Breadcrumb ExpressJS handler
app.use(raygunClient.expressHandlerBreadcrumbs);

// Setup the rest of the app, e.g.
app.use("/", routes);
```

This middleware can be user together with the provided ExpressJS error handler `expressHandler`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"used" instead of "user"

The order in which the middlewares are configured is important. `expressHandlerBreadcrumbs` should go first to scope breadcrumbs correctly.

```js
app.use(raygunClient.expressHandlerBreadcrumbs);
app.use(raygunClient.expressHandler);
```

### Batched error transport

You can enable a batched transport mode for the Raygun client by passing `{batch: true}` when initializing.
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export default tseslint.config(
// Add node globals to ignore undefined
globals: {
"__dirname": false,
"__filename": false,
"console": false,
"module": false,
"process": false,
Expand Down
11 changes: 7 additions & 4 deletions examples/express-sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ in the subdirectory where you found this README.md file.

## Interesting files to look

- `raygun.client.js`
- Setup of Raygun (lines 9-14)
- `app.js`
- Setup of Raygun (lines 9-12)
- Sets the user (lines 27-29)
- Attaches Raygun to Express (line 60)
- Sets the user (lines 17-19)
- Attaches Raygun Breadcrumb middleware to Express (line 26)
- Attaches Raygun to Express (line 53)
- `routes/index.js`
- Tries to use a fake object, which bounces up to the Express handler (lines 11-15)
- `/send` endpoint: Sends a custom error to Raygun (lines 11-34)
- `/error` endpoint: Tries to use a fake object, which bounces up to the Express handler (lines 36-49)
23 changes: 7 additions & 16 deletions examples/express-sample/app.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
var config = require("config");

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

// Setup Raygun
var raygun = require("raygun");
var raygunClient = new raygun.Client().init({
apiKey: config.Raygun.Key,
});

var express = require("express");
var path = require("path");
var logger = require("morgan");
var cookieParser = require("cookie-parser");
var bodyParser = require("body-parser");
var sassMiddleware = require("node-sass-middleware");
var raygunClient = require("./raygun.client");

var routes = require("./routes/index");
var users = require("./routes/users");
Expand All @@ -34,6 +20,9 @@ raygunClient.user = function (req) {
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");

// Add the Raygun breadcrumb Express handler
app.use(raygunClient.expressHandlerBreadcrumbs);

// uncomment after placing your favicon in /public
// app.use(favicon(__dirname + '/public/favicon.ico'));
Comment on lines 26 to 27
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The favicon was missing in the /public folder, this should be cleaned up, or we need a favicon. I'll see if we can get a raygun one.

app.use(logger("dev"));
Expand All @@ -58,7 +47,9 @@ app.use(express.static(path.join(__dirname, "public")));
app.use("/", routes);
app.use("/users", users);

// Add the Raygun Express handler
// Add the Raygun error Express handler
app.use(raygunClient.expressHandler);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree that it (express naming usage) is too tightly coupled at the moment. But until we see popular requests for other frameworks, I'm happy with the current implementation until we implement support for other frameworks. I think the new approach looks good (moving to raygun.breadcrumbs.express.ts).

But does a standalone node app need breadcrumbs tracked manually?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But does a standalone node app need breadcrumbs tracked manually?

Yes, users would need to manually add breadcrumbs.

The expressBreadcrumbsHandler only adds a single breadcrumb for requests, so it isn't a big difference nevertheless.


raygunClient.addBreadcrumb("Express Server started!");

module.exports = app;
2 changes: 2 additions & 0 deletions examples/express-sample/package-lock.json

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

16 changes: 16 additions & 0 deletions examples/express-sample/raygun.client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var config = require("config");

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

// Setup Raygun
var raygun = require("raygun");
var raygunClient = new raygun.Client().init({
apiKey: config.Raygun.Key,
});

module.exports = raygunClient;
42 changes: 39 additions & 3 deletions examples/express-sample/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
var express = require("express");
var router = express.Router();
const express = require("express");
const router = express.Router();
const raygunClient = require("../raygun.client");

/* GET home page. */
router.get("/", function (req, res, next) {
res.render("index", {
title: "Express",
title: "Raygun Express Example",
});
});

router.get("/send", function (req, res, next) {
raygunClient.addBreadcrumb({
level: "debug",
category: "Example",
message: "Breadcrumb in /send endpoint",
customData: {
"custom-data": "data",
},
});

raygunClient
.send("Custom Raygun Error in /send endpoint")
.then((message) => {
res.render("send", {
title: "Sent custom error to Raygun",
body: `Raygun status code: ${message.statusCode}`,
});
})
.catch((error) => {
res.render("send", {
title: "Failed to send custom error to Raygun",
body: error.toString(),
});
});
});

router.get("/error", function (req, res, next) {
raygunClient.addBreadcrumb({
level: "debug",
category: "Example",
message: "Breadcrumb in /error endpoint",
customData: {
"custom-data": "data",
},
});

// Call an object that doesn't exist to send an error to Raygun
fakeObject.FakeMethod();
res.send(500);
Expand Down
3 changes: 3 additions & 0 deletions examples/express-sample/views/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>

<div>
<a href="/send">Send a custom error</a>
</div>
<div>
<a href="/error">Throw an error</a>
</div>
Expand Down
18 changes: 18 additions & 0 deletions examples/express-sample/views/send.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p><%= title %></p>
<div>
<%= body %>
</div>

<div>
<a href="/">Back to index</a>
</div>
</body>
</html>
2 changes: 2 additions & 0 deletions examples/using-domains/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ 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) {
raygunClient.addBreadcrumb("Domain error caught!");
console.log(`[Raygun4Node-Domains-Sample] Domain error caught: ${err}`);
// Try send data to Raygun
raygunClient
Expand All @@ -40,6 +41,7 @@ appDomain.on("error", function (err) {
appDomain.run(function () {
var fs = require("fs");

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

// Try and read a file that doesn't exist
Expand Down
2 changes: 2 additions & 0 deletions examples/using-domains/package-lock.json

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

34 changes: 34 additions & 0 deletions lib/raygun.breadcrumbs.express.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Request } from "express";
import type { Breadcrumb } from "./types";
import { getBreadcrumbs } from "./raygun.breadcrumbs";

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

/**
* Parses an ExpressJS Request and adds it to the breadcrumbs store
* @param request
*/
export function addRequestBreadcrumb(request: Request) {
const crumbs = getBreadcrumbs();

if (!crumbs) {
debug(
"[raygun.breadcrumbs.express.ts] Add request breadcrumb skip, no store!",
);
return;
}

const internalCrumb: Breadcrumb = {
category: "http",
message: `${request.method} ${request.url}`,
level: "info",
timestamp: Number(new Date()),
type: "request",
};

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

crumbs.push(internalCrumb);
}
Loading