自学内容网 自学内容网

从零开始开发纯血鸿蒙应用之日志模块实现

二、实现日志模块

不论是app,还是service,为了方便观察程序的运行状态,日志打印是少不了的,然而,日志打印不能单纯打印在控制台里,还应该打印到文件中,越是正式的工程项目,就越会用文件去记录应用日志,从而方便后续导出进行分析。

1、初始化日志模块

在上一篇中,已经介绍了工程目录,其中就提到了一个用 static Library 创建的 lib_log 模块,现在,就开始对该模块进行初始化:
在这里插入图片描述
需要关注的是 src\main\ets 目录和 index.ets 文件。index.ets 文件的内容比较简单,只有一行:export { LoggerFactory as Logger } from "./src/main/ets/LogFactory",这对于有过 Javascript 或 Typescript 经验的前端开发来说,应该是相当熟悉的导包语句了。

2、功能原型

日志模块的功能,坦白说,并不是我自己全部设计出来的,而是基于华为开发者官网提供的鸿蒙Demo中的日志打印Demo进行改造的:
在这里插入图片描述
对应的 Demo 源码在华为鸿蒙日志打印Demo

3、改造与实现

日志模块的src/main/ets目录下,一共有6份代码文件,分别为:

  • LogLevel.ts
  • LogModel.ts
  • LogConfigure.ts
  • LogConfigure.ets
  • Logger.ets
  • LogFactory.ets

现在,从代码最简单的 LogLevel 开始

3.1、LogLevel

先看一下源码:

/*
 * Copyright (c) 2022 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.
 */
/*
 * Copyright (c) 2024/12/1 彭友聪
 * TxtEdit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2024/12/1 09:22
 * file: Level.ts
 * product: DevEco Studio
 * */

export enum LogLevel {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3,
  FATAL = 4,
}

该文件主要声明定义日志级别,因此,只有一个 enum,也比较好理解,所以,我也不过多赘述。

3.2、LogModel

详细代码如下:

/*
 * Copyright (c) 2022 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.
 */
/*
 * Copyright (c) 2024/12/1 彭友聪
 * TxtEdit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2024/12/1 09:28
 * file: Model.ts
 * product: DevEco Studio
 * */

import { hilog } from '@kit.PerformanceAnalysisKit'
import { LogLevel } from "./LogLevel"

export class LogModel {
  private domain: number;
  private prefix: string;
  private format: string = `%{public}s %{public}s`;

  constructor(prefix: string) {
    this.prefix = prefix;
    this.domain = 0xFF00;
  }

  debug(...args: any[]) {
    hilog.debug(this.domain, this.prefix, this.format, args);
  }

  info(...args: any[]) {
    hilog.info(this.domain, this.prefix, this.format, args);
  }

  warn(...args: any[]) {
    hilog.warn(this.domain, this.prefix, this.format, args);
  }

  error(...args: any[]) {
    hilog.error(this.domain, this.prefix, this.format, args);
  }

  fatal(...args: any[]) {
    hilog.fatal(this.domain, this.prefix, this.format, args);
  }

  isLoggable(prefix: string, level: LogLevel) {
    return hilog.isLoggable(this.domain, prefix, level);
  }
}

该文件的结构如下:
在这里插入图片描述
相对于 LogLevel 来说,复杂了很多,但仔细阅读,会发现并没有那么复杂。

首先,是三个私有的字段 domain、prefix 和 format,domain 是一个日志域,习惯上会赋值为0xFF00,prefix 就是日志记录的前缀内容,format就是日志记录的打印格式。

接着是一组对应每种日志级别的日志打印方法,实现代码大同小异,都是以相同的传参顺序调用 hilog API。

最后是一个用于判断能不能进行日志打印的 isLoggable 方法。

3.3、LogConfigure

这部分,分别有一个ts文件和一个ets文件组成,首先看一下 ts 文件:

/*
 * Copyright (c) 2022 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.
 */

