Add a whole lot more logging to the backend

This commit is contained in:
Travis Ralston 2018-03-30 16:34:39 -06:00
parent 39a71429f3
commit ff088e3e49
14 changed files with 55 additions and 18 deletions

View File

@ -1,4 +1,5 @@
import * as memoryCache from "memory-cache"; import * as memoryCache from "memory-cache";
import { LogService } from "matrix-js-snippets";
export class MemoryCache { export class MemoryCache {
@ -30,6 +31,7 @@ class _CacheManager {
public for(namespace: string): MemoryCache { public for(namespace: string): MemoryCache {
let cache = this.caches[namespace]; let cache = this.caches[namespace];
if (!cache) { if (!cache) {
LogService.info("MemoryCache", "Creating a new cache for namespace: " + namespace);
cache = this.caches[namespace] = new MemoryCache(); cache = this.caches[namespace] = new MemoryCache();
} }

View File

@ -62,7 +62,7 @@ export default class Webserver {
parsedUrl.query["scalar_token"] = "redacted"; parsedUrl.query["scalar_token"] = "redacted";
parsedUrl.search = undefined; // to force URL.format to use `query` parsedUrl.search = undefined; // to force URL.format to use `query`
} }
LogService.verbose("Webserver", "Incoming request: " + req.method + " " + URL.format(parsedUrl)); LogService.info("Webserver", "Incoming request: " + req.method + " " + URL.format(parsedUrl));
next(); next();
}); });
this.app.use((_req, res, next) => { this.app.use((_req, res, next) => {

View File

@ -47,7 +47,7 @@ export class AdminAppserviceService {
@POST @POST
@Path("new") @Path("new")
public async createAppservice(@QueryParam("scalar_token") scalarToken: string, request: AppserviceCreateRequest): Promise<AppserviceResponse> { public async createAppservice(@QueryParam("scalar_token") scalarToken: string, request: AppserviceCreateRequest): Promise<AppserviceResponse> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
// Trim off the @ sign if it's on the prefix // Trim off the @ sign if it's on the prefix
if (request.userPrefix[0] === "@") { if (request.userPrefix[0] === "@") {
@ -60,6 +60,7 @@ export class AdminAppserviceService {
} }
const appservice = await AppserviceStore.create(AppserviceStore.getSafeUserId(request.userPrefix)); const appservice = await AppserviceStore.create(AppserviceStore.getSafeUserId(request.userPrefix));
LogService.info("AdminAppserviceService", userId + " created an application service");
return this.mapAppservice(appservice); return this.mapAppservice(appservice);
} }

View File

@ -5,6 +5,7 @@ import { DimensionIntegrationsService } from "../dimension/DimensionIntegrations
import { WidgetStore } from "../../db/WidgetStore"; import { WidgetStore } from "../../db/WidgetStore";
import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache"; import { Cache, CACHE_INTEGRATIONS } from "../../MemoryCache";
import { Integration } from "../../integrations/Integration"; import { Integration } from "../../integrations/Integration";
import { LogService } from "matrix-js-snippets";
interface SetEnabledRequest { interface SetEnabledRequest {
enabled: boolean; enabled: boolean;
@ -24,11 +25,12 @@ export class AdminIntegrationsService {
@POST @POST
@Path(":category/:type/options") @Path(":category/:type/options")
public async setOptions(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetOptionsRequest): Promise<any> { public async setOptions(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetOptionsRequest): Promise<any> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
if (category === "widget") await WidgetStore.setOptions(type, body.options); if (category === "widget") await WidgetStore.setOptions(type, body.options);
else throw new ApiError(400, "Unrecognized category"); else throw new ApiError(400, "Unrecognized category");
LogService.info("AdminIntegrationsService", userId + " updated the integration options for " + category + "/" + type);
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return {}; // 200 OK return {}; // 200 OK
} }
@ -37,11 +39,12 @@ export class AdminIntegrationsService {
@POST @POST
@Path(":category/:type/enabled") @Path(":category/:type/enabled")
public async setEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetEnabledRequest): Promise<any> { public async setEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("category") category: string, @PathParam("type") type: string, body: SetEnabledRequest): Promise<any> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
if (category === "widget") await WidgetStore.setEnabled(type, body.enabled); if (category === "widget") await WidgetStore.setEnabled(type, body.enabled);
else throw new ApiError(400, "Unrecognized category"); else throw new ApiError(400, "Unrecognized category");
LogService.info("AdminIntegrationsService", userId + " set " + category + "/" + type + " to " + (body.enabled ? "enabled" : "disabled"));
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return {}; // 200 OK return {}; // 200 OK
} }

View File

@ -51,8 +51,11 @@ export class AdminNebService {
@POST @POST
@Path(":id/integration/:type/enabled") @Path(":id/integration/:type/enabled")
public async setIntegrationEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, request: SetEnabledRequest): Promise<any> { public async setIntegrationEnabled(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, request: SetEnabledRequest): Promise<any> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
await NebStore.setIntegrationEnabled(nebId, integrationType, request.enabled); await NebStore.setIntegrationEnabled(nebId, integrationType, request.enabled);
LogService.info("AdminNebService", userId + " set the " + integrationType + " on NEB " + nebId + " to " + (request.enabled ? "enabled" : "disabled"));
Cache.for(CACHE_NEB).clear(); Cache.for(CACHE_NEB).clear();
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return {}; // 200 OK return {}; // 200 OK
@ -61,8 +64,11 @@ export class AdminNebService {
@POST @POST
@Path(":id/integration/:type/config") @Path(":id/integration/:type/config")
public async setIntegrationConfig(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, newConfig: any): Promise<any> { public async setIntegrationConfig(@QueryParam("scalar_token") scalarToken: string, @PathParam("id") nebId: number, @PathParam("type") integrationType: string, newConfig: any): Promise<any> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
await NebStore.setIntegrationConfig(nebId, integrationType, newConfig); await NebStore.setIntegrationConfig(nebId, integrationType, newConfig);
LogService.info("AdminNebService", userId + " updated the configuration for " + integrationType + " on NEB " + nebId);
Cache.for(CACHE_NEB).clear(); Cache.for(CACHE_NEB).clear();
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return {}; // 200 OK return {}; // 200 OK
@ -78,10 +84,12 @@ export class AdminNebService {
@POST @POST
@Path("new/upstream") @Path("new/upstream")
public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<NebConfig> { public async newConfigForUpstream(@QueryParam("scalar_token") scalarToken: string, request: CreateWithUpstream): Promise<NebConfig> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
try { try {
const neb = await NebStore.createForUpstream(request.upstreamId); const neb = await NebStore.createForUpstream(request.upstreamId);
LogService.info("AdminNebService", userId + " created a new NEB instance from upstream " + request.upstreamId);
Cache.for(CACHE_NEB).clear(); Cache.for(CACHE_NEB).clear();
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return neb; return neb;
@ -94,10 +102,12 @@ export class AdminNebService {
@POST @POST
@Path("new/appservice") @Path("new/appservice")
public async newConfigForAppservice(@QueryParam("scalar_token") scalarToken: string, request: CreateWithAppservice): Promise<NebConfig> { public async newConfigForAppservice(@QueryParam("scalar_token") scalarToken: string, request: CreateWithAppservice): Promise<NebConfig> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
try { try {
const neb = await NebStore.createForAppservice(request.appserviceId, request.adminUrl); const neb = await NebStore.createForAppservice(request.appserviceId, request.adminUrl);
LogService.info("AdminNebService", userId + " created a new NEB instance from appservice " + request.appserviceId);
Cache.for(CACHE_NEB).clear(); Cache.for(CACHE_NEB).clear();
Cache.for(CACHE_INTEGRATIONS).clear(); Cache.for(CACHE_INTEGRATIONS).clear();
return neb; return neb;

View File

@ -2,6 +2,7 @@ import { GET, Path, POST, QueryParam } from "typescript-rest";
import { AdminService } from "./AdminService"; import { AdminService } from "./AdminService";
import { Cache, CACHE_UPSTREAM } from "../../MemoryCache"; import { Cache, CACHE_UPSTREAM } from "../../MemoryCache";
import Upstream from "../../db/models/Upstream"; import Upstream from "../../db/models/Upstream";
import { LogService } from "matrix-js-snippets";
interface UpstreamRepsonse { interface UpstreamRepsonse {
id: number; id: number;
@ -42,7 +43,7 @@ export class AdminUpstreamService {
@POST @POST
@Path("new") @Path("new")
public async createUpstream(@QueryParam("scalar_token") scalarToken: string, request: NewUpstreamRequest): Promise<UpstreamRepsonse> { public async createUpstream(@QueryParam("scalar_token") scalarToken: string, request: NewUpstreamRequest): Promise<UpstreamRepsonse> {
await AdminService.validateAndGetAdminTokenOwner(scalarToken); const userId = await AdminService.validateAndGetAdminTokenOwner(scalarToken);
const upstream = await Upstream.create({ const upstream = await Upstream.create({
name: request.name, name: request.name,
@ -51,6 +52,7 @@ export class AdminUpstreamService {
apiUrl: request.apiUrl, apiUrl: request.apiUrl,
}); });
LogService.info("AdminUpstreamService", userId + " created a new upstream");
Cache.for(CACHE_UPSTREAM).clear(); Cache.for(CACHE_UPSTREAM).clear();
return this.mapUpstream(upstream); return this.mapUpstream(upstream);
} }

View File

@ -1,4 +1,5 @@
import { GET, Path } from "typescript-rest"; import { GET, Path } from "typescript-rest";
import { LogService } from "matrix-js-snippets";
/** /**
* API for the health of Dimension * API for the health of Dimension
@ -9,6 +10,7 @@ export class DimensionHealthService {
@GET @GET
@Path("heartbeat") @Path("heartbeat")
public async heartbeat(): Promise<any> { public async heartbeat(): Promise<any> {
LogService.info("DimensionHealthService", "Heartbeat called");
return {}; // 200 OK return {}; // 200 OK
} }
} }

View File

@ -84,6 +84,7 @@ export class ScalarService {
isDimensionToken: true, isDimensionToken: true,
}); });
LogService.info("ScalarService", mxUserId + " has registered for a scalar token successfully");
return {scalar_token: dimensionScalarToken.scalarToken}; return {scalar_token: dimensionScalarToken.scalarToken};
} }

View File

@ -2,6 +2,7 @@ import AppService from "./models/AppService";
import AppServiceUser from "./models/AppServiceUser"; import AppServiceUser from "./models/AppServiceUser";
import * as randomString from "random-string"; import * as randomString from "random-string";
import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient"; import { MatrixAppserviceClient } from "../matrix/MatrixAppserviceClient";
import { LogService } from "matrix-js-snippets";
export class AppserviceStore { export class AppserviceStore {
@ -37,22 +38,25 @@ export class AppserviceStore {
public static getSafeUserId(userIdOrPrefix: string): string { public static getSafeUserId(userIdOrPrefix: string): string {
// Force user IDs to be lowercase and strip out characters that aren't permitted // Force user IDs to be lowercase and strip out characters that aren't permitted
// https://matrix.org/docs/spec/appendices.html#user-identifiers // https://matrix.org/docs/spec/appendices.html#user-identifiers
return userIdOrPrefix.toLowerCase().replace(/[^a-z0-9._\-=]/g, '.'); if (userIdOrPrefix.startsWith('@')) {
// we only change out the parts we care about. The rest will be crushed down.
userIdOrPrefix = userIdOrPrefix.replace(/@/g, '=40');
userIdOrPrefix = userIdOrPrefix.replace(/:/g, '=3A');
} }
return userIdOrPrefix.toLowerCase().replace(/[^a-z0-9._\-=]/g, '.');
public static async getUsers(appserviceId: string): Promise<AppServiceUser[]> {
return AppServiceUser.findAll({where: {appserviceId: appserviceId}});
} }
public static async registerUser(appserviceId: string, userId: string): Promise<AppServiceUser> { public static async registerUser(appserviceId: string, userId: string): Promise<AppServiceUser> {
const appservice = await AppService.findOne({where: {id: appserviceId}}); const appservice = await AppService.findOne({where: {id: appserviceId}});
if (!appservice) throw new Error("Appservice not found"); if (!appservice) throw new Error("Appservice not found");
LogService.info("AppserviceStore", "Registering to own " + userId + " in appservice " + appserviceId);
const client = new MatrixAppserviceClient(appservice); const client = new MatrixAppserviceClient(appservice);
const localpart = AppserviceStore.getSafeUserId(userId.substring(1).split(":")[0]); const localpart = AppserviceStore.getSafeUserId(userId.substring(1).split(":")[0]);
const response = await client.registerUser(localpart); const response = await client.registerUser(localpart);
LogService.info("AppserviceStore", "Successfully registered " + userId);
return AppServiceUser.create({ return await AppServiceUser.create({
id: userId, id: userId,
appserviceId: appserviceId, appserviceId: appserviceId,
accessToken: response.access_token, accessToken: response.access_token,

View File

@ -48,6 +48,7 @@ export async function getFederationUrl(serverName: string): Promise<string> {
export async function doFederatedApiCall(method: string, serverName: string, endpoint: string, query?: object, body?: object): Promise<any> { export async function doFederatedApiCall(method: string, serverName: string, endpoint: string, query?: object, body?: object): Promise<any> {
const federationUrl = await getFederationUrl(serverName); const federationUrl = await getFederationUrl(serverName);
LogService.info("matrix", "Doing federated API call: " + federationUrl + endpoint);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ request({
method: method, method: method,
@ -74,6 +75,7 @@ export async function doFederatedApiCall(method: string, serverName: string, end
export async function doClientApiCall(method: string, endpoint: string, query?: object, body?: object): Promise<any> { export async function doClientApiCall(method: string, endpoint: string, query?: object, body?: object): Promise<any> {
let url = config.homeserver.clientServerUrl; let url = config.homeserver.clientServerUrl;
if (url.endsWith("/")) url = url.substring(0, url.length - 1); if (url.endsWith("/")) url = url.substring(0, url.length - 1);
LogService.info("matrix", "Doing client API call: " + url + endpoint);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ request({

View File

@ -60,6 +60,7 @@ export class NebClient {
private doRequest<T>(endpoint: string, body: any): Promise<T> { private doRequest<T>(endpoint: string, body: any): Promise<T> {
const adminUrl = (this.neb.adminUrl.endsWith("/") ? this.neb.adminUrl.substring(0, this.neb.adminUrl.length - 1) : this.neb.adminUrl); const adminUrl = (this.neb.adminUrl.endsWith("/") ? this.neb.adminUrl.substring(0, this.neb.adminUrl.length - 1) : this.neb.adminUrl);
if (!endpoint.startsWith("/")) endpoint = "/" + endpoint; if (!endpoint.startsWith("/")) endpoint = "/" + endpoint;
LogService.info("NebClient", "Doing NEB call: " + adminUrl + endpoint);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ request({

View File

@ -27,7 +27,6 @@ interface InternalTravisCiConfig {
export class NebProxy { export class NebProxy {
constructor(private neb: NebConfig, private requestingUserId: string) { constructor(private neb: NebConfig, private requestingUserId: string) {
} }
public async getBotUserId(integration: NebIntegration): Promise<string> { public async getBotUserId(integration: NebIntegration): Promise<string> {
@ -324,6 +323,7 @@ export class NebProxy {
const apiUrl = upstream.apiUrl.endsWith("/") ? upstream.apiUrl.substring(0, upstream.apiUrl.length - 1) : upstream.apiUrl; const apiUrl = upstream.apiUrl.endsWith("/") ? upstream.apiUrl.substring(0, upstream.apiUrl.length - 1) : upstream.apiUrl;
const url = apiUrl + (endpoint.startsWith("/") ? endpoint : "/" + endpoint); const url = apiUrl + (endpoint.startsWith("/") ? endpoint : "/" + endpoint);
LogService.info("NebProxy", "Doing upstream NEB request: " + url);
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
request({ request({

View File

@ -9,6 +9,7 @@ export class ScalarClient {
} }
public register(openId: OpenId): Promise<ScalarRegisterResponse> { public register(openId: OpenId): Promise<ScalarRegisterResponse> {
LogService.info("ScalarClient", "Doing upstream scalar request: " + this.upstream.scalarUrl + "/register");
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request({ request({
method: "POST", method: "POST",

View File

@ -1,12 +1,20 @@
import { LogService } from "matrix-js-snippets";
import * as git from "git-rev-sync"; import * as git from "git-rev-sync";
let version = "Unknown"; let version = "Unknown";
let gitHash = null;
try { try {
version = "v" + require("../../package.json").version; version = "v" + require("../../package.json").version;
} catch (error) { } catch (error) {
LogService.error("version", error); // The log service isn't set up by the time we require this file
console.error("version", error);
} }
export const CURRENT_VERSION = version + "-" + git.short(); try {
gitHash = git.short();
} catch (error) {
// The log service isn't set up by the time we require this file
console.error("version", error);
}
export const CURRENT_VERSION = version + (gitHash ? "-" + gitHash : "");