Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Add Log Prefix for logs in an instance #275

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions nodecg-io-core/extension/__tests__/instanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ describe("InstanceManager", () => {
expect(instanceManager.deleteServiceInstance(testInstance)).toBe(true);

expect(testService.stopClient).toHaveBeenCalledTimes(1);
expect(testService.stopClient).toHaveBeenCalledWith(testServiceInstance.client);
expect(testService.stopClient.mock.calls[0][0]).toBe(testServiceInstance.client);
});

test("should log error if client cannot be stopped because the service could not be found", () => {
Expand Down Expand Up @@ -344,7 +344,7 @@ describe("InstanceManager", () => {
expect(inst.client).toBeDefined();
expect(inst.client?.()).toBe(inst.config);
expect(testService.createClient).toHaveBeenCalledTimes(1);
expect(testService.createClient).toHaveBeenCalledWith(inst.config);
expect(testService.createClient.mock.calls[0][0]).toBe(inst.config);
});

test("should create client if no config is required, even if config is undefined", async () => {
Expand Down Expand Up @@ -477,7 +477,7 @@ describe("InstanceManager", () => {

expect(svc.removeHandlers).not.toHaveBeenCalled();
expect(svc.createClient).toHaveBeenCalledTimes(1);
expect(svc.createClient).toHaveBeenCalledWith(inst.config);
expect(svc.createClient.mock.calls[0][0]).toBe(inst.config);
});

test("should do nothing if removeHandlers is not implemented by the service", () => {
Expand Down
4 changes: 2 additions & 2 deletions nodecg-io-core/extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { InstanceManager } from "./instanceManager";
import { Service } from "./service";
import { PersistenceManager } from "./persistenceManager";
import { ServiceProvider } from "./serviceProvider";

import { Logger } from "./utils/logger";
/**
* Main type of NodeCG extension that the core bundle exposes.
* Contains references to all internal modules.
Expand Down Expand Up @@ -88,7 +88,7 @@ function onExit(
if (!service.failed && client) {
nodecg.log.info(`Stopping service ${key} of type ${service.result.serviceType}.`);
try {
service.result.stopClient(client);
service.result.stopClient(client, new Logger(key, nodecg));
} catch (err) {
nodecg.log.info(
`Could not stop service ${key} of type ${service.result.serviceType}: ${String(err)}`,
Expand Down
15 changes: 9 additions & 6 deletions nodecg-io-core/extension/instanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ServiceManager } from "./serviceManager";
import { BundleManager } from "./bundleManager";
import Ajv from "ajv";
import { EventEmitter } from "events";

import { Logger } from "./utils/logger";
/**
* Manages instances of services and their configs/clients.
*/
Expand Down Expand Up @@ -105,7 +105,7 @@ export class InstanceManager extends EventEmitter {
} else {
this.nodecg.log.info(`Successfully stopped client of service instance "${instanceName}".`);
try {
svc.result.stopClient(instance.client);
svc.result.stopClient(instance.client, new Logger(instanceName, this.nodecg));
} catch (e) {
this.nodecg.log.error(`Couldn't stop service instance: ${e}`);
}
Expand Down Expand Up @@ -172,7 +172,10 @@ export class InstanceManager extends EventEmitter {

// Validation by the service.
try {
const validationRes = await service.result.validateConfig(config);
const validationRes = await service.result.validateConfig(
config,
new Logger(instanceName, this.nodecg),
);
if (validationRes.failed) {
throw validationRes.errorMessage;
}
Expand Down Expand Up @@ -220,7 +223,7 @@ export class InstanceManager extends EventEmitter {
// If the service does not require a config we can safely ignore the undefined error because in that case
// passing undefined is the intended behavior.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const client = await service.createClient(inst.config!);
const client = await service.createClient(inst.config!, new Logger(instanceName, this.nodecg));

// Check if a error happened while creating the client
if (client.failed) {
Expand All @@ -230,7 +233,7 @@ export class InstanceManager extends EventEmitter {
inst.client = client.result;
}
} catch (err) {
const msg = `The "${inst.serviceType}" service produced an error while creating a client: ${err}`;
const msg = `The "${inst.serviceType}" service with the name "${instanceName}" produced an error while creating a client: ${err}`;
this.nodecg.log.error(msg);
inst.client = undefined;
return error(msg);
Expand All @@ -244,7 +247,7 @@ export class InstanceManager extends EventEmitter {
if (oldClient !== undefined) {
this.nodecg.log.info(`Stopping old unused ${inst.serviceType} client...`);
try {
service.stopClient(oldClient);
service.stopClient(oldClient, new Logger(instanceName, this.nodecg));
} catch (e) {
this.nodecg.log.error(`Couldn't stop service instance: ${e}`);
}
Expand Down
10 changes: 7 additions & 3 deletions nodecg-io-core/extension/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Result } from "./utils/result";
import { ServiceProvider } from "./serviceProvider";
import { Logger } from "./utils/logger";

/**
* Models a map using a object, instead of a iterator like the javascript es6 map.
Expand Down Expand Up @@ -48,26 +49,29 @@ export interface Service<R, C> {
* This function validates the passed config after it has been validated against the json schema (if applicable).
* Should make deeper checks like checking validity of auth tokens.
* @param config the config which should be validated.
* @param logger the logger which logs with the instance.name as prefix
* @return void if the config passes validation and an error string describing the issue if not.
*/
validateConfig(config: R): Promise<Result<void>>;
validateConfig(config: R, logger: Logger): Promise<Result<void>>;

/**
* Creates a client to the service using the validated config.
* The returned result will be passed to bundles and they should be able to use the service with this returned client.
*
* @param config the user provided config for the service.
* @param logger the logger which logs with the instance.name as prefix
* @return the client if everything went well and an error string describing the issue if a error occured.
*/
createClient(config: R): Promise<Result<C>>;
createClient(config: R, logger: Logger): Promise<Result<C>>;

/**
* Stops a client of this service that is not needed anymore.
* Services should close any connections that might exist here.
*
* @param client the client that needs to be stopped.
* @param logger the logger which logs with the instance.name as prefix
*/
stopClient(client: C): void;
stopClient(client: C, logger: Logger): void;

/**
* Removes all handlers from a service client.
Expand Down
11 changes: 7 additions & 4 deletions nodecg-io-core/extension/serviceBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Result } from "./utils/result";

import * as fs from "fs";
import * as path from "path";
import { Logger } from "./utils/logger";

/**
* Class helping to create a nodecg-io service
Expand Down Expand Up @@ -42,7 +43,6 @@ export abstract class ServiceBundle<R, C> implements Service<R, C> {
this.nodecg = nodecg;
this.serviceType = serviceName;
this.schema = this.readSchema(pathSegments);

this.nodecg.log.info(this.serviceType + " bundle started.");
this.core = this.nodecg.extensions["nodecg-io-core"] as unknown as NodeCGIOCore | undefined;
if (this.core === undefined) {
Expand All @@ -64,26 +64,29 @@ export abstract class ServiceBundle<R, C> implements Service<R, C> {
* This function validates the passed config after it has been validated against the json schema (if applicable).
* Should make deeper checks like checking validity of auth tokens.
* @param config the config which should be validated.
* @param logger the logger which logs with the instance.name as prefix
* @return void if the config passes validation and an error string describing the issue if not.
*/
abstract validateConfig(config: R): Promise<Result<void>>;
abstract validateConfig(config: R, logger: Logger): Promise<Result<void>>;

/**
* Creates a client to the service using the validated config.
* The returned result will be passed to bundles and they should be able to use the service with this returned client.
*
* @param config the user provided config for the service.
* @param logger the logger which logs with the instance.name as prefix
* @return the client if everything went well and an error string describing the issue if a error occured.
*/
abstract createClient(config: R): Promise<Result<C>>;
abstract createClient(config: R, logger: Logger): Promise<Result<C>>;

/**
* Stops a client of this service that is not needed anymore.
* Services should close any connections that might exist here.
*
* @param client the client that needs to be stopped.
* @param logger the logger which logs with the instance.name as prefix
*/
abstract stopClient(client: C): void;
abstract stopClient(client: C, logger: Logger): void;

/**
* Removes all handlers from a service client.
Expand Down
25 changes: 25 additions & 0 deletions nodecg-io-core/extension/utils/logger/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { NodeCG } from "nodecg-types/types/server";

export class Logger {
constructor(private name: string, private nodecg: NodeCG) {}
trace(...args: any[]): void {
this.nodecg.log.trace(`[${this.name}] ${args[0]}`, ...args.slice(1));
}

debug(...args: any[]): void {
this.nodecg.log.debug(`[${this.name}] ${args[0]}`, ...args.slice(1));
}

info(...args: any[]): void {
this.nodecg.log.info(`[${this.name}] ${args[0]}`, ...args.slice(1));
}

warn(...args: any[]): void {
this.nodecg.log.warn(`[${this.name}] ${args[0]}`, ...args.slice(1));
}

error(...args: any[]): void {
this.nodecg.log.error(`[${this.name}] ${args[0]}`, ...args.slice(1));
}
}
1 change: 1 addition & 0 deletions nodecg-io-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export type { ObjectMap, Service, ServiceDependency, ServiceInstance } from "./e
export * from "./extension/utils/result";
export * from "./extension/serviceBundle";
export * from "./extension/serviceProvider";
export * from "./extension/utils/logger";
6 changes: 3 additions & 3 deletions services/nodecg-io-ahk/extension/AHK.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import fetch from "node-fetch";
import { NodeCG } from "nodecg-types/types/server";
import { Logger } from "nodecg-io-core";

export class AHK {
private readonly address: string;

public constructor(private nodecg: NodeCG, host: string, port: number) {
public constructor(private logger: Logger, host: string, port: number) {
this.address = `http://${host}:${port}`;
}

Expand All @@ -17,7 +17,7 @@ export class AHK {
try {
await fetch(`${this.address}/send/${command}`, { method: "GET" });
} catch (err) {
this.nodecg.log.error(`Error while using the AHK Connector: ${err}`);
this.logger.error(`Error while using the AHK Connector: ${err}`);
}
}
}
10 changes: 5 additions & 5 deletions services/nodecg-io-ahk/extension/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NodeCG } from "nodecg-types/types/server";
import { Result, emptySuccess, success, ServiceBundle } from "nodecg-io-core";
import { Result, emptySuccess, success, ServiceBundle, Logger } from "nodecg-io-core";
import { AHK } from "./AHK";

interface AHKServiceConfig {
Expand All @@ -14,14 +14,14 @@ module.exports = (nodecg: NodeCG) => {
};

class AhkService extends ServiceBundle<AHKServiceConfig, AHKServiceClient> {
async validateConfig(config: AHKServiceConfig): Promise<Result<void>> {
const ahk = new AHK(this.nodecg, config.host, config.port);
async validateConfig(config: AHKServiceConfig, logger: Logger): Promise<Result<void>> {
const ahk = new AHK(logger, config.host, config.port);
await ahk.testConnection(); // Will throw an error if server doesn't exist.
return emptySuccess();
}

async createClient(config: AHKServiceConfig): Promise<Result<AHKServiceClient>> {
const ahk = new AHK(this.nodecg, config.host, config.port);
async createClient(config: AHKServiceConfig, logger: Logger): Promise<Result<AHKServiceClient>> {
const ahk = new AHK(logger, config.host, config.port);
return success(ahk);
}

Expand Down
6 changes: 3 additions & 3 deletions services/nodecg-io-android/extension/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { spawn } from "child_process";
import { onExit, readableToString } from "@rauschma/stringio";
import { AddressInfo } from "net";
import { buffer as readableToBuffer } from "get-stream";
import { NodeCG } from "nodecg-types/types/server";
import { Logger } from "nodecg-io-core";

/**
* Represents an android device that is connected via ADB.
Expand All @@ -29,7 +29,7 @@ export class Android {
public readonly contactManager: ContactManager;
public readonly fileManager: FileManager;

constructor(private nodecg: NodeCG, device: string) {
constructor(private logger: Logger, device: string) {
this.device = device;
this.connected = false;

Expand Down Expand Up @@ -95,7 +95,7 @@ export class Android {
try {
await handler();
} catch (err) {
this.nodecg.log.error(`A disconnect handler for nodecg-io-android threw an error: ${err}`);
this.logger.error(`A disconnect handler for nodecg-io-android threw an error: ${err}`);
}
}
await this.rawRequest("cancel_all_subscriptions", {});
Expand Down
16 changes: 8 additions & 8 deletions services/nodecg-io-android/extension/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NodeCG } from "nodecg-types/types/server";
import { Result, emptySuccess, success, ServiceBundle } from "nodecg-io-core";
import { Result, emptySuccess, success, ServiceBundle, Logger } from "nodecg-io-core";
import { Android } from "./android";

interface AndroidServiceConfig {
Expand All @@ -13,25 +13,25 @@ module.exports = (nodecg: NodeCG) => {
};

class AndroidService extends ServiceBundle<AndroidServiceConfig, AndroidServiceClient> {
async validateConfig(config: AndroidServiceConfig): Promise<Result<void>> {
const client = new Android(this.nodecg, config.device);
async validateConfig(config: AndroidServiceConfig, logger: Logger): Promise<Result<void>> {
const client = new Android(logger, config.device);
await client.connect();
await client.disconnect();
return emptySuccess();
}

async createClient(config: AndroidServiceConfig): Promise<Result<AndroidServiceClient>> {
const client = new Android(this.nodecg, config.device);
async createClient(config: AndroidServiceConfig, logger: Logger): Promise<Result<AndroidServiceClient>> {
const client = new Android(logger, config.device);
await client.connect();
this.nodecg.log.info("Successfully connected to adb.");
logger.info("Successfully connected to adb.");
return success(client);
}

async stopClient(client: AndroidServiceClient): Promise<void> {
async stopClient(client: AndroidServiceClient, logger: Logger): Promise<void> {
try {
await client.disconnect();
} catch (err) {
this.nodecg.log.error(err);
logger.error(err);
// Do nothing. If we did not catch it it'd cause an infinite loop.
}
}
Expand Down
6 changes: 3 additions & 3 deletions services/nodecg-io-artnet/extension/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NodeCG } from "nodecg-types/types/server";
import { Result, emptySuccess, success, ServiceBundle } from "nodecg-io-core";
import { Result, emptySuccess, success, ServiceBundle, Logger } from "nodecg-io-core";
export { ArtNetServiceClient } from "./artnetServiceClient";
import { ArtNetServiceClient } from "./artnetServiceClient";

Expand All @@ -26,9 +26,9 @@ class ArtNetService extends ServiceBundle<ArtNetServiceConfig, ArtNetServiceClie
return success(client);
}

stopClient(client: ArtNetServiceClient): void {
stopClient(client: ArtNetServiceClient, logger: Logger): void {
client.close();
this.nodecg.log.info("Successfully stopped the Art-Net service.");
logger.info("Successfully stopped the Art-Net service.");
}

removeHandlers(client: ArtNetServiceClient): void {
Expand Down
10 changes: 5 additions & 5 deletions services/nodecg-io-curseforge/extension/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NodeCG } from "nodecg-types/types/server";
import { Result, emptySuccess, success, ServiceBundle } from "nodecg-io-core";
import { Result, emptySuccess, success, ServiceBundle, Logger } from "nodecg-io-core";
import { CurseForge } from "./curseforgeClient";

export type CurseForgeClient = CurseForge;
Expand Down Expand Up @@ -44,14 +44,14 @@ class CurseforgeService extends ServiceBundle<never, CurseForgeClient> {
return emptySuccess();
}

async createClient(): Promise<Result<CurseForgeClient>> {
async createClient(_: never, logger: Logger): Promise<Result<CurseForgeClient>> {
const client = new CurseForge();
this.nodecg.log.info("Successfully created CurseForge client.");
logger.info("Successfully created CurseForge client.");
return success(client);
}

stopClient(_: CurseForgeClient): void {
this.nodecg.log.info("Successfully stopped CurseForge client.");
stopClient(_: CurseForgeClient, logger: Logger): void {
logger.info("Successfully stopped CurseForge client.");
}

requiresNoConfig = true;
Expand Down
Loading