/*
 * Copyright (c) 2024/12/1 彭友聪
 * TxtEdit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2024/12/1 09:24
 * file: Configure.ts
 * product: DevEco Studio
 * */

import { LogLevel } from './LogLevel'

export type LogConfigure = {
  cheese: {
    types: string[],
    filename?: string
  }
  defaults: {
    appender: string,
    level: LogLevel
  }
}

声明了两种类型,cheese 和 defaults,从代码上可以轻松看出,cheese 类型是在需要将日志打印到文件中进行使用的。

再来看一下对应的 ets 文件:

/*
 * Copyright (c) 2022 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.
 */
/*
 * Copyright (c) 2024/12/1 彭友聪
 * TxtEdit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2024/12/1 09:31
 * file: LogConfigure.ets
 * product: DevEco Studio
 * */

import { LogLevel } from './LogLevel'

class cheeseStruct {
  types: Array<String> = [];
  filename?: string = "";
}

class defaultsStruct {
  appender: string = "";
  level: LogLevel = LogLevel.DEBUG;
}

export class LogConfigure {
  public cheese: cheeseStruct = { types: [], filename: "" };
  public defaults: defaultsStruct = { appender: "", level: LogLevel.DEBUG };

  constructor(types: Array<string>, filename: string, appender: string, level: LogLevel) {
    this.cheese.types = types;
    this.cheese.filename = filename;
    this.defaults.appender = appender;
    this.defaults.level = level;
  }

  updateFilename(filename: string) {
    this.cheese.filename = filename;
  }

  updateLevel(level: LogLevel) {
    this.defaults.level = level;
  }

}

基本上,就是在对 ts 文件中的代码进行扩展和细化,向外提供日志配置器,初始化日志配置器,必须传入日志打印类型(hilog、file)、日志文件名、附加内容和日志级别,同时也向外提供了动态更新日志文件名和和日志级别的方法。

3.4、Logger

这个文件是实现日志打印功能的主要文件,从结构上就可以看出内容丰富:
在这里插入图片描述
文件包含了一个日志打印器和四个日志策略类。

3.4.1、ILoggerStrategy
interface ILoggerStrategy {
  updateConfigure(configure: LogConfigure): void

  debug(message: string): void

  info(message: string): void

  warn(message: string): void

  error(message: string): void

  fatal(message: string): void
}

如上所示,这是一个接口类,也即抽象类,是后续的三个日志策略类的父类。

3.4.2、ConsoleLoggerStrategy
class ConsoleLoggerStrategy implements ILoggerStrategy {
  private configure: LogConfigure;
  private domain: number;

  constructor(configure: LogConfigure) {
    this.configure = configure;
    this.domain = 0xFF00;
  }

  updateConfigure(configure: LogConfigure) {
    this.configure = configure;
  }

  public debug(message: string): void {
    hilog.debug(this.domain, this.configure.defaults.appender, message);
  }

  public info(message: string): void {
    hilog.info(this.domain, this.configure.defaults.appender, message);
  }

  public warn(message: string): void {
    hilog.warn(this.domain, this.configure.defaults.appender, message);
  }

  public error(message: string): void {
    hilog.error(this.domain, this.configure.defaults.appender, message);
  }

  public fatal(message: string): void {
    hilog.fatal(this.domain, this.configure.defaults.appender, message);
  }
}

顾名思义,是控制台日志策略

3.4.3、HilogLoggerStrategy
class HilogLoggerStrategy implements ILoggerStrategy {
  private configure: LogConfigure;
  private loggerModel: LogModel;

  constructor(configure: LogConfigure) {
    this.configure = configure;
    this.loggerModel = new LogModel(`${configure.defaults.appender}`);
  }

  updateConfigure(configure: LogConfigure) {
    this.configure = configure;
  }

  public debug(message: string): void {
    this.loggerModel.debug(`[DEBUG] ${this.configure.defaults.appender} - `, `${message}`);
  }

  public info(message: string): void {
    this.loggerModel.info(`[INFO] ${this.configure.defaults.appender} - `, `${message}`);
  }

