* Copyright (c) 2025 - 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ErrorCode } from './util/error'
import { WorkerMessageType, LogLevel, BuildConfig } from './types'
export enum SubsystemCode {
ARK_GUARD = '113',
BUILDSYSTEM = '114',
ES2PANDA = '115',
SDK = '117'
}
export interface ILogger {
printInfo(message: string): void;
printWarn(message: string): void;
printDebug(message: string): void;
printError(error: LogData): void;
printErrorAndExit(error: LogData): void;
}
export type LoggerGetter = (code: SubsystemCode) => ILogger;
export class Logger implements ILogger {
private static instance?: Logger;
private enableDebugOutput: boolean = false;
private loggerMap: { [key in SubsystemCode]?: ILogger };
private hasErrorOccurred: boolean = false;
private constructor(loggerGetter: LoggerGetter, enableDebugOutput?: boolean) {
this.enableDebugOutput = enableDebugOutput ?? false;
this.loggerMap = {};
this.loggerMap[SubsystemCode.ARK_GUARD] = loggerGetter(SubsystemCode.ARK_GUARD);
this.loggerMap[SubsystemCode.BUILDSYSTEM] = loggerGetter(SubsystemCode.BUILDSYSTEM);
this.loggerMap[SubsystemCode.ES2PANDA] = loggerGetter(SubsystemCode.ES2PANDA);
this.loggerMap[SubsystemCode.SDK] = loggerGetter(SubsystemCode.SDK);
}
public static getInstance(loggerGetter?: LoggerGetter, enableDebugOutput?: boolean): Logger {
if (!Logger.instance) {
if (!loggerGetter) {
throw new Error('loggerGetter is required for the first Logger instantiation.');
}
Logger.instance = new Logger(loggerGetter, enableDebugOutput);
}
return Logger.instance;
}
public static destroyInstance(): void {
Logger.instance = undefined;
}
public printInfo(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void {
const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode);
logger.printInfo(`[ID:${process.pid}] [time:${Date.now()}] ${message}`);
}
public printWarn(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void {
const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode);
logger.printWarn(`[ID:${process.pid}] [time:${Date.now()}] ${message}`);
}
public printDebug(message: string, subsystemCode: SubsystemCode = SubsystemCode.BUILDSYSTEM): void {
if (!this.enableDebugOutput) {
return;
}
const logger: ILogger = this.getLoggerFromSubsystemCode(subsystemCode);
logger.printDebug(`[ID:${process.pid}] [time:${Date.now()}] ${message}`);
}
public printError(error: LogData): void {
this.hasErrorOccurred = true;
const logger: ILogger = this.getLoggerFromErrorCode(error.code);
logger.printError(error);
}
public printErrorAndExit(error: LogData): void {
this.hasErrorOccurred = true;
const logger: ILogger = this.getLoggerFromErrorCode(error.code);
logger.printErrorAndExit(error);
}
protected isValidErrorCode(errorCode: ErrorCode): boolean {
return /^\d{8}$/.test(errorCode);
}
protected getLoggerFromErrorCode(errorCode: ErrorCode): ILogger {
if (!this.isValidErrorCode(errorCode)) {
throw new Error('Invalid errorCode.');
}
const subsystemCode = errorCode.slice(0, 3) as SubsystemCode;
const logger = this.getLoggerFromSubsystemCode(subsystemCode);
return logger;
}
protected getLoggerFromSubsystemCode(subsystemCode: SubsystemCode): ILogger {
if (!this.loggerMap[subsystemCode]) {
throw new Error('Invalid subsystemCode.');
}
return this.loggerMap[subsystemCode];
}
public hasErrors(): boolean {
return this.hasErrorOccurred;
}
public resetErrorFlag(): void {
this.hasErrorOccurred = false;
}
}
export class LogDataFactory {
static newInstance(
code: ErrorCode,
description: string,
cause: string = '',
position: string = '',
solutions: string[] = [],
moreInfo?: Object
): LogData {
const data: LogData = new LogData(code, description, cause, position, solutions, moreInfo);
return data;
}
}
export class LogData {
code: ErrorCode;
description: string;
cause: string;
position: string;
solutions: string[];
moreInfo?: Object;
constructor(
code: ErrorCode,
description: string,
cause: string,
position: string,
solutions: string[],
moreInfo?: Object
) {
this.code = code;
this.description = description;
this.cause = cause;
this.position = position;
this.solutions = solutions;
this.moreInfo = moreInfo;
}
toString(): string {
let errorString = `ERROR Code: ${this.code} ${this.description}\n`;
if (this.cause || this.position) {
errorString += `Error Message: ${this.cause}\n`;
if (this.position) {
errorString += `Position: ${this.position}\n`;
}
errorString += '\n\n';
}
if (this.solutions.length > 0 && this.solutions[0] !== '') {
errorString += `* Try the following: \n${this.solutions.map(str => ` > ${str}`).join('\n')}\n`;
}
if (this.moreInfo) {
errorString += '\nMore Info:\n';
for (const [key, value] of Object.entries(this.moreInfo)) {
errorString += ` - ${key.toUpperCase()}: ${value}\n`;
}
}
return errorString;
}
}
class ConsoleLogger implements ILogger {
private static instances: { [key: string]: ConsoleLogger } = {};
private constructor() { }
public printInfo(message: string): void {
console.info('[INFO]', message);
}
public printWarn(message: string): void {
console.warn('[WARN]', message);
}
public printDebug(message: string): void {
console.debug('[DEBUG]', message);
}
public printError(error: LogData): void {
console.error('[ERROR]', error.toString());
}
public printErrorAndExit(error: LogData): void {
this.printError(error);
process.exit(1);
}
public static createLogger(subsystemCode: string): ConsoleLogger {
if (!ConsoleLogger.instances[subsystemCode]) {
ConsoleLogger.instances[subsystemCode] = new ConsoleLogger();
}
return ConsoleLogger.instances[subsystemCode];
}
}
export function getConsoleLogger(subsystemCode: string): ConsoleLogger {
return ConsoleLogger.createLogger(subsystemCode);
}
export class InterProcessLogger implements ILogger {
private static instances: { [key: string]: InterProcessLogger } = {};
private constructor() { }
public printInfo(message: string): void {
process.send!({ type: WorkerMessageType.LOG, data: { level: LogLevel.INFO, message } });
}
public printWarn(message: string): void {
process.send!({ type: WorkerMessageType.LOG, data: { level: LogLevel.WARN, message } });
}
public printDebug(message: string): void {
process.send!({ type: WorkerMessageType.LOG, data: { level: LogLevel.DEBUG, message } });
}
public printError(error: LogData): void {
process.send!({ type: WorkerMessageType.LOG, data: { level: LogLevel.ERROR, error } });
}
public printErrorAndExit(error: LogData): void {
process.send!({ type: WorkerMessageType.LOG, data: { level: LogLevel.ERROR_AND_EXIT, error } });
process.exit(1);
}
public static createLogger(subsystemCode: string): InterProcessLogger {
if (!InterProcessLogger.instances[subsystemCode]) {
InterProcessLogger.instances[subsystemCode] = new InterProcessLogger();
}
return InterProcessLogger.instances[subsystemCode];
}
}
export function getInterProcessLogger(subsystemCode: string): InterProcessLogger {
return InterProcessLogger.createLogger(subsystemCode);
}
export function patchBuildConfigLogger(buildConfig: BuildConfig, loggerGetter: LoggerGetter): void {
buildConfig.getHvigorConsoleLogger = loggerGetter;
}