diff --git a/src/bot.ts b/src/bot.ts index a4ad3f165b76522bae8c7a28d2929214da578c02..388b017e47c9afb35ac0d58f50ab4ac1d85f2765 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -392,6 +392,7 @@ export class DiscordBot { } const channel = guild.channels.get(room); if (channel && channel.type === "text") { + this.ClientFactory.bindMetricsToChannel(channel as Discord.TextChannel); const lookupResult = new ChannelLookupResult(); lookupResult.channel = channel as Discord.TextChannel; lookupResult.botUser = this.bot.user.id === client.user.id; @@ -451,6 +452,7 @@ export class DiscordBot { // NOTE: Don't send replies to discord if we are a puppet. msg = await chan.send(embed.description, opts); } else if (hook) { + MetricPeg.get.remoteCall("hook.send"); msg = await hook.send(embed.description, { avatarURL: embed!.author!.icon_url, embeds: embedSet.replyEmbed ? [embedSet.replyEmbed] : undefined, @@ -542,6 +544,7 @@ export class DiscordBot { if (guild) { const channel = client.channels.get(entry.remote!.get("discord_channel") as string); if (channel) { + this.ClientFactory.bindMetricsToChannel(channel as Discord.TextChannel); return channel; } throw Error("Channel given in room entry not found"); diff --git a/src/clientfactory.ts b/src/clientfactory.ts index 54e2694ca422b4a611867f2eccb2c21dee170303..22ded344c65d527239db79d357ea56324f4c08a1 100644 --- a/src/clientfactory.ts +++ b/src/clientfactory.ts @@ -16,14 +16,12 @@ limitations under the License. import { DiscordBridgeConfigAuth } from "./config"; import { DiscordStore } from "./store"; -import { Client as DiscordClient } from "discord.js"; +import { Client as DiscordClient, TextChannel } from "discord.js"; import { Log } from "./log"; -import { Util } from "./util"; +import { MetricPeg } from "./metrics"; const log = new Log("ClientFactory"); -const READY_TIMEOUT = 30000; - export class DiscordClientFactory { private config: DiscordBridgeConfigAuth; private store: DiscordStore; @@ -110,4 +108,12 @@ export class DiscordClientFactory { return this.botClient; } } + + public bindMetricsToChannel(channel: TextChannel) { + // tslint:disable-next-line:only-arrow-functions + channel.send = function() { + MetricPeg.get.remoteCall("channel.send"); + return channel.send.apply(channel, arguments); + }; + } } diff --git a/src/discordas.ts b/src/discordas.ts index c39c2dba72f8d0add2ba3800b64788408cb3b363..2e989ceaab9566e27befd17dda64acb0c52db876 100644 --- a/src/discordas.ts +++ b/src/discordas.ts @@ -163,15 +163,14 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) { + "The config option userStorePath no longer has any use."); } - await bridge.run(port, config); log.info(`Started listening on port ${port}`); if (config.bridge.enableMetrics) { log.info("Enabled metrics"); - MetricPeg.setMetrics(new PrometheusBridgeMetrics().init(bridge)); + MetricPeg.set(new PrometheusBridgeMetrics().init(bridge)); } - + try { await store.init(undefined, bridge.getRoomStore(), bridge.getUserStore()); } catch (ex) { diff --git a/src/metrics.ts b/src/metrics.ts index 698c0daf645d88173f8bba3a21cf9cd61d05bfbd..e322c77b54630f03f48867fcbdd6161bd8adab03 100644 --- a/src/metrics.ts +++ b/src/metrics.ts @@ -1,4 +1,4 @@ -import { PrometheusMetrics } from "matrix-appservice-bridge"; +import { PrometheusMetrics, Bridge } from "matrix-appservice-bridge"; import { Gauge, Counter, Histogram } from "prom-client"; import { Log } from "./log"; @@ -7,7 +7,7 @@ const log = new Log("BridgeMetrics"); const REQUEST_EXPIRE_TIME_MS = 30000; interface IAgeCounter { - setGauge(gauge: Gauge, morelabels: any); + setGauge(gauge: Gauge, morelabels: string[]); bump(age: number); } @@ -31,25 +31,24 @@ export interface IBridgeMetrics { } export class DummyBridgeMetrics implements IBridgeMetrics { - registerRequest() {} - requestOutcome() {} - remoteCall() {} - setPresenceCount() {} - storeCall() {} + public registerRequest() {} + public requestOutcome() {} + public remoteCall() {} + public setPresenceCount() {} + public storeCall() {} } export class MetricPeg { - private static _metrics: IBridgeMetrics = new DummyBridgeMetrics(); - - public static get get() : IBridgeMetrics { - return this._metrics; + public static get get(): IBridgeMetrics { + return this.metrics; } - - public static setMetrics(metrics: IBridgeMetrics) { - this._metrics = metrics; + + public static set(metrics: IBridgeMetrics) { + this.metrics = metrics; } -} + private static metrics: IBridgeMetrics = new DummyBridgeMetrics(); +} export class PrometheusBridgeMetrics implements IBridgeMetrics { private metrics; @@ -60,45 +59,46 @@ export class PrometheusBridgeMetrics implements IBridgeMetrics { private matrixRequest: Histogram; private requestsInFlight: Map<string, number>; private bridgeGauges: IBridgeGauges = { - matrixRoomConfigs: 0, - remoteRoomConfigs: 0, matrixGhosts: 0, - remoteGhosts: 0, + matrixRoomConfigs: 0, matrixRoomsByAge: new AgeCounters(), - remoteRoomsByAge: new AgeCounters(), matrixUsersByAge: new AgeCounters(), + remoteGhosts: 0, + remoteRoomConfigs: 0, + remoteRoomsByAge: new AgeCounters(), remoteUsersByAge: new AgeCounters(), }; - public init(bridge: any) { + public init(bridge: Bridge) { this.metrics = new PrometheusMetrics(); this.metrics.registerMatrixSdkMetrics(); this.metrics.registerBridgeGauges(() => this.bridgeGauges); this.metrics.addAppServicePath(bridge); this.remoteCallCounter = this.metrics.addCounter({ - name: "remote_api_calls", help: "Count of remote API calls made", labels: ["method"], + name: "remote_api_calls", }); this.storeCallCounter = this.metrics.addCounter({ - name: "store_calls", help: "Count of store function calls made", labels: ["method", "cached"], + name: "store_calls", }); this.presenceGauge = this.metrics.addGauge({ - name: "active_presence_users", help: "Count of users in the presence queue", labels: [], + + name: "active_presence_users", }); this.matrixRequest = this.metrics.addTimer({ - name: "matrix_request_seconds", help: "Histogram of processing durations of received Matrix messages", labels: ["outcome"], + name: "matrix_request_seconds", }); this.remoteRequest = this.metrics.addTimer({ - name: "remote_request_seconds", help: "Histogram of processing durations of received remote messages", labels: ["outcome"], + name: "remote_request_seconds", }); this.requestsInFlight = new Map(); setInterval(() => { @@ -107,7 +107,7 @@ export class PrometheusBridgeMetrics implements IBridgeMetrics { this.requestsInFlight.delete(id); } }); - }, REQUEST_EXPIRE_TIME_MS) + }, REQUEST_EXPIRE_TIME_MS); return this; } @@ -121,7 +121,7 @@ export class PrometheusBridgeMetrics implements IBridgeMetrics { if (!startTime) { log.verbose(`Got "requestOutcome" for ${id}, but this request was never started`); return; - } + } const duration = Date.now() - startTime; (isRemote ? this.remoteRequest : this.matrixRequest).observe({outcome}, duration / 1000); } @@ -137,5 +137,4 @@ export class PrometheusBridgeMetrics implements IBridgeMetrics { public storeCall(method: string, cached: boolean) { this.storeCallCounter.inc({method, cached: cached ? "yes" : "no"}); } -}; - +} diff --git a/test/mocks/discordclientfactory.ts b/test/mocks/discordclientfactory.ts index 803dedc118f7cacbb9c44443840dee85c06c4c4b..b174f3d5f9455581b13c2a3460f4eee4a32caa7d 100644 --- a/test/mocks/discordclientfactory.ts +++ b/test/mocks/discordclientfactory.ts @@ -31,4 +31,6 @@ export class DiscordClientFactory { } return this.botClient; } + + public bindMetricsToChannel() {} } diff --git a/test/test_discordbot.ts b/test/test_discordbot.ts index f6208be1d18c3d8d32377f56c27a94458ddcde8f..173517038ad91f19d6b10305187c00862ec3ae97 100644 --- a/test/test_discordbot.ts +++ b/test/test_discordbot.ts @@ -453,7 +453,7 @@ describe("DiscordBot", () => { bot.lockChannel(chan); await bot.waitUnlock(chan); const diff = Date.now() - t; - expect(diff).to.be.greaterThan(SHORTDELAY - 1); + expect(diff).to.be.greaterThan(SHORTDELAY - 5); }); }); // }); diff --git a/tslint.json b/tslint.json index cd7de561c3b3ba02450df7cb5bf011afd9c73be2..ff80b039be7c4830651871937b672e77c31faca3 100644 --- a/tslint.json +++ b/tslint.json @@ -9,7 +9,7 @@ "object-literal-sort-keys": "off", "no-any": true, "arrow-return-shorthand": true, - "no-magic-numbers": true, + "no-magic-numbers": [true, -1, 0, 1, 1000], "prefer-for-of": true, "typedef": { "severity": "warning"