  public warn(message: string): void {
    this.loggerModel.warn(`[WARN] ${this.configure.defaults.appender} - `, `${message}`);
  }

  public error(message: string): void {
    this.loggerModel.error(`[ERROR] ${this.configure.defaults.appender} - `, `${message}`);
  }

  public fatal(message: string): void {
    this.loggerModel.fatal(`[FATAL] ${this.configure.defaults.appender} - `, `${message}`);
  }
}

HilogLoggerStrategy 与 ConsoleLoggerStrategy 的区别在于是否直接调用 hilog 打印日志

3.4.4、FileLoggerStrategy
class FileLoggerStrategy implements ILoggerStrategy {
  private fd: number = 0;
  private configure: LogConfigure;
  private fileStream?: fileIo.File;

  constructor(configure: LogConfigure, context: common.UIAbilityContext) {
    // Initialization file.
    this.configure = configure;
    let path = context.filesDir;
    let result = `${path}/${this.configure.cheese.filename}`;
    try {
      this.fileStream = fileIo.openSync(result, fileIo.OpenMode.APPEND| fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
      this.fd = this.fileStream.fd;
    } catch (err) {
      return;
    }
  }

  updateConfigure(configure: LogConfigure) {
    this.configure = configure;
  }

  private getTodayStr(): string {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0');
    const day = String(today.getDate()).padStart(2, '0');
    const hour = String(today.getHours()).padStart(2, '0');
    const minute = String(today.getMinutes()).padStart(2, '0');
    const second = String(today.getSeconds()).padStart(2, '0');
    const dateString = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
    return dateString;

  }

  private async writeFile(message: string) {
    // Write a document.
    const dateStr = this.getTodayStr();
    const package_name = 'com.pyc.TxtEdit'
    const logData = `${dateStr} ${package_name} - ${message} `
    fileIo.writeSync(this.fd, `${logData}\n`);
    // fs.closeSync(this.fileStream)
    // this.fileStream.closeSync()
  }

  public async debug(message: string): Promise<void> {
    await this.writeFile(`[DEBUG] ${this.configure.defaults.appender}, ${message}`);
  }

  public async info(message: string): Promise<void> {
    await this.writeFile(`[INFO] ${this.configure.defaults.appender}, ${message}`);
  }

  public async warn(message: string): Promise<void> {
    await this.writeFile(`[WARN] ${this.configure.defaults.appender}, ${message}`);
  }

  public async error(message: string): Promise<void> {
    await this.writeFile(`[ERROR] ${this.configure.defaults.appender}, ${message}`);
  }

  public async fatal(message: string): Promise<void> {
    await this.writeFile(`[FATAL] ${this.configure.defaults.appender}, ${message}`);
  }
}

由于文件日志策略涉及到文件IO,所以,它的方法实现形式与前面两种大为不同,采用异步方法进行日志打印,并且多了一个独有的 writeFile 方法。

3.4.5、Logger

日志器的实现代码如下:

export class Logger {
  private configure = new LogConfigure([''], '', '', LogLevel.DEBUG);
  private loggerModel: LogModel = new LogModel(`${this.configure.defaults.appender}`);
  private strategies: Map<string, ILoggerStrategy> = new Map();
  private context: common.UIAbilityContext;

  constructor(context: common.UIAbilityContext) {
    this.context = context;
  }

  public setConfigure(configure: LogConfigure) {
    this.strategies = new Map();
    this.configure = configure;
    this.loggerModel = new LogModel(`${configure.defaults.appender}`);
    if (!configure || !configure.cheese || !configure.cheese.types) {
      return;
    }
    if (configure.cheese.types.includes('file')) {
      this.strategies.set('file', new FileLoggerStrategy(this.configure, this.context));
    }
    if (configure.cheese.types.includes('hilog')) {
      this.strategies.set('hilog', new HilogLoggerStrategy(this.configure));
    }

    if (configure.cheese.types.includes('console') || this.strategies.size <= 0) {
      this.strategies.set('console', new ConsoleLoggerStrategy(this.configure));
    }
  }

