mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Very basic support for Sentry.
The Sentry package is very useful for monitoring runtime errors. With this PR, we simply add the necessary mechanism to: - log to sentry any uncaught error that reaches the toplevel, including startup errors; - log to sentry any error that we already log internally as `ERROR`.
This commit is contained in:
parent
e35b855744
commit
27ab1c710f
@ -209,6 +209,20 @@ health:
|
||||
# Defaults to 418.
|
||||
unhealthyStatus: 418
|
||||
|
||||
# Sentry options. Sentry is a tool used to receive/collate/triage runtime
|
||||
# errors and performance issues. Skip this section if you do not wish to use
|
||||
# Sentry.
|
||||
sentry:
|
||||
# The key used to upload Sentry data to the server.
|
||||
# dsn: "https://XXXXXXXXX@example.com/YYY
|
||||
|
||||
# Frequency of performance monitoring.
|
||||
# A number in [0.0, 1.0], where 0.0 means "don't bother with tracing"
|
||||
# and 1.0 means "trace performance at every opportunity".
|
||||
# tracesSampleRate: 0.5
|
||||
|
||||
|
||||
|
||||
# Options for exposing web APIs.
|
||||
web:
|
||||
# Whether to enable web APIs.
|
||||
|
@ -44,6 +44,8 @@
|
||||
"typescript-formatter": "^7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/node": "^7.17.2",
|
||||
"@sentry/tracing": "^7.17.2",
|
||||
"await-lock": "^2.2.2",
|
||||
"body-parser": "^1.20.1",
|
||||
"config": "^3.3.8",
|
||||
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { extractRequestError, LogLevel, LogService, MatrixClient, MessageType, Permalinks, TextualMessageEventContent, UserID } from "matrix-bot-sdk";
|
||||
import { IConfig } from "./config";
|
||||
import { htmlEscape } from "./utils";
|
||||
@ -34,7 +35,7 @@ export default class ManagementRoomOutput {
|
||||
private readonly managementRoomId: string,
|
||||
private readonly client: MatrixClient,
|
||||
private readonly config: IConfig,
|
||||
) {
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@ -94,6 +95,9 @@ export default class ManagementRoomOutput {
|
||||
* @param isRecursive Whether logMessage is being called from logMessage.
|
||||
*/
|
||||
public async logMessage(level: LogLevel, module: string, message: string | any, additionalRoomIds: string[] | string | null = null, isRecursive = false): Promise<any> {
|
||||
if (level === LogLevel.ERROR) {
|
||||
Sentry.captureMessage(`${module}: ${message}`, 'error');
|
||||
}
|
||||
if (!additionalRoomIds) additionalRoomIds = [];
|
||||
if (!Array.isArray(additionalRoomIds)) additionalRoomIds = [additionalRoomIds];
|
||||
|
||||
@ -115,7 +119,12 @@ export default class ManagementRoomOutput {
|
||||
evContent = await this.replaceRoomIdsWithPills(clientMessage, new Set(roomIds), "m.notice");
|
||||
}
|
||||
|
||||
await client.sendMessage(this.managementRoomId, evContent);
|
||||
try {
|
||||
await client.sendMessage(this.managementRoomId, evContent);
|
||||
} catch (ex) {
|
||||
// We want to be informed if we cannot log a message.
|
||||
Sentry.captureException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
levelToFn[level.toString()](module, message);
|
||||
|
@ -319,11 +319,10 @@ export class Mjolnir {
|
||||
LogService.error("Mjolnir", extractRequestError(err));
|
||||
this.stop();
|
||||
await this.managementRoomOutput.logMessage(LogLevel.ERROR, "Mjolnir@startup", "Startup failed due to error - see console");
|
||||
throw err;
|
||||
} catch (e) {
|
||||
LogService.error("Mjolnir", `Failed to report startup error to the management room: ${e}`);
|
||||
throw err;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@ -602,3 +601,4 @@ export class Mjolnir {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,16 @@ export interface IConfig {
|
||||
healthyStatus: number;
|
||||
unhealthyStatus: number;
|
||||
};
|
||||
// If specified, attempt to upload any crash statistics to sentry.
|
||||
sentry?: {
|
||||
dsn: string;
|
||||
|
||||
// Frequency of performance monitoring.
|
||||
//
|
||||
// A number in [0.0, 1.0], where 0.0 means "don't bother with tracing"
|
||||
// and 1.0 means "trace performance at every opportunity".
|
||||
tracesSampleRate: number;
|
||||
};
|
||||
};
|
||||
web: {
|
||||
enabled: boolean;
|
||||
|
11
src/index.ts
11
src/index.ts
@ -15,6 +15,9 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import * as path from "path";
|
||||
|
||||
import { Healthz } from "./health/healthz";
|
||||
|
||||
import {
|
||||
LogLevel,
|
||||
LogService,
|
||||
@ -23,10 +26,10 @@ import {
|
||||
RichConsoleLogger,
|
||||
SimpleFsStorageProvider
|
||||
} from "matrix-bot-sdk";
|
||||
|
||||
import { read as configRead } from "./config";
|
||||
import { Healthz } from "./health/healthz";
|
||||
import { Mjolnir } from "./Mjolnir";
|
||||
import { patchMatrixClient } from "./utils";
|
||||
import { initializeSentry, patchMatrixClient } from "./utils";
|
||||
|
||||
|
||||
(async function () {
|
||||
@ -39,6 +42,10 @@ import { patchMatrixClient } from "./utils";
|
||||
|
||||
LogService.info("index", "Starting bot...");
|
||||
|
||||
// Initialize error reporting as early as possible.
|
||||
if (config.health.sentry) {
|
||||
initializeSentry(config);
|
||||
}
|
||||
const healthz = new Healthz(config);
|
||||
healthz.isHealthy = false; // start off unhealthy
|
||||
if (config.health.healthz.enabled) {
|
||||
|
28
src/utils.ts
28
src/utils.ts
@ -24,7 +24,11 @@ import {
|
||||
} from "matrix-bot-sdk";
|
||||
import { ClientRequest, IncomingMessage } from "http";
|
||||
import { default as parseDuration } from "parse-duration";
|
||||
import * as Sentry from '@sentry/node';
|
||||
import * as _ from '@sentry/tracing'; // Performing the import activates tracing.
|
||||
|
||||
import ManagementRoomOutput from "./ManagementRoomOutput";
|
||||
import { IConfig } from "./config";
|
||||
|
||||
// Define a few aliases to simplify parsing durations.
|
||||
|
||||
@ -396,3 +400,27 @@ export function patchMatrixClient() {
|
||||
patchMatrixClientForConciseExceptions();
|
||||
patchMatrixClientForRetry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize Sentry for error monitoring and reporting.
|
||||
*
|
||||
* This method is idempotent. If `config` specifies that Sentry
|
||||
* should not be used, it does nothing.
|
||||
*/
|
||||
export function initializeSentry(config: IConfig) {
|
||||
if (sentryInitialized) {
|
||||
return;
|
||||
}
|
||||
if (config.health.sentry) {
|
||||
// Configure error monitoring with Sentry.
|
||||
let sentry = config.health.sentry;
|
||||
Sentry.init({
|
||||
dsn: sentry.dsn,
|
||||
tracesSampleRate: sentry.tracesSampleRate,
|
||||
});
|
||||
sentryInitialized = true;
|
||||
}
|
||||
}
|
||||
// Set to `true` once we have initialized `Sentry` to ensure
|
||||
// that we do not attempt to initialize it more than once.
|
||||
let sentryInitialized = false;
|
@ -23,7 +23,7 @@ import {
|
||||
} from "matrix-bot-sdk";
|
||||
import { Mjolnir} from '../../src/Mjolnir';
|
||||
import { overrideRatelimitForUser, registerUser } from "./clientHelper";
|
||||
import { patchMatrixClient } from "../../src/utils";
|
||||
import { initializeSentry, patchMatrixClient } from "../../src/utils";
|
||||
import { IConfig } from "../../src/config";
|
||||
|
||||
/**
|
||||
@ -49,6 +49,8 @@ export async function ensureAliasedRoomExists(client: MatrixClient, alias: strin
|
||||
}
|
||||
|
||||
async function configureMjolnir(config: IConfig) {
|
||||
// Initialize error monitoring as early as possible.
|
||||
initializeSentry(config);
|
||||
try {
|
||||
await registerUser(config.homeserverUrl, config.pantalaimon.username, config.pantalaimon.username, config.pantalaimon.password, true)
|
||||
} catch (e) {
|
||||
|
59
yarn.lock
59
yarn.lock
@ -104,6 +104,51 @@
|
||||
domhandler "^4.2.0"
|
||||
selderee "^0.6.0"
|
||||
|
||||
"@sentry/core@7.22.0":
|
||||
version "7.22.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.22.0.tgz#8e50f288e5e8fcaa2774daffd2487e042a392893"
|
||||
integrity sha512-qYJiJrL1mfQQln84mNunBRUkXq7xDGQQoNh4Sz9VYP5698va51zmS5BLYRCZ5CkPwRYNuhUqlUXN7bpYGYOOIA==
|
||||
dependencies:
|
||||
"@sentry/types" "7.22.0"
|
||||
"@sentry/utils" "7.22.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/node@^7.17.2":
|
||||
version "7.22.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.22.0.tgz#d575481e56d3326ad457378db5ab7cc804b712fd"
|
||||
integrity sha512-jKhxqKsbEEaY/g3FTzpj1fwukN0IkNv4V+0Fl+t/EmSNUL/7q5FMmDBa+fFQuQzwps8UEpzqPOzMSRapVsoP0w==
|
||||
dependencies:
|
||||
"@sentry/core" "7.22.0"
|
||||
"@sentry/types" "7.22.0"
|
||||
"@sentry/utils" "7.22.0"
|
||||
cookie "^0.4.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@^7.17.2":
|
||||
version "7.22.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.22.0.tgz#ec29325ee2c5959670097c104e47a78797cef17b"
|
||||
integrity sha512-s68aSnrRaWQ+Z5oG9ozIegUkofZy9PwicuXYEPA8K/R30F1CVpHvDZ/3KlFnByl+aXTbAyLBQzN2sAObB5g4pQ==
|
||||
dependencies:
|
||||
"@sentry/core" "7.22.0"
|
||||
"@sentry/types" "7.22.0"
|
||||
"@sentry/utils" "7.22.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@7.22.0":
|
||||
version "7.22.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.22.0.tgz#58e4ce77b523048e0f31e2ea4b597946d76f6079"
|
||||
integrity sha512-LhCL+wb1Jch+OesB2CIt6xpfO1Ab6CRvoNYRRzVumWPLns1T3ZJkarYfhbLaOEIb38EIbPgREdxn2AJT560U4Q==
|
||||
|
||||
"@sentry/utils@7.22.0":
|
||||
version "7.22.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.22.0.tgz#fb46dc2761e2d44cf70bc3e1fba61d55852904b5"
|
||||
integrity sha512-1GiNw1opIngxg0nktCTc9wibh4/LV12kclrnB9dAOHrqazZXHXZRAkjqrhQphKcMpT+3By91W6EofjaDt5a/hg==
|
||||
dependencies:
|
||||
"@sentry/types" "7.22.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@tootallnate/once@1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
@ -829,6 +874,11 @@ cookie@0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookie@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
core-util-is@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@ -2203,6 +2253,11 @@ lru-cache@^7.10.1:
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.13.1.tgz#267a81fbd0881327c46a81c5922606a2cfe336c4"
|
||||
integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==
|
||||
|
||||
lru_map@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd"
|
||||
integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
@ -3436,9 +3491,9 @@ tsconfig-paths@^3.5.0:
|
||||
minimist "^1.2.0"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^1.13.0, tslib@^1.8.1:
|
||||
tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.3:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslint@^6.1.3:
|
||||
|
Loading…
Reference in New Issue
Block a user