diff --git a/package-lock.json b/package-lock.json index 61fee866..011cac01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "packages/*" ], "devDependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "@types/jest": "^26.0.19", "husky": "^4.3.6", "jest": "^26.6.3", @@ -100,9 +100,9 @@ "link": true }, "node_modules/@appsignal/types": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@appsignal/types/-/types-2.1.3.tgz", - "integrity": "sha512-slMFN+nhFr8jnhTbE+vNsHKd4VrUrtv7RCuYnHn5ataGI8BD75YMWBcwqwQD/0NZlVUuvbDlS0G6M+JzZUN/AA==" + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@appsignal/types/-/types-2.1.5.tgz", + "integrity": "sha512-LtxfqACzS10PYxAqe2YgT9k/pPFyzAwyXvi48QddWyBj7ROleAXrEKJjjgqxOYszIGCm14cbCLSDmEDvIVnDbw==" }, "node_modules/@babel/code-frame": { "version": "7.12.11", @@ -10633,7 +10633,7 @@ "version": "1.0.11", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "apollo-server-plugin-base": "^0.10.3", "tslib": "^2.0.3" }, @@ -10651,7 +10651,7 @@ "version": "1.0.12", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "tslib": "^2.0.3" }, "devDependencies": { @@ -10672,7 +10672,7 @@ "version": "1.0.2", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "shimmer": "^1.2.1", "tslib": "^2.0.3" }, @@ -10696,7 +10696,7 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "tslib": "^2.0.3" }, "devDependencies": { @@ -10717,7 +10717,7 @@ "license": "MIT", "dependencies": { "@appsignal/core": "^1.1.4", - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "require-in-the-middle": "^5.1.0", "semver": "^7.3.4", "shimmer": "^1.2.1", @@ -10815,7 +10815,7 @@ "@appsignal/apollo-server": { "version": "file:packages/apollo-server", "requires": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "apollo-server-plugin-base": "*", "tslib": "^2.0.3" }, @@ -10847,7 +10847,7 @@ "@appsignal/express": { "version": "file:packages/express", "requires": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "@types/express": "*", "express": "*", "tslib": "^2.0.3" @@ -10863,7 +10863,7 @@ "@appsignal/koa": { "version": "file:packages/koa", "requires": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "@types/koa": "*", "@types/koa__router": "*", "@types/shimmer": "*", @@ -10882,7 +10882,7 @@ "@appsignal/nextjs": { "version": "file:packages/nextjs", "requires": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "next": "*", "tslib": "^2.0.3" }, @@ -10899,7 +10899,7 @@ "requires": { "@appsignal/core": "^1.1.4", "@appsignal/nodejs-ext": "=2.0.0", - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "@types/pg": "*", "@types/redis": "*", "@types/semver": "*", @@ -10934,9 +10934,9 @@ } }, "@appsignal/types": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@appsignal/types/-/types-2.1.3.tgz", - "integrity": "sha512-slMFN+nhFr8jnhTbE+vNsHKd4VrUrtv7RCuYnHn5ataGI8BD75YMWBcwqwQD/0NZlVUuvbDlS0G6M+JzZUN/AA==" + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@appsignal/types/-/types-2.1.5.tgz", + "integrity": "sha512-LtxfqACzS10PYxAqe2YgT9k/pPFyzAwyXvi48QddWyBj7ROleAXrEKJjjgqxOYszIGCm14cbCLSDmEDvIVnDbw==" }, "@babel/code-frame": { "version": "7.12.11", diff --git a/package.json b/package.json index 4192a85d..a38ef84d 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "packages/*" ], "devDependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "@types/jest": "^26.0.19", "husky": "^4.3.6", "jest": "^26.6.3", diff --git a/packages/apollo-server/package.json b/packages/apollo-server/package.json index 0571e4d1..92070a57 100644 --- a/packages/apollo-server/package.json +++ b/packages/apollo-server/package.json @@ -5,7 +5,7 @@ "types": "dist/index", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "apollo-server-plugin-base": "^0.10.3", "tslib": "^2.0.3" }, diff --git a/packages/express/package.json b/packages/express/package.json index 189d8e13..5447e45c 100644 --- a/packages/express/package.json +++ b/packages/express/package.json @@ -5,7 +5,7 @@ "types": "dist/index", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "tslib": "^2.0.3" }, "peerDependencies": { diff --git a/packages/koa/package.json b/packages/koa/package.json index bad6daad..45f2d522 100644 --- a/packages/koa/package.json +++ b/packages/koa/package.json @@ -5,7 +5,7 @@ "types": "dist/index", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "shimmer": "^1.2.1", "tslib": "^2.0.3" }, diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index adae1ae3..45ca012d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -5,7 +5,7 @@ "types": "dist/index", "license": "MIT", "dependencies": { - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "tslib": "^2.0.3" }, "peerDependencies": { diff --git a/packages/nodejs/.changesets/add-senderror-helper-to-tracer.md b/packages/nodejs/.changesets/add-senderror-helper-to-tracer.md new file mode 100644 index 00000000..728f68a2 --- /dev/null +++ b/packages/nodejs/.changesets/add-senderror-helper-to-tracer.md @@ -0,0 +1,20 @@ +--- +bump: "patch" +--- + +Add sendError helper to Tracer object. + +This new helper allows you to track an error separately from any other span +inside the current context. Or use it to set up in your own error handling to +report errors in a catch-statement if no performance monitoring is needed. + +```js +try { + // Do complex stuff +} catch (error) { + appsignal.tracer().sendError(error, span => { + span.setName("daily.task"); // Set a recognizable action name + span.set("user_id", user_id); // Set custom tags + }); +} +``` diff --git a/packages/nodejs/package.json b/packages/nodejs/package.json index ffa253e5..bf67ab88 100644 --- a/packages/nodejs/package.json +++ b/packages/nodejs/package.json @@ -9,7 +9,7 @@ }, "dependencies": { "@appsignal/core": "^1.1.4", - "@appsignal/types": "^2.1.3", + "@appsignal/types": "^2.1.5", "require-in-the-middle": "^5.1.0", "semver": "^7.3.4", "shimmer": "^1.2.1", diff --git a/packages/nodejs/src/__tests__/tracer.test.ts b/packages/nodejs/src/__tests__/tracer.test.ts index 91db5986..e4d9ad9c 100644 --- a/packages/nodejs/src/__tests__/tracer.test.ts +++ b/packages/nodejs/src/__tests__/tracer.test.ts @@ -37,6 +37,48 @@ describe("Tracer", () => { }) }) + describe(".sendError()", () => { + const err = new Error("FooBarError") + + it("works without metadata callback", () => { + tracer.sendError(err) + }) + + it("uses a RootSpan", done => { + tracer.sendError(err, rootSpan => { + expect(rootSpan).toBeInstanceOf(RootSpan) + + return done() + }) + }) + + it("assigns metadata to the span if needed", done => { + tracer.sendError(err, rootSpan => { + rootSpan.setName("foo") + rootSpan.setCategory("bar") + rootSpan.set("pod", 42) + + const rootSpanData = JSON.parse(rootSpan.toJSON()) + + expect(rootSpanData.name).toEqual("foo") + expect(rootSpanData.attributes["appsignal:category"]).toEqual("bar") + expect(rootSpanData.attributes.pod).toEqual(42) + + return done() + }) + }) + + it("adds the given error to the span", done => { + tracer.sendError(err, rootSpan => { + const rootSpanData = JSON.parse(rootSpan.toJSON()) + + expect(rootSpanData.error.message).toEqual("FooBarError") + + return done() + }) + }) + }) + describe("Span instrumentation", () => { it("can instrument a function (async)", async done => { const rootSpan = tracer.createSpan().setName(name) diff --git a/packages/nodejs/src/noops/tracer.ts b/packages/nodejs/src/noops/tracer.ts index cdd7d3d6..b7cc9aa6 100644 --- a/packages/nodejs/src/noops/tracer.ts +++ b/packages/nodejs/src/noops/tracer.ts @@ -29,6 +29,10 @@ export class NoopTracer implements Tracer { return new NoopSpan() } + public sendError(error: Error, fn: (s: NodeSpan) => T): void { + return + } + public withSpan(span: NodeSpan, fn: (s: NodeSpan) => T): T { return fn(span) } diff --git a/packages/nodejs/src/tracer.ts b/packages/nodejs/src/tracer.ts index 55728e74..b058a3c1 100644 --- a/packages/nodejs/src/tracer.ts +++ b/packages/nodejs/src/tracer.ts @@ -93,6 +93,21 @@ export class BaseTracer implements Tracer { return activeRootSpan } + /** + * Sends an error in a newly created `RootSpan` that will be closed after + * the given error is added to it. + * + * The created `RootSpan` is passed as the single argument to the given function. + * This allows you to add arbitrary metadata to it. + */ + public sendError(error: Error, fn?: (s: NodeSpan) => T): void { + const rootSpan = new RootSpan() + + rootSpan.setError(error) + if (fn && typeof fn === "function") fn(rootSpan) + rootSpan.close() + } + /** * Executes a given function within the context of a given `Span`. When the * function has finished executing, any value returned by the given function