diff --git a/.gitignore b/.gitignore
index 3520a38..cd8a550 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
 .nyc_output
+.tap
 coverage
 node_modules
-.tap
diff --git a/index.js b/index.js
index fb37913..9423253 100644
--- a/index.js
+++ b/index.js
@@ -3,7 +3,7 @@
 const { Transform } = require("node:stream");
 
 const { prettyFactory } = require("pino-pretty");
-const Sentry = require("@sentry/node");
+const { init, withScope, captureException } = require("@sentry/node");
 
 const LEVEL_MAP = {
   10: "trace",
@@ -14,8 +14,27 @@ const LEVEL_MAP = {
   60: "fatal",
 };
 
+const pinoIgnore = [
+  // default pino keys
+  "time",
+  "pid",
+  "hostname",
+  // remove keys from pino-http
+  "req",
+  "res",
+  "responseTime",
+].join(",");
+
+const pinoErrorProps = [
+  "event",
+  "status",
+  "headers",
+  "request",
+  "sentryEventId",
+].join(",");
+
 /**
- * Implements Probot's default logging formatting and error captionaing using Sentry.
+ * Implements Probot's default logging formatting and error captioning using Sentry.
  *
  * @param {import("./").Options} options
  * @returns Transform
@@ -28,7 +47,7 @@ function getTransformStream(options = {}) {
   const sentryEnabled = !!options.sentryDsn;
 
   if (sentryEnabled) {
-    Sentry.init({
+    init({
       dsn: options.sentryDsn,
       // See https://github.com/getsentry/sentry-javascript/issues/1964#issuecomment-688482615
       // 6 is enough to serialize the deepest property across all GitHub Event payloads
@@ -37,19 +56,8 @@ function getTransformStream(options = {}) {
   }
 
   const pretty = prettyFactory({
-    ignore: [
-      // default pino keys
-      "time",
-      "pid",
-      "hostname",
-      // remove keys from pino-http
-      "req",
-      "res",
-      "responseTime",
-    ].join(","),
-    errorProps: ["event", "status", "headers", "request", "sentryEventId"].join(
-      ",",
-    ),
+    ignore: pinoIgnore,
+    errorProps: pinoErrorProps,
   });
 
   return new Transform({
@@ -76,18 +84,26 @@ function getTransformStream(options = {}) {
         return;
       }
 
-      Sentry.withScope(function (scope) {
+      withScope((scope) => {
         const sentryLevelName = data.level === 50 ? "error" : "fatal";
         scope.setLevel(sentryLevelName);
 
-        for (const extra of ["event", "headers", "request", "status"]) {
-          if (!data[extra]) continue;
-
-          scope.setExtra(extra, data[extra]);
+        if (data.event) {
+          scope.setExtra("event", data.event);
+        }
+        if (data.headers) {
+          scope.setExtra("headers", data.headers);
+        }
+        if (data.request) {
+          scope.setExtra("request", data.request);
+        }
+        if (data.status) {
+          scope.setExtra("status", data.status);
         }
 
         // set user id and username to installation ID and account login
-        if (data.event && data.event.payload) {
+        const payload = data.event?.payload || data.err?.event?.payload;
+        if (payload) {
           const {
             // When GitHub App is installed organization wide
             installation: { id, account: { login: account } = {} } = {},
@@ -96,7 +112,7 @@ function getTransformStream(options = {}) {
             organization: { login: organization } = {},
             // When the repository belongs to a user
             repository: { owner: { login: owner } = {} } = {},
-          } = data.event.payload;
+          } = payload;
 
           scope.setUser({
             id,
@@ -104,7 +120,7 @@ function getTransformStream(options = {}) {
           });
         }
 
-        const sentryEventId = Sentry.captureException(toSentryError(data));
+        const sentryEventId = captureException(toSentryError(data));
 
         // reduce logging data and add reference to sentry event instead
         if (data.event) {
diff --git a/package-lock.json b/package-lock.json
index dd289fc..750b04d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,8 +18,7 @@
         "pino-probot": "bin/cli.js"
       },
       "devDependencies": {
-        "@types/pino": "^7.0.5",
-        "pino": "^6.6.0",
+        "pino": "^9.3.2",
         "prettier": "^3.4.2",
         "tap": "^21.0.1"
       },
@@ -1873,15 +1872,6 @@
         "@types/pg": "*"
       }
     },
-    "node_modules/@types/pino": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/@types/pino/-/pino-7.0.5.tgz",
-      "integrity": "sha512-wKoab31pknvILkxAF8ss+v9iNyhw5Iu/0jLtRkUD74cNfOOLJNnqfFKAv0r7wVaTQxRZtWrMpGfShwwBjOcgcg==",
-      "dev": true,
-      "dependencies": {
-        "pino": "*"
-      }
-    },
     "node_modules/@types/shimmer": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
@@ -2720,12 +2710,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/flatstr": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz",
-      "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==",
-      "dev": true
-    },
     "node_modules/foreground-child": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
@@ -2783,21 +2767,6 @@
         "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
-    "node_modules/fsevents": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
-      "dev": true,
-      "hasInstallScript": true,
-      "license": "MIT",
-      "optional": true,
-      "os": [
-        "darwin"
-      ],
-      "engines": {
-        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
-      }
-    },
     "node_modules/function-bind": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -4155,18 +4124,26 @@
       }
     },
     "node_modules/pino": {
-      "version": "6.14.0",
-      "resolved": "https://registry.npmjs.org/pino/-/pino-6.14.0.tgz",
-      "integrity": "sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==",
+      "version": "9.6.0",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz",
+      "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==",
       "dev": true,
+      "license": "MIT",
       "dependencies": {
-        "fast-redact": "^3.0.0",
-        "fast-safe-stringify": "^2.0.8",
-        "flatstr": "^1.0.12",
-        "pino-std-serializers": "^3.1.0",
-        "process-warning": "^1.0.0",
+        "atomic-sleep": "^1.0.0",
+        "fast-redact": "^3.1.1",
+        "on-exit-leak-free": "^2.1.0",
+        "pino-abstract-transport": "^2.0.0",
+        "pino-std-serializers": "^7.0.0",
+        "process-warning": "^4.0.0",
         "quick-format-unescaped": "^4.0.3",
-        "sonic-boom": "^1.0.2"
+        "real-require": "^0.2.0",
+        "safe-stable-stringify": "^2.3.1",
+        "sonic-boom": "^4.0.1",
+        "thread-stream": "^3.0.0"
+      },
+      "bin": {
+        "pino": "bin.js"
       }
     },
     "node_modules/pino-abstract-transport": {
@@ -4202,20 +4179,12 @@
         "pino-pretty": "bin.js"
       }
     },
-    "node_modules/pino-pretty/node_modules/sonic-boom": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
-      "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
-      "license": "MIT",
-      "dependencies": {
-        "atomic-sleep": "^1.0.0"
-      }
-    },
     "node_modules/pino-std-serializers": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz",
-      "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==",
-      "dev": true
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
+      "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/pirates": {
       "version": "4.0.6",
@@ -4360,10 +4329,21 @@
       }
     },
     "node_modules/process-warning": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz",
-      "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==",
-      "dev": true
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
+      "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "MIT"
     },
     "node_modules/promise-inflight": {
       "version": "1.0.1",
@@ -4482,6 +4462,16 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/real-require": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+      "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12.13.0"
+      }
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -4597,6 +4587,16 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -4781,13 +4781,12 @@
       }
     },
     "node_modules/sonic-boom": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz",
-      "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==",
-      "dev": true,
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
+      "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
+      "license": "MIT",
       "dependencies": {
-        "atomic-sleep": "^1.0.0",
-        "flatstr": "^1.0.12"
+        "atomic-sleep": "^1.0.0"
       }
     },
     "node_modules/spdx-correct": {
@@ -5279,6 +5278,16 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/thread-stream": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
+      "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "real-require": "^0.2.0"
+      }
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
diff --git a/package.json b/package.json
index 4c243d5..2d002d3 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,7 @@
   "license": "MIT",
   "repository": "github:probot/pino",
   "devDependencies": {
-    "@types/pino": "^7.0.5",
-    "pino": "^6.6.0",
+    "pino": "^9.3.2",
     "prettier": "^3.4.2",
     "tap": "^21.0.1"
   },
diff --git a/test/cli.test.js b/test/cli.test.js
index 6e9a4af..0a0a348 100644
--- a/test/cli.test.js
+++ b/test/cli.test.js
@@ -2,7 +2,6 @@
 
 const { join: pathJoin } = require("node:path");
 const { spawn } = require("node:child_process");
-const { createServer } = require("node:http");
 const { test } = require("tap");
 
 const cliPath = require.resolve(pathJoin(__dirname, "..", "bin", "cli.js"));
@@ -12,8 +11,6 @@ const logLine =
   '{"level":30,"time":1445858940000,"name":"probot","msg":"hello future","pid":42,"hostname":"foo"}\n';
 const errorLine =
   '{"level":50,"time":1597399283686,"pid":35269,"hostname":"Gregors-MacBook-Pro.local","name":"probot","status":500,"event":{"event":"installation_repositories.added","id":"123","installation":456},"headers":{"x-github-request-id":"789"},"request":{"headers":{"accept":"application/vnd.github.v3+json","authorization":"[Filtered]","user-agent":"probot/10.0.0"},"method":"GET","url":"https://api.github.com/repos/octocat/hello-world/"},"stack":"Error: Oops\\n    at Object.<anonymous> (/Users/gregor/Projects/probot/pino/example.js:37:15)\\n    at Module._compile (internal/modules/cjs/loader.js:1137:30)\\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\\n    at Module.load (internal/modules/cjs/loader.js:985:32)\\n    at Function.Module._load (internal/modules/cjs/loader.js:878:14)\\n    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)\\n    at internal/main/run_main_module.js:17:47","type":"Error","msg":"Oops"}\n';
-const fatalErrorLine =
-  '{"level":60,"time":1597426544906,"pid":43024,"hostname":"Gregors-MacBook-Pro.local","name":"probot","stack":"Error: Oh no!\\n    at Object.<anonymous> (/Users/gregor/Projects/probot/pino/example.js:59:12)\\n    at Module._compile (internal/modules/cjs/loader.js:1137:30)\\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\\n    at Module.load (internal/modules/cjs/loader.js:985:32)\\n    at Function.Module._load (internal/modules/cjs/loader.js:878:14)\\n    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)\\n    at internal/main/run_main_module.js:17:47","type":"Error","msg":"Oh no!"}\n';
 
 const stripAnsiColorRE =
   /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
@@ -24,6 +21,8 @@ const env = {
 };
 
 test("cli", (t) => {
+  t.plan(4);
+
   t.test(
     "formats using pino-pretty and Probot's preferences by default",
     (t) => {
@@ -92,132 +91,4 @@ test("cli", (t) => {
     child.stdin.write(logLine);
     t.teardown(() => child.kill());
   });
-
-  t.test("SENTRY_DSN", (t) => {
-    t.plan(5);
-
-    const server = createServer((request, response) => {
-      // we can access HTTP headers
-      let body = "";
-      request.on("data", (chunk) => {
-        body += chunk.toString();
-      });
-      request.on("end", () => {
-        const data = body.split("\n").map((line) => JSON.parse(line));
-        const error = data[2].exception.values[0];
-
-        t.equal(error.type, "Error");
-        t.equal(error.value, "Oops");
-        t.strictSame(data[2].extra, {
-          event: {
-            event: "installation_repositories.added",
-            id: "123",
-            installation: 456,
-          },
-          headers: { "x-github-request-id": "789" },
-          request: {
-            headers: {
-              accept: "application/vnd.github.v3+json",
-              authorization: "[Filtered]",
-              "user-agent": "probot/10.0.0",
-            },
-            method: "GET",
-            url: "https://api.github.com/repos/octocat/hello-world/",
-          },
-          status: 500,
-        });
-        server.close(() => t.end());
-      });
-
-      response.writeHead(200);
-      response.write("ok");
-      response.end();
-    });
-
-    server.listen(0);
-
-    const child = spawn(nodeBinaryPath, [cliPath], {
-      env: {
-        ...env,
-        SENTRY_DSN: `http://user@localhost:${server.address().port}/123`,
-      },
-    });
-    child.on("error", t.threw);
-    child.stdout.on("data", (data) => {
-      const errorStringLines = data
-        .toString()
-        .replace(stripAnsiColorRE, "")
-        .split(/\n/);
-      t.equal(errorStringLines[0].trim(), "ERROR (probot): Oops");
-
-      // skip the error stack, normalize Sentry Event ID, compare error details only
-      t.equal(
-        errorStringLines
-          .slice(9)
-          .join("\n")
-          .trim()
-          .replace(/sentryEventId: \w+$/, "sentryEventId: 123"),
-        `event: {
-        id: "123"
-    }
-    status: 500
-    headers: {
-        x-github-request-id: "789"
-    }
-    request: {
-        method: "GET"
-        url: "https://api.github.com/repos/octocat/hello-world/"
-    }
-    sentryEventId: 123`,
-      );
-    });
-    child.stdin.write(errorLine);
-
-    t.teardown(() => child.kill());
-  });
-
-  t.test("SENTRY_DSN with fatal error", (t) => {
-    t.plan(3);
-
-    const server = createServer((request, response) => {
-      let body = "";
-      request.on("data", (chunk) => {
-        body += chunk.toString();
-      });
-      request.on("end", () => {
-        const data = body.split("\n").map((line) => JSON.parse(line));
-        const error = data[2].exception.values[0];
-
-        t.equal(error.type, "Error");
-        t.equal(error.value, "Oh no!");
-
-        server.close(() => t.end());
-      });
-
-      response.writeHead(200);
-      response.write("ok");
-      response.end();
-    });
-
-    server.listen(0);
-
-    const child = spawn(nodeBinaryPath, [cliPath], {
-      env: {
-        ...env,
-        SENTRY_DSN: `http://user@localhost:${server.address().port}/123`,
-      },
-    });
-    child.on("error", t.threw);
-    child.stdout.on("data", (data) => {
-      t.match(
-        data.toString().replace(stripAnsiColorRE, ""),
-        /^FATAL \(probot\): Oh no!\n/,
-      );
-    });
-    child.stdin.write(fatalErrorLine);
-
-    t.teardown(() => child.kill());
-  });
-
-  t.end();
 });
diff --git a/test/index.test.js b/test/index.test.js
index 0c541a3..9fda0a0 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -1,18 +1,16 @@
 "use strict";
 
-const Sentry = require("@sentry/node");
-
 const { Writable: WritableStream } = require("stream");
 
 const { test } = require("tap");
-const pino = require("pino");
+const { pino } = require("pino");
 const { getTransformStream } = require("..");
 
 test("API", (t) => {
   let env = Object.assign({}, process.env);
 
   t.afterEach(() => {
-    process.env = Object.assign({}, env);
+    process.env = { ...env };
   });
 
   t.test("getTransformStream export", (t) => {
@@ -25,114 +23,6 @@ test("API", (t) => {
     t.end();
   });
 
-  t.test("Sentry integration enabled", (t) => {
-    const transform = getTransformStream({
-      sentryDsn: "http://username@example.com/1234",
-    });
-    const log = pino({}, transform);
-
-    function event(payload) {
-      const error = new Error("Hello from the test");
-      error.level = 50;
-      error.event = {
-        payload: Object.assign(
-          {
-            installation: {
-              id: "456",
-            },
-          },
-          payload,
-        ),
-      };
-      return error;
-    }
-
-    t.test("without user", (t) => {
-      t.plan(1);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.strictSame(event.user, { id: "456" });
-        });
-
-        log.fatal(event({}));
-      });
-    });
-
-    t.test("with organization", (t) => {
-      t.plan(1);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.match(event.user, { username: "org" });
-        });
-
-        log.fatal(event({ organization: { login: "org" } }));
-      });
-    });
-
-    t.test("with installation account", (t) => {
-      t.plan(1);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.match(event.user, { username: "account" });
-        });
-
-        log.fatal(event({ installation: { account: { login: "account" } } }));
-      });
-    });
-
-    t.test("with repository owner", (t) => {
-      t.plan(1);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.match(event.user, { username: "owner" });
-        });
-
-        log.fatal(event({ repository: { owner: { login: "owner" } } }));
-      });
-    });
-
-    t.test("with repository owner and without installation", (t) => {
-      t.plan(1);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.match(event.user, { username: "owner" });
-        });
-
-        log.fatal(
-          event({
-            installation: undefined,
-            repository: { owner: { login: "owner" } },
-          }),
-        );
-      });
-    });
-
-    t.test("with logFormat: json", (t) => {
-      t.plan(1);
-
-      const transform = getTransformStream({
-        sentryDsn: "http://username@example.com/1234",
-        logFormat: "json",
-      });
-      const log = pino({}, transform);
-
-      Sentry.withScope(function (scope) {
-        scope.addEventProcessor(function (event, hint) {
-          t.match(event.user, { username: "owner" });
-        });
-
-        log.fatal(event({ repository: { owner: { login: "owner" } } }));
-      });
-    });
-
-    t.end();
-  });
-
   t.test(
     "A single \\n is added to the end log lines when LOG_FORMAT is set to 'json' (https://github.com/probot/probot/issues/1334)",
     (t) => {
diff --git a/test/sentry.cli.test.js b/test/sentry.cli.test.js
new file mode 100644
index 0000000..4eab1b8
--- /dev/null
+++ b/test/sentry.cli.test.js
@@ -0,0 +1,163 @@
+"use strict";
+
+const { join: pathJoin } = require("node:path");
+const { spawn } = require("node:child_process");
+const { createServer } = require("node:http");
+const { test } = require("tap");
+const { once } = require("node:events");
+
+const SENTRY_DSN = "http://username@example.com/1234";
+
+const cliPath = require.resolve(pathJoin(__dirname, "..", "bin", "cli.js"));
+const nodeBinaryPath = process.argv[0];
+
+const errorLine =
+  '{"level":50,"time":1597399283686,"pid":35269,"hostname":"Gregors-MacBook-Pro.local","name":"probot","status":500,"event":{"event":"installation_repositories.added","id":"123","installation":456},"headers":{"x-github-request-id":"789"},"request":{"headers":{"accept":"application/vnd.github.v3+json","authorization":"[Filtered]","user-agent":"probot/10.0.0"},"method":"GET","url":"https://api.github.com/repos/octocat/hello-world/"},"stack":"Error: Oops\\n    at Object.<anonymous> (/Users/gregor/Projects/probot/pino/example.js:37:15)\\n    at Module._compile (internal/modules/cjs/loader.js:1137:30)\\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\\n    at Module.load (internal/modules/cjs/loader.js:985:32)\\n    at Function.Module._load (internal/modules/cjs/loader.js:878:14)\\n    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)\\n    at internal/main/run_main_module.js:17:47","type":"Error","msg":"Oops"}\n';
+const fatalErrorLine =
+  '{"level":60,"time":1597426544906,"pid":43024,"hostname":"Gregors-MacBook-Pro.local","name":"probot","stack":"Error: Oh no!\\n    at Object.<anonymous> (/Users/gregor/Projects/probot/pino/example.js:59:12)\\n    at Module._compile (internal/modules/cjs/loader.js:1137:30)\\n    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\\n    at Module.load (internal/modules/cjs/loader.js:985:32)\\n    at Function.Module._load (internal/modules/cjs/loader.js:878:14)\\n    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)\\n    at internal/main/run_main_module.js:17:47","type":"Error","msg":"Oh no!"}\n';
+
+const stripAnsiColorRE =
+  /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
+
+const env = {
+  // disable colors
+  TERM: "dumb",
+};
+
+test("SENTRY_DSN", (t) => {
+  t.plan(2);
+
+  t.test("SENTRY_DSN with ERROR error", async (t) => {
+    t.plan(2);
+
+    const server = createServer((request, response) => {
+      // we can access HTTP headers
+      let body = "";
+      request.on("data", (chunk) => {
+        body += chunk.toString();
+      });
+      request.on("end", () => {
+        const data = JSON.parse(body.split("\n")[2]);
+        const error = data.exception.values[0];
+
+        t.equal(error.type, "Error");
+        t.equal(error.value, "Oops");
+        t.strictSame(data.extra, {
+          event: {
+            event: "installation_repositories.added",
+            id: "123",
+            installation: 456,
+          },
+          headers: { "x-github-request-id": "789" },
+          request: {
+            headers: {
+              accept: "application/vnd.github.v3+json",
+              authorization: "[Filtered]",
+              "user-agent": "probot/10.0.0",
+            },
+            method: "GET",
+            url: "https://api.github.com/repos/octocat/hello-world/",
+          },
+          status: 500,
+        });
+      });
+
+      response.writeHead(200);
+      response.write("ok");
+      response.end();
+    });
+
+    server.listen(0);
+
+    await once(server, "listening");
+
+    const child = spawn(nodeBinaryPath, [cliPath], {
+      env: {
+        ...env,
+        SENTRY_DSN,
+      },
+    });
+    child.on("error", t.threw);
+    child.stdout.on("data", (data) => {
+      const errorStringLines = data
+        .toString()
+        .replace(stripAnsiColorRE, "")
+        .split(/\n/);
+      t.equal(errorStringLines[0].trim(), "ERROR (probot): Oops");
+
+      // skip the error stack, normalize Sentry Event ID, compare error details only
+      t.equal(
+        errorStringLines
+          .slice(9)
+          .join("\n")
+          .trim()
+          .replace(/sentryEventId: \w+$/, "sentryEventId: 123"),
+        `event: {
+        id: "123"
+    }
+    status: 500
+    headers: {
+        x-github-request-id: "789"
+    }
+    request: {
+        method: "GET"
+        url: "https://api.github.com/repos/octocat/hello-world/"
+    }
+    sentryEventId: 123`,
+      );
+    });
+    child.stdin.write(errorLine);
+
+    t.teardown(() => {
+      child.kill();
+      server.closeAllConnections();
+      server.close();
+    });
+  });
+
+  t.test("SENTRY_DSN with FATAL error", async (t) => {
+    t.plan(1);
+
+    const server = createServer((request, response) => {
+      let body = "";
+      request.on("data", (chunk) => {
+        body += chunk.toString();
+      });
+      request.on("end", () => {
+        const data = JSON.parse(body.split("\n")[2]);
+        const error = data.exception.values[0];
+
+        t.equal(error.type, "Error");
+        t.equal(error.value, "Oh no!");
+      });
+
+      response.writeHead(200);
+      response.write("ok");
+      response.end();
+    });
+
+    server.listen(0);
+    await once(server, "listening");
+
+    const child = spawn(nodeBinaryPath, [cliPath], {
+      env: {
+        ...env,
+        SENTRY_DSN,
+      },
+    });
+    child.on("error", t.threw);
+    child.stdout.on("data", (data) => {
+      t.match(
+        data.toString().replace(stripAnsiColorRE, ""),
+        /^FATAL \(probot\): Oh no!\n/,
+      );
+    });
+    child.stdin.write(fatalErrorLine);
+
+    t.teardown(() => {
+      child.kill();
+      server.closeAllConnections();
+      server.close();
+    });
+  });
+});
diff --git a/test/sentry.runtime.test.js b/test/sentry.runtime.test.js
new file mode 100644
index 0000000..f3f77ad
--- /dev/null
+++ b/test/sentry.runtime.test.js
@@ -0,0 +1,124 @@
+"use strict";
+
+const { withScope } = require("@sentry/node");
+
+const { test } = require("tap");
+const { pino } = require("pino");
+const { getTransformStream } = require("..");
+
+test("API", (t) => {
+  t.plan(1);
+
+  let env = Object.assign({}, process.env);
+
+  t.afterEach(() => {
+    process.env = { ...env };
+  });
+
+  t.test("Sentry integration enabled", (t) => {
+    t.plan(6);
+    const transform = getTransformStream({
+      sentryDsn: "http://username@example.com/1234",
+    });
+    const log = pino({}, transform);
+
+    function event(payload) {
+      const error = new Error("Hello from the test");
+      error.level = 50;
+      error.event = {
+        payload: Object.assign(
+          {
+            installation: {
+              id: "456",
+            },
+          },
+          payload,
+        ),
+      };
+      return error;
+    }
+
+    t.test("without user", (t) => {
+      t.plan(1);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.strictSame(event.user, { id: "456" });
+        });
+
+        log.fatal(event({}));
+      });
+    });
+
+    t.test("with organization", (t) => {
+      t.plan(1);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.match(event.user, { username: "org" });
+        });
+
+        log.fatal(event({ organization: { login: "org" } }));
+      });
+    });
+
+    t.test("with installation account", (t) => {
+      t.plan(1);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.match(event.user, { username: "account" });
+        });
+
+        log.fatal(event({ installation: { account: { login: "account" } } }));
+      });
+    });
+
+    t.test("with repository owner", (t) => {
+      t.plan(1);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.match(event.user, { username: "owner" });
+        });
+
+        log.fatal(event({ repository: { owner: { login: "owner" } } }));
+      });
+    });
+
+    t.test("with repository owner and without installation", (t) => {
+      t.plan(1);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.match(event.user, { username: "owner" });
+        });
+
+        log.fatal(
+          event({
+            installation: undefined,
+            repository: { owner: { login: "owner" } },
+          }),
+        );
+      });
+    });
+
+    t.test("with logFormat: json", (t) => {
+      t.plan(1);
+
+      const transform = getTransformStream({
+        sentryDsn: "http://username@example.com/1234",
+        logFormat: "json",
+      });
+      const log = pino({}, transform);
+
+      withScope(function (scope) {
+        scope.addEventProcessor(function (event, hint) {
+          t.match(event.user, { username: "owner" });
+        });
+
+        log.fatal(event({ repository: { owner: { login: "owner" } } }));
+      });
+    });
+  });
+});