@itwin/core-common
Version:
iTwin.js components common to frontend and backend
132 lines • 6.74 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module RpcInterface
*/
import { BentleyStatus, Logger } from "@itwin/core-bentley";
import { CommonLoggerCategory } from "../../CommonLoggerCategory";
import { IModelError } from "../../IModelError";
import { RpcProtocolEvent } from "../core/RpcConstants";
import { RpcInvocation } from "../core/RpcInvocation";
import { WebAppRpcRequest } from "./WebAppRpcRequest";
/* eslint-disable @typescript-eslint/no-deprecated */
/** @internal */
const BACKEND = Symbol.for("@itwin.WebAppRpcLogging.Backend");
const FRONTEND = Symbol.for("@itwin.WebAppRpcLogging.Frontend");
/** @internal */
export class WebAppRpcLogging {
static initializeBackend(instance) {
globalThis[BACKEND] = instance;
}
static initializeFrontend(instance) {
globalThis[FRONTEND] = instance;
}
static get backend() {
const instance = globalThis[BACKEND];
if (typeof (instance) === "undefined") {
throw new IModelError(BentleyStatus.ERROR, "Backend logging is not initialized.");
}
return instance;
}
static get frontend() {
const instance = globalThis[FRONTEND];
if (typeof (instance) === "undefined") {
throw new IModelError(BentleyStatus.ERROR, "Frontend logging is not initialized.");
}
return instance;
}
static async logProtocolEvent(event, object) {
if (object instanceof WebAppRpcRequest) {
await WebAppRpcLogging.frontend.logProtocolEvent(event, object);
}
else if (object instanceof RpcInvocation) {
await WebAppRpcLogging.backend.logProtocolEvent(event, object);
}
}
getRpcInterfaceName(g) {
return (typeof g === "string") ? g : g.interfaceName;
}
findPathIds(path) {
let iTwinId = "";
let iModelId = "";
const tokens = path.split("/");
for (let i = 0; i !== tokens.length; ++i) {
// For backwards compatibility, find old "context" or current "iTwin" terminology
if ((/^context$/i).test(tokens[i]) || (/^itwin$/i).test(tokens[i])) {
iTwinId = tokens[i + 1] || "";
}
if ((/^imodel$/i).test(tokens[i])) {
iModelId = tokens[i + 1] || "";
}
}
return { iTwinId, iModelId };
}
buildOperationDescriptor(operation) {
if (!operation) {
return "unknown.unknown";
}
const interfaceName = typeof (operation.interfaceDefinition) === "string" ? operation.interfaceDefinition : operation.interfaceDefinition.interfaceName;
const operationName = operation.operationName;
return `${interfaceName}.${operationName}`;
}
logRequest(loggerCategory, message, object) {
const operationDescriptor = this.buildOperationDescriptor(object.operation);
const pathIds = this.findPathIds(object.path);
Logger.logTrace(loggerCategory, `${message}.${operationDescriptor}`, () => ({
method: object.method,
path: object.path,
operation: object.operation.operationName,
rpcInterface: this.getRpcInterfaceName(object.operation.interfaceDefinition),
// Alert! The following properties are required by Bentley DevOps standards. Do not change their names!
ActivityId: object.id, // eslint-disable-line @typescript-eslint/naming-convention
TimeElapsed: ("elapsed" in object) ? object.elapsed : 0, // eslint-disable-line @typescript-eslint/naming-convention
MachineName: this.getHostname(), // eslint-disable-line @typescript-eslint/naming-convention
...pathIds,
}));
}
logResponse(loggerCategory, message, object, status, elapsed) {
const operationDescriptor = this.buildOperationDescriptor(object.operation);
const pathIds = this.findPathIds(object.path);
Logger.logTrace(loggerCategory, `${message}.${operationDescriptor}`, () => ({
method: object.method,
path: object.path,
operation: object.operation.operationName,
rpcInterface: this.getRpcInterfaceName(object.operation.interfaceDefinition),
status,
// Alert! The following properties are required by Bentley DevOps standards. Do not change their names!
ActivityId: object.id, // eslint-disable-line @typescript-eslint/naming-convention
TimeElapsed: elapsed, // eslint-disable-line @typescript-eslint/naming-convention
MachineName: this.getHostname(), // eslint-disable-line @typescript-eslint/naming-convention
...pathIds,
}));
}
}
class WebAppRpcLoggingFrontend extends WebAppRpcLogging {
async logProtocolEvent(event, object) {
switch (event) {
case RpcProtocolEvent.RequestCreated: return this.logRequest(CommonLoggerCategory.RpcInterfaceFrontend, "RpcInterface.frontend.request", object);
case RpcProtocolEvent.ResponseLoaded: return this.logResponse(CommonLoggerCategory.RpcInterfaceFrontend, "RpcInterface.frontend.response", object, object.metadata.status, object.elapsed);
case RpcProtocolEvent.ConnectionErrorReceived: return this.logErrorFrontend("RpcInterface.frontend.connectionError", object);
case RpcProtocolEvent.ConnectionAborted: return this.logErrorFrontend("RpcInterface.frontend.connectionAborted", object);
}
}
getHostname() {
return globalThis.window?.location?.host ?? "imodeljs-mobile";
}
logErrorFrontend(message, request) {
const operationDescriptor = this.buildOperationDescriptor(request.operation);
const pathIds = this.findPathIds(request.path);
Logger.logInfo(CommonLoggerCategory.RpcInterfaceFrontend, `${message}.${operationDescriptor}`, () => ({
method: request.method,
path: request.path,
// Alert! The following properties are required by Bentley DevOps standards. Do not change their names!
ActivityId: request.id, // eslint-disable-line @typescript-eslint/naming-convention
MachineName: this.getHostname(), // eslint-disable-line @typescript-eslint/naming-convention
...pathIds,
}));
}
}
WebAppRpcLogging.initializeFrontend(new WebAppRpcLoggingFrontend());
//# sourceMappingURL=WebAppRpcLogging.js.map