  public updateConfigure(filename: string, level: LogLevel) {
    this.configure.updateFilename(filename);
    this.configure.updateLevel(level);
    this.strategies.forEach((_value, key) => {
      this.strategies?.get(key)?.updateConfigure(this.configure);
    })
  }

  public debug(message: string): void {
    let levelCheck = this.loggerModel.isLoggable(this.configure.defaults.appender, LogLevel.DEBUG);
    if (levelCheck && this.configure.defaults.level <= LogLevel.DEBUG) {
      this.strategies.forEach((_value, key) => {
        this.strategies?.get(key)?.debug(message);
      })
    }
  }

  public info(message: string): void {
    let levelCheck = this.loggerModel.isLoggable(message, LogLevel.INFO);
    if (levelCheck && this.configure.defaults.level <= LogLevel.INFO) {
      this.strategies.forEach((_value, key) => {
        this.strategies?.get(key)?.info(message);
      })
    }
  }

  public warn(message: string): void {
    let levelCheck = this.loggerModel.isLoggable(message, LogLevel.WARN);
    if (levelCheck && this.configure.defaults.level <= LogLevel.WARN) {
      this.strategies.forEach((_value, key) => {
        this.strategies.get(key)?.warn(message);
      })
    }
  }

  public error(message: string): void {
    let levelCheck = this.loggerModel.isLoggable(message, LogLevel.ERROR);
    if (levelCheck && this.configure.defaults.level <= LogLevel.ERROR) {
      this.strategies.forEach((_value, key) => {
        this.strategies.get(key)?.error(message);
      })
    }
  }
public fatal(message: string): void {
    let levelCheck = this.loggerModel.isLoggable(message, LogLevel.FATAL);
    if (levelCheck && this.configure.defaults.level <= LogLevel.FATAL) {
      this.strategies.forEach((_value, key) => {
        this.strategies.get(key)?.fatal(message);
      })
    }
  }
}

日志器允许同时使用多个日志策略,每个具体的日志打印方法,都会循环调用每个策略去打印日志。

3.5、LoggerFactory

考虑到 Logger 直接对外暴露,用起来不是很方便,所以利用工厂类的设计模式,用个 LoggerFactory 再次封装一下,再对外提供日志 API:

/*
 * Copyright (c) 2024/12/1 彭友聪
 * TxtEdit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2024/12/1 09:43
 * file: LogFactory.ets
 * product: DevEco Studio
 * */
import { common } from "@kit.AbilityKit";
import { DirectoryConstants } from "lib_constant";
import { LogConfigure } from "./LogConfigure";
import { Logger } from "./Logger";
import { LogLevel } from "./LogLevel";
import { fileIo } from "@kit.CoreFileKit";

export class LoggerFactory {
  private constructor() {
  }

  private static context: common.UIAbilityContext|null = null;
  private static logger: Logger|null = null;

  static init(context: common.UIAbilityContext){
    LoggerFactory.context = context;
    LoggerFactory.logger = new Logger(LoggerFactory.context);
    const today: string = LoggerFactory.getTodayStr();
    const fileDir = LoggerFactory.context.filesDir
    const prefix = `${DirectoryConstants.LOG_PATH}`;
    if (!fileIo.accessSync(`${fileDir}/${prefix}`)) {
      fileIo.mkdirSync(`${fileDir}/${prefix}`);
    }
    const currentLogFile: string = `${prefix}/${today}.log`;
    const configure = new LogConfigure(['file', 'hilog'], currentLogFile, '', LogLevel.INFO);
    LoggerFactory.logger.setConfigure(configure);
  }

  private static getTodayStr(): string {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0');
    const day = String(today.getDate()).padStart(2, '0');
    const dateString = `${year}_${month}_${day}`;
    return dateString;

  }

