Add a new /singleplayer/log route (!160)
Add a new /singleplayer/log route for logging data to the server console from the client Supports: - All server log levels - `Custom` log level with text/background color - Specifying the source of the log line (ex. Plugin name) Example output: ![Example](https://i.imgur.com/c0XBYLm.png) Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/160 Co-authored-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com> Co-committed-by: DrakiaXYZ <drakiaxyz@noreply.dev.sp-tarkov.com>
This commit is contained in:
parent
1f90ae0bb0
commit
170a185928
26
project/src/callbacks/ClientLogCallbacks.ts
Normal file
26
project/src/callbacks/ClientLogCallbacks.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { ClientLogController } from "@spt-aki/controllers/ClientLogController";
|
||||||
|
import { INullResponseData } from "@spt-aki/models/eft/httpResponse/INullResponseData";
|
||||||
|
import { IClientLogRequest } from "@spt-aki/models/spt/logging/IClientLogRequest";
|
||||||
|
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
|
||||||
|
import { inject, injectable } from "tsyringe";
|
||||||
|
|
||||||
|
/** Handle client logging related events */
|
||||||
|
@injectable()
|
||||||
|
export class ClientLogCallbacks
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
|
||||||
|
@inject("ClientLogController") protected clientLogController: ClientLogController
|
||||||
|
)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle /singleplayer/log
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
public clientLog(url: string, info: IClientLogRequest, sessionID: string): INullResponseData
|
||||||
|
{
|
||||||
|
this.clientLogController.clientLog(info);
|
||||||
|
return this.httpResponse.nullResponse();
|
||||||
|
}
|
||||||
|
}
|
57
project/src/controllers/ClientLogController.ts
Normal file
57
project/src/controllers/ClientLogController.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { IClientLogRequest } from "@spt-aki/models/spt/logging/IClientLogRequest";
|
||||||
|
import { LogBackgroundColor } from "@spt-aki/models/spt/logging/LogBackgroundColor";
|
||||||
|
import { LogLevel } from "@spt-aki/models/spt/logging/LogLevel";
|
||||||
|
import { LogTextColor } from "@spt-aki/models/spt/logging/LogTextColor";
|
||||||
|
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||||
|
import { inject, injectable } from "tsyringe";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ClientLogController
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
@inject("WinstonLogger") protected logger: ILogger
|
||||||
|
)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle /singleplayer/log
|
||||||
|
*/
|
||||||
|
public clientLog(logRequest: IClientLogRequest): void
|
||||||
|
{
|
||||||
|
const message = `[${logRequest.Source}] ${logRequest.Message}`;
|
||||||
|
const color = logRequest.Color ?? LogTextColor.WHITE;
|
||||||
|
const backgroundColor = logRequest.BackgroundColor ?? LogBackgroundColor.DEFAULT;
|
||||||
|
|
||||||
|
// Allow supporting either string or enum levels
|
||||||
|
// Required due to the C# modules serializing enums as their name
|
||||||
|
let level = logRequest.Level;
|
||||||
|
if (typeof level === "string")
|
||||||
|
{
|
||||||
|
level = LogLevel[level.toUpperCase() as keyof typeof LogLevel];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (level)
|
||||||
|
{
|
||||||
|
case LogLevel.ERROR:
|
||||||
|
this.logger.error(message);
|
||||||
|
break;
|
||||||
|
case LogLevel.WARN:
|
||||||
|
this.logger.warning(message);
|
||||||
|
break;
|
||||||
|
case LogLevel.SUCCESS:
|
||||||
|
this.logger.success(message);
|
||||||
|
break;
|
||||||
|
case LogLevel.INFO:
|
||||||
|
this.logger.info(message);
|
||||||
|
break;
|
||||||
|
case LogLevel.CUSTOM:
|
||||||
|
this.logger.log(message, color, backgroundColor);
|
||||||
|
break;
|
||||||
|
case LogLevel.DEBUG:
|
||||||
|
this.logger.debug(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.logger.info(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { DependencyContainer, Lifecycle } from "tsyringe";
|
|||||||
|
|
||||||
import { BotCallbacks } from "@spt-aki/callbacks/BotCallbacks";
|
import { BotCallbacks } from "@spt-aki/callbacks/BotCallbacks";
|
||||||
import { BundleCallbacks } from "@spt-aki/callbacks/BundleCallbacks";
|
import { BundleCallbacks } from "@spt-aki/callbacks/BundleCallbacks";
|
||||||
|
import { ClientLogCallbacks } from "@spt-aki/callbacks/ClientLogCallbacks";
|
||||||
import { CustomizationCallbacks } from "@spt-aki/callbacks/CustomizationCallbacks";
|
import { CustomizationCallbacks } from "@spt-aki/callbacks/CustomizationCallbacks";
|
||||||
import { DataCallbacks } from "@spt-aki/callbacks/DataCallbacks";
|
import { DataCallbacks } from "@spt-aki/callbacks/DataCallbacks";
|
||||||
import { DialogueCallbacks } from "@spt-aki/callbacks/DialogueCallbacks";
|
import { DialogueCallbacks } from "@spt-aki/callbacks/DialogueCallbacks";
|
||||||
@ -33,6 +34,7 @@ import { WeatherCallbacks } from "@spt-aki/callbacks/WeatherCallbacks";
|
|||||||
import { WishlistCallbacks } from "@spt-aki/callbacks/WishlistCallbacks";
|
import { WishlistCallbacks } from "@spt-aki/callbacks/WishlistCallbacks";
|
||||||
import { ApplicationContext } from "@spt-aki/context/ApplicationContext";
|
import { ApplicationContext } from "@spt-aki/context/ApplicationContext";
|
||||||
import { BotController } from "@spt-aki/controllers/BotController";
|
import { BotController } from "@spt-aki/controllers/BotController";
|
||||||
|
import { ClientLogController } from "@spt-aki/controllers/ClientLogController";
|
||||||
import { CustomizationController } from "@spt-aki/controllers/CustomizationController";
|
import { CustomizationController } from "@spt-aki/controllers/CustomizationController";
|
||||||
import { DialogueController } from "@spt-aki/controllers/DialogueController";
|
import { DialogueController } from "@spt-aki/controllers/DialogueController";
|
||||||
import { GameController } from "@spt-aki/controllers/GameController";
|
import { GameController } from "@spt-aki/controllers/GameController";
|
||||||
@ -157,6 +159,7 @@ import { ImageSerializer } from "@spt-aki/routers/serializers/ImageSerializer";
|
|||||||
import { NotifySerializer } from "@spt-aki/routers/serializers/NotifySerializer";
|
import { NotifySerializer } from "@spt-aki/routers/serializers/NotifySerializer";
|
||||||
import { BotStaticRouter } from "@spt-aki/routers/static/BotStaticRouter";
|
import { BotStaticRouter } from "@spt-aki/routers/static/BotStaticRouter";
|
||||||
import { BundleStaticRouter } from "@spt-aki/routers/static/BundleStaticRouter";
|
import { BundleStaticRouter } from "@spt-aki/routers/static/BundleStaticRouter";
|
||||||
|
import { ClientLogStaticRouter } from "@spt-aki/routers/static/ClientLogStaticRouter";
|
||||||
import { CustomizationStaticRouter } from "@spt-aki/routers/static/CustomizationStaticRouter";
|
import { CustomizationStaticRouter } from "@spt-aki/routers/static/CustomizationStaticRouter";
|
||||||
import { DataStaticRouter } from "@spt-aki/routers/static/DataStaticRouter";
|
import { DataStaticRouter } from "@spt-aki/routers/static/DataStaticRouter";
|
||||||
import { DialogStaticRouter } from "@spt-aki/routers/static/DialogStaticRouter";
|
import { DialogStaticRouter } from "@spt-aki/routers/static/DialogStaticRouter";
|
||||||
@ -305,6 +308,7 @@ export class Container
|
|||||||
depContainer.registerType("OnUpdate", "SaveCallbacks");
|
depContainer.registerType("OnUpdate", "SaveCallbacks");
|
||||||
|
|
||||||
depContainer.registerType("StaticRoutes", "BotStaticRouter");
|
depContainer.registerType("StaticRoutes", "BotStaticRouter");
|
||||||
|
depContainer.registerType("StaticRoutes", "ClientLogStaticRouter");
|
||||||
depContainer.registerType("StaticRoutes", "CustomizationStaticRouter");
|
depContainer.registerType("StaticRoutes", "CustomizationStaticRouter");
|
||||||
depContainer.registerType("StaticRoutes", "DataStaticRouter");
|
depContainer.registerType("StaticRoutes", "DataStaticRouter");
|
||||||
depContainer.registerType("StaticRoutes", "DialogStaticRouter");
|
depContainer.registerType("StaticRoutes", "DialogStaticRouter");
|
||||||
@ -429,6 +433,7 @@ export class Container
|
|||||||
// Static routes
|
// Static routes
|
||||||
depContainer.register<BotStaticRouter>("BotStaticRouter", { useClass: BotStaticRouter });
|
depContainer.register<BotStaticRouter>("BotStaticRouter", { useClass: BotStaticRouter });
|
||||||
depContainer.register<BundleStaticRouter>("BundleStaticRouter", { useClass: BundleStaticRouter });
|
depContainer.register<BundleStaticRouter>("BundleStaticRouter", { useClass: BundleStaticRouter });
|
||||||
|
depContainer.register<ClientLogStaticRouter>("ClientLogStaticRouter", { useClass: ClientLogStaticRouter });
|
||||||
depContainer.register<CustomizationStaticRouter>("CustomizationStaticRouter", { useClass: CustomizationStaticRouter });
|
depContainer.register<CustomizationStaticRouter>("CustomizationStaticRouter", { useClass: CustomizationStaticRouter });
|
||||||
depContainer.register<DataStaticRouter>("DataStaticRouter", { useClass: DataStaticRouter });
|
depContainer.register<DataStaticRouter>("DataStaticRouter", { useClass: DataStaticRouter });
|
||||||
depContainer.register<DialogStaticRouter>("DialogStaticRouter", { useClass: DialogStaticRouter });
|
depContainer.register<DialogStaticRouter>("DialogStaticRouter", { useClass: DialogStaticRouter });
|
||||||
@ -538,6 +543,7 @@ export class Container
|
|||||||
// Callbacks
|
// Callbacks
|
||||||
depContainer.register<BotCallbacks>("BotCallbacks", { useClass: BotCallbacks });
|
depContainer.register<BotCallbacks>("BotCallbacks", { useClass: BotCallbacks });
|
||||||
depContainer.register<BundleCallbacks>("BundleCallbacks", { useClass: BundleCallbacks });
|
depContainer.register<BundleCallbacks>("BundleCallbacks", { useClass: BundleCallbacks });
|
||||||
|
depContainer.register<ClientLogCallbacks>("ClientLogCallbacks", { useClass: ClientLogCallbacks });
|
||||||
depContainer.register<CustomizationCallbacks>("CustomizationCallbacks", { useClass: CustomizationCallbacks });
|
depContainer.register<CustomizationCallbacks>("CustomizationCallbacks", { useClass: CustomizationCallbacks });
|
||||||
depContainer.register<DataCallbacks>("DataCallbacks", { useClass: DataCallbacks });
|
depContainer.register<DataCallbacks>("DataCallbacks", { useClass: DataCallbacks });
|
||||||
depContainer.register<DialogueCallbacks>("DialogueCallbacks", { useClass: DialogueCallbacks });
|
depContainer.register<DialogueCallbacks>("DialogueCallbacks", { useClass: DialogueCallbacks });
|
||||||
@ -632,6 +638,7 @@ export class Container
|
|||||||
{
|
{
|
||||||
// Controllers
|
// Controllers
|
||||||
depContainer.register<BotController>("BotController", { useClass: BotController });
|
depContainer.register<BotController>("BotController", { useClass: BotController });
|
||||||
|
depContainer.register<ClientLogController>("ClientLogController", { useClass: ClientLogController });
|
||||||
depContainer.register<CustomizationController>("CustomizationController", { useClass: CustomizationController });
|
depContainer.register<CustomizationController>("CustomizationController", { useClass: CustomizationController });
|
||||||
depContainer.register<DialogueController>("DialogueController", { useClass: DialogueController });
|
depContainer.register<DialogueController>("DialogueController", { useClass: DialogueController });
|
||||||
depContainer.register<GameController>("GameController", { useClass: GameController });
|
depContainer.register<GameController>("GameController", { useClass: GameController });
|
||||||
|
10
project/src/models/spt/logging/IClientLogRequest.ts
Normal file
10
project/src/models/spt/logging/IClientLogRequest.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { LogLevel } from "@spt-aki/models/spt/logging/LogLevel";
|
||||||
|
|
||||||
|
export interface IClientLogRequest
|
||||||
|
{
|
||||||
|
Source: string
|
||||||
|
Level: LogLevel | string
|
||||||
|
Message: string
|
||||||
|
Color?: string
|
||||||
|
BackgroundColor?: string
|
||||||
|
}
|
9
project/src/models/spt/logging/LogLevel.ts
Normal file
9
project/src/models/spt/logging/LogLevel.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export enum LogLevel
|
||||||
|
{
|
||||||
|
ERROR = 0,
|
||||||
|
WARN = 1,
|
||||||
|
SUCCESS = 2,
|
||||||
|
INFO = 3,
|
||||||
|
CUSTOM = 4,
|
||||||
|
DEBUG = 5
|
||||||
|
}
|
26
project/src/routers/static/ClientLogStaticRouter.ts
Normal file
26
project/src/routers/static/ClientLogStaticRouter.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { inject, injectable } from "tsyringe";
|
||||||
|
|
||||||
|
import { ClientLogCallbacks } from "@spt-aki/callbacks/ClientLogCallbacks";
|
||||||
|
import { RouteAction, StaticRouter } from "@spt-aki/di/Router";
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class ClientLogStaticRouter extends StaticRouter
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
@inject("ClientLogCallbacks") protected clientLogCallbacks: ClientLogCallbacks
|
||||||
|
)
|
||||||
|
{
|
||||||
|
super(
|
||||||
|
[
|
||||||
|
new RouteAction(
|
||||||
|
"/singleplayer/log",
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
(url: string, info: any, sessionID: string, output: string): any =>
|
||||||
|
{
|
||||||
|
return this.clientLogCallbacks.clientLog(url, info, sessionID);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user