  static info(message: string, ...args: string[]){
    if (LoggerFactory.context ) {
      const today: string = LoggerFactory.getTodayStr();
      const prefix = `${DirectoryConstants.LOG_PATH}`;
      const currentLogFile: string = `${prefix}/${today}.log`;
      if (LoggerFactory.logger) {
        LoggerFactory.logger.updateConfigure(currentLogFile, LogLevel.INFO);
        const logRecord: string = `${args[0]} ${message}`;
        LoggerFactory.logger.info(logRecord)
      } else {
        throw new Error("Must init logger at First");
      }
    } else {
      throw new Error("Must init context at First");
    }
  }

  static warn(message: string, ...args: string[]){
    if (LoggerFactory.context ) {
      const today: string = LoggerFactory.getTodayStr();
      const prefix = `${DirectoryConstants.LOG_PATH}`;
      const currentLogFile: string = `${prefix}/${today}.log`;
      if (LoggerFactory.logger) {
        LoggerFactory.logger.updateConfigure(currentLogFile, LogLevel.WARN)
        const logRecord: string = `${args[0]} ${message}`;
        LoggerFactory.logger.info(logRecord)
      } else {
        throw new Error("Must init logger at First");
      }
    } else {
      throw new Error("Must init context at First");
    }
  }

  static debug(message: string, ...args: string[]){
    if (LoggerFactory.context ) {
      const today: string = LoggerFactory.getTodayStr();
      const prefix = `${DirectoryConstants.LOG_PATH}`;
      const currentLogFile: string = `${prefix}/${today}.log`;
      if (LoggerFactory.logger) {
        LoggerFactory.logger.updateConfigure(currentLogFile, LogLevel.DEBUG)
        const logRecord: string = `${args[0]} ${message}`;
        LoggerFactory.logger.info(logRecord)
      } else {
        throw new Error("Must init logger at First");
      }
    } else {
      throw new Error("Must init context at First");
    }
  }

  static error(message: string, ...args: string[]){
    if (LoggerFactory.context ) {
      const today: string = LoggerFactory.getTodayStr();
      const prefix = `${DirectoryConstants.LOG_PATH}`;
      const currentLogFile: string = `${prefix}/${today}.log`;
      if (LoggerFactory.logger) {
        LoggerFactory.logger.updateConfigure(currentLogFile, LogLevel.ERROR)
        const logRecord: string = `${args[0]} ${message}`;
        LoggerFactory.logger.info(logRecord)
      } else {
        throw new Error("Must init logger at First");
      }
    } else {
      throw new Error("Must init context at First");
    }
  }

  static fatal(message: string, ...args: string[]){
    if (LoggerFactory.context ) {
      const today: string = LoggerFactory.getTodayStr();
      const prefix = `${DirectoryConstants.LOG_PATH}`;
      const currentLogFile: string = `${prefix}/${today}.log`;
      if (LoggerFactory.logger) {
        LoggerFactory.logger.updateConfigure(currentLogFile, LogLevel.FATAL)
        const logRecord: string = `${args[0]} ${message}`;
        LoggerFactory.logger.info(logRecord)
      } else {
        throw new Error("Must init logger at First");
      }
    } else {
      throw new Error("Must init context at First");
    }
  }

}

4、日志器使用

哪个模块需要进行日志打印,就在对应模块的 oh-package.josn5 文件的 dependencies 标签中增加依赖:

{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "lib_constant": "file:../lib/lib_constant",
    "lib_resources": "file:../lib/lib_resources",
    "lib_util": "file:../lib/lib_util",
    "lib_log": "file:../lib/lib_log",
    'lib_comps': 'file:../lib/lib_comps'
  }
}

但是,需要强调的是,由于日志器里面需要 UI 上下文,所以,必须在 EntryAbility 类的 onCreate 方法的最开始处,调用 LoggerFactory 的 init 方法:
在这里插入图片描述
之后,就可以在需要进行日志打印的地方,调用合适级别的日志方法进行打印即可,打印效果如下:
在这里插入图片描述
上面是打印在文件中的日志,下面时打印在日志窗口中的日志记录


原文地址:https://blog.csdn.net/qq_42896653/article/details/144782468

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!