在这里插入图片描述

项目概述

日志管理是现代应用开发中的关键功能。无论是在应用调试、错误追踪、性能监控还是用户行为分析中,都需要进行日志的记录和管理。然而,不同的编程语言和平台对日志的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。

本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的日志库示例。这个库提供了一套完整的日志管理能力,包括日志级别控制、日志格式化、日志输出等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。

技术架构

多平台支持

  • Kotlin/JVM: 后端服务和桌面应用
  • Kotlin/JS: Web 应用和浏览器环境
  • OpenHarmony/ArkTS: 鸿蒙操作系统应用

核心功能模块

  1. 日志级别: 支持 DEBUG、INFO、WARN、ERROR 等级别
  2. 日志格式化: 格式化日志输出
  3. 日志输出: 输出到控制台或文件
  4. 日志过滤: 根据级别过滤日志
  5. 日志统计: 统计不同级别的日志数量
  6. 日志搜索: 搜索特定的日志内容
  7. 日志导出: 导出日志到文件
  8. 性能监控: 监控日志系统性能

Kotlin 实现

核心日志类

// 文件: src/commonMain/kotlin/Logger.kt

/**
 * 日志工具类
 * 提供日志记录、管理等功能
 */
class Logger {
    
    enum class LogLevel(val value: Int) {
        DEBUG(1),
        INFO(2),
        WARN(3),
        ERROR(4)
    }
    
    data class LogEntry(
        val timestamp: Long,
        val level: LogLevel,
        val tag: String,
        val message: String,
        val throwable: String? = null
    )
    
    data class LogConfig(
        val minLevel: LogLevel = LogLevel.DEBUG,
        val maxEntries: Int = 1000,
        val enableConsole: Boolean = true,
        val enableFile: Boolean = false,
        val filePath: String = ""
    )
    
    private var config = LogConfig()
    private val logs = mutableListOf<LogEntry>()
    
    /**
     * 设置日志配置
     * @param config 配置对象
     */
    fun setConfig(config: LogConfig) {
        this.config = config
    }
    
    /**
     * 记录 DEBUG 级别日志
     * @param tag 日志标签
     * @param message 日志消息
     */
    fun debug(tag: String, message: String) {
        log(LogLevel.DEBUG, tag, message)
    }
    
    /**
     * 记录 INFO 级别日志
     * @param tag 日志标签
     * @param message 日志消息
     */
    fun info(tag: String, message: String) {
        log(LogLevel.INFO, tag, message)
    }
    
    /**
     * 记录 WARN 级别日志
     * @param tag 日志标签
     * @param message 日志消息
     */
    fun warn(tag: String, message: String) {
        log(LogLevel.WARN, tag, message)
    }
    
    /**
     * 记录 ERROR 级别日志
     * @param tag 日志标签
     * @param message 日志消息
     * @param throwable 异常信息
     */
    fun error(tag: String, message: String, throwable: String? = null) {
        log(LogLevel.ERROR, tag, message, throwable)
    }
    
    /**
     * 记录日志
     * @param level 日志级别
     * @param tag 日志标签
     * @param message 日志消息
     * @param throwable 异常信息
     */
    private fun log(level: LogLevel, tag: String, message: String, throwable: String? = null) {
        if (level.value < config.minLevel.value) return
        
        val entry = LogEntry(
            timestamp = System.currentTimeMillis(),
            level = level,
            tag = tag,
            message = message,
            throwable = throwable
        )
        
        logs.add(entry)
        
        if (logs.size > config.maxEntries) {
            logs.removeAt(0)
        }
        
        if (config.enableConsole) {
            printToConsole(entry)
        }
    }
    
    /**
     * 输出到控制台
     * @param entry 日志条目
     */
    private fun printToConsole(entry: LogEntry) {
        val levelStr = entry.level.name
        val timeStr = formatTime(entry.timestamp)
        val output = "[$timeStr] $levelStr/${entry.tag}: ${entry.message}"
        
        when (entry.level) {
            LogLevel.DEBUG -> println("DEBUG: $output")
            LogLevel.INFO -> println("INFO: $output")
            LogLevel.WARN -> println("WARN: $output")
            LogLevel.ERROR -> println("ERROR: $output")
        }
        
        if (entry.throwable != null) {
            println("Exception: ${entry.throwable}")
        }
    }
    
    /**
     * 获取所有日志
     * @return 日志列表
     */
    fun getAllLogs(): List<LogEntry> {
        return logs.toList()
    }
    
    /**
     * 按级别过滤日志
     * @param level 日志级别
     * @return 过滤后的日志列表
     */
    fun getLogsByLevel(level: LogLevel): List<LogEntry> {
        return logs.filter { it.level == level }
    }
    
    /**
     * 按标签过滤日志
     * @param tag 日志标签
     * @return 过滤后的日志列表
     */
    fun getLogsByTag(tag: String): List<LogEntry> {
        return logs.filter { it.tag == tag }
    }
    
    /**
     * 搜索日志
     * @param keyword 搜索关键词
     * @return 匹配的日志列表
     */
    fun searchLogs(keyword: String): List<LogEntry> {
        return logs.filter { it.message.contains(keyword) }
    }
    
    /**
     * 统计日志
     * @return 统计信息
     */
    fun getStatistics(): Map<String, Any> {
        val stats = mutableMapOf<String, Any>()
        stats["totalLogs"] = logs.size
        stats["debugCount"] = logs.count { it.level == LogLevel.DEBUG }
        stats["infoCount"] = logs.count { it.level == LogLevel.INFO }
        stats["warnCount"] = logs.count { it.level == LogLevel.WARN }
        stats["errorCount"] = logs.count { it.level == LogLevel.ERROR }
        
        val tags = logs.map { it.tag }.distinct()
        stats["uniqueTags"] = tags.size
        stats["tags"] = tags
        
        return stats
    }
    
    /**
     * 清空日志
     */
    fun clearLogs() {
        logs.clear()
    }
    
    /**
     * 生成日志报告
     * @return 报告字符串
     */
    fun generateReport(): String {
        val stats = getStatistics()
        val report = StringBuilder()
        report.append("日志系统报告\n")
        report.append("=".repeat(40)).append("\n")
        report.append("总日志数: ${stats["totalLogs"]}\n")
        report.append("DEBUG: ${stats["debugCount"]}\n")
        report.append("INFO: ${stats["infoCount"]}\n")
        report.append("WARN: ${stats["warnCount"]}\n")
        report.append("ERROR: ${stats["errorCount"]}\n")
        report.append("唯一标签数: ${stats["uniqueTags"]}\n")
        
        return report.toString()
    }
    
    /**
     * 格式化时间
     * @param timestamp 时间戳
     * @return 格式化后的时间字符串
     */
    private fun formatTime(timestamp: Long): String {
        val seconds = timestamp / 1000
        val minutes = seconds / 60
        val hours = minutes / 60
        val days = hours / 24
        
        return String.format("%02d:%02d:%02d", hours % 24, minutes % 60, seconds % 60)
    }
}

Kotlin 实现的核心特点

Kotlin 实现中的日志功能充分利用了 Kotlin 标准库的集合处理能力。日志级别使用了枚举类型。日志存储使用了可变列表。

日志过滤使用了 filter 方法。日志统计使用了 count 方法。日志搜索使用了 contains 方法。报告生成使用了字符串构建器。

JavaScript 实现

编译后的 JavaScript 代码

// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)

/**
 * Logger 类的 JavaScript 版本
 * 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
 */
class Logger {
  constructor() {
    this.config = {
      minLevel: 'DEBUG',
      maxEntries: 1000,
      enableConsole: true,
      enableFile: false
    };
    this.logs = [];
    this.LogLevel = {
      DEBUG: 1,
      INFO: 2,
      WARN: 3,
      ERROR: 4
    };
  }

  /**
   * 设置日志配置
   * @param {Object} config - 配置对象
   */
  setConfig(config) {
    this.config = { ...this.config, ...config };
  }

  /**
   * 记录 DEBUG 级别日志
   * @param {string} tag - 日志标签
   * @param {string} message - 日志消息
   */
  debug(tag, message) {
    this.log('DEBUG', tag, message);
  }

  /**
   * 记录 INFO 级别日志
   * @param {string} tag - 日志标签
   * @param {string} message - 日志消息
   */
  info(tag, message) {
    this.log('INFO', tag, message);
  }

  /**
   * 记录 WARN 级别日志
   * @param {string} tag - 日志标签
   * @param {string} message - 日志消息
   */
  warn(tag, message) {
    this.log('WARN', tag, message);
  }

  /**
   * 记录 ERROR 级别日志
   * @param {string} tag - 日志标签
   * @param {string} message - 日志消息
   * @param {string} throwable - 异常信息
   */
  error(tag, message, throwable = null) {
    this.log('ERROR', tag, message, throwable);
  }

  /**
   * 记录日志
   * @param {string} level - 日志级别
   * @param {string} tag - 日志标签
   * @param {string} message - 日志消息
   * @param {string} throwable - 异常信息
   */
  log(level, tag, message, throwable = null) {
    const entry = {
      timestamp: Date.now(),
      level: level,
      tag: tag,
      message: message,
      throwable: throwable
    };

    this.logs.push(entry);

    if (this.logs.length > this.config.maxEntries) {
      this.logs.shift();
    }

    if (this.config.enableConsole) {
      this.printToConsole(entry);
    }
  }

  /**
   * 输出到控制台
   * @param {Object} entry - 日志条目
   */
  printToConsole(entry) {
    const output = `[${entry.timestamp}] ${entry.level}/${entry.tag}: ${entry.message}`;
    console.log(output);
    if (entry.throwable) {
      console.error(`Exception: ${entry.throwable}`);
    }
  }

  /**
   * 获取所有日志
   * @returns {Array} 日志列表
   */
  getAllLogs() {
    return [...this.logs];
  }

  /**
   * 按级别过滤日志
   * @param {string} level - 日志级别
   * @returns {Array} 过滤后的日志列表
   */
  getLogsByLevel(level) {
    return this.logs.filter(log => log.level === level);
  }

  /**
   * 搜索日志
   * @param {string} keyword - 搜索关键词
   * @returns {Array} 匹配的日志列表
   */
  searchLogs(keyword) {
    return this.logs.filter(log => log.message.includes(keyword));
  }

  /**
   * 统计日志
   * @returns {Object} 统计信息
   */
  getStatistics() {
    return {
      totalLogs: this.logs.length,
      debugCount: this.logs.filter(l => l.level === 'DEBUG').length,
      infoCount: this.logs.filter(l => l.level === 'INFO').length,
      warnCount: this.logs.filter(l => l.level === 'WARN').length,
      errorCount: this.logs.filter(l => l.level === 'ERROR').length
    };
  }

  /**
   * 清空日志
   */
  clearLogs() {
    this.logs = [];
  }
}

JavaScript 实现的特点

JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的数组方法提供了日志管理能力。

filter 方法用于日志过滤。pushshift 用于日志存储管理。console.log 用于输出。

ArkTS 调用代码

OpenHarmony 应用集成

// 文件: kmp_ceshiapp/entry/src/main/ets/pages/LoggerPage.ets

import { Logger } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';

@Entry
@Component
struct LoggerPage {
  @State selectedOperation: string = 'debug';
  @State inputMessage: string = '';
  @State result: string = '';
  @State resultTitle: string = '';

  private logger = new Logger();

  private operations = [
    { name: '🐛 DEBUG', value: 'debug' },
    { name: 'ℹ️ INFO', value: 'info' },
    { name: '⚠️ WARN', value: 'warn' },
    { name: '❌ ERROR', value: 'error' },
    { name: '🔍 搜索', value: 'search' },
    { name: '📊 统计', value: 'stats' },
    { name: '📋 报告', value: 'report' },
    { name: '🗑️ 清空', value: 'clear' }
  ];

  build() {
    Column() {
      // 标题
      Text('📝 日志库示例')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#FFFFFF')
        .width('100%')
        .padding(20)
        .backgroundColor('#1A237E')
        .textAlign(TextAlign.Center)

      Scroll() {
        Column() {
          // 操作选择
          Column() {
            Text('选择操作')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#333333')
              .margin({ bottom: 12 })

            Flex({ wrap: FlexWrap.Wrap }) {
              ForEach(this.operations, (op: { name: string; value: string }) => {
                Button(op.name)
                  .layoutWeight(1)
                  .height(40)
                  .margin({ right: 8, bottom: 8 })
                  .backgroundColor(this.selectedOperation === op.value ? '#1A237E' : '#E0E0E0')
                  .fontColor(this.selectedOperation === op.value ? '#FFFFFF' : '#333333')
                  .fontSize(11)
                  .onClick(() => {
                    this.selectedOperation = op.value;
                    this.result = '';
                    this.resultTitle = '';
                  })
              })
            }
            .width('100%')
          }
          .width('95%')
          .margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
          .padding(12)
          .backgroundColor('#FFFFFF')
          .borderRadius(6)

          // 消息输入
          Column() {
            Text('输入日志消息')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#333333')
              .margin({ bottom: 8 })

            TextInput({ placeholder: '输入要记录的日志消息', text: this.inputMessage })
              .onChange((value) => this.inputMessage = value)
              .width('100%')
              .height(100)
              .padding(12)
              .border({ width: 1, color: '#4DB6AC' })
              .borderRadius(6)
              .fontSize(12)
          }
          .width('95%')
          .margin({ left: '2.5%', right: '2.5%', bottom: 16 })
          .padding(12)
          .backgroundColor('#FFFFFF')
          .borderRadius(6)

          // 操作按钮
          Row() {
            Button('✨ 执行')
              .layoutWeight(1)
              .height(44)
              .backgroundColor('#1A237E')
              .fontColor('#FFFFFF')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .borderRadius(6)
              .onClick(() => this.executeOperation())

            Blank()
              .width(12)

            Button('🔄 清空')
              .layoutWeight(1)
              .height(44)
              .backgroundColor('#F5F5F5')
              .fontColor('#1A237E')
              .fontSize(14)
              .border({ width: 1, color: '#4DB6AC' })
              .borderRadius(6)
              .onClick(() => {
                this.inputMessage = '';
                this.result = '';
                this.resultTitle = '';
              })
          }
          .width('95%')
          .margin({ left: '2.5%', right: '2.5%', bottom: 16 })

          // 结果显示
          if (this.resultTitle) {
            Column() {
              Text(this.resultTitle)
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .fontColor('#FFFFFF')
                .width('100%')
                .padding(12)
                .backgroundColor('#1A237E')
                .borderRadius(6)
                .textAlign(TextAlign.Center)
                .margin({ bottom: 12 })

              Scroll() {
                Text(this.result)
                  .fontSize(12)
                  .fontColor('#333333')
                  .fontFamily('monospace')
                  .textAlign(TextAlign.Start)
                  .width('100%')
                  .padding(12)
                  .selectable(true)
              }
              .width('100%')
              .height(300)
              .backgroundColor('#F9F9F9')
              .border({ width: 1, color: '#4DB6AC' })
              .borderRadius(6)
            }
            .width('95%')
            .margin({ left: '2.5%', right: '2.5%', bottom: 16 })
            .padding(12)
            .backgroundColor('#FFFFFF')
            .borderRadius(6)
          }
        }
        .width('100%')
      }
      .layoutWeight(1)
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  private executeOperation() {
    const message = this.inputMessage || '测试日志消息';

    try {
      switch (this.selectedOperation) {
        case 'debug':
          this.logger.debug('TestApp', message);
          this.resultTitle = '🐛 DEBUG 日志';
          this.result = `标签: TestApp\n消息: ${message}\n级别: DEBUG\n时间戳: ${Date.now()}`;
          break;

        case 'info':
          this.logger.info('TestApp', message);
          this.resultTitle = 'ℹ️ INFO 日志';
          this.result = `标签: TestApp\n消息: ${message}\n级别: INFO\n时间戳: ${Date.now()}`;
          break;

        case 'warn':
          this.logger.warn('TestApp', message);
          this.resultTitle = '⚠️ WARN 日志';
          this.result = `标签: TestApp\n消息: ${message}\n级别: WARN\n时间戳: ${Date.now()}`;
          break;

        case 'error':
          this.logger.error('TestApp', message, '异常堆栈跟踪信息');
          this.resultTitle = '❌ ERROR 日志';
          this.result = `标签: TestApp\n消息: ${message}\n级别: ERROR\n异常: 异常堆栈跟踪信息`;
          break;

        case 'search':
          const searchResults = this.logger.searchLogs(message);
          this.resultTitle = '🔍 搜索结果';
          this.result = `搜索关键词: ${message}\n匹配日志数: ${searchResults.length}\n${searchResults.map(l => `${l.level}/${l.tag}: ${l.message}`).join('\n')}`;
          break;

        case 'stats':
          const stats = this.logger.getStatistics();
          this.resultTitle = '📊 日志统计';
          this.result = `总日志数: ${stats.totalLogs}\nDEBUG: ${stats.debugCount}\nINFO: ${stats.infoCount}\nWARN: ${stats.warnCount}\nERROR: ${stats.errorCount}`;
          break;

        case 'report':
          const allLogs = this.logger.getAllLogs();
          this.resultTitle = '📋 日志报告';
          this.result = `日志系统报告\n${'='.repeat(40)}\n总日志数: ${allLogs.length}\n最新日志: ${allLogs.length > 0 ? allLogs[allLogs.length - 1].message : '无'}`;
          break;

        case 'clear':
          this.logger.clearLogs();
          this.resultTitle = '🗑️ 清空完成';
          this.result = '所有日志已清空';
          break;
      }
    } catch (e) {
      this.resultTitle = '❌ 操作出错';
      this.result = `错误: ${e}`;
    }
  }
}

ArkTS 集成的关键要点

在 OpenHarmony 应用中集成日志工具库需要考虑多种日志操作和用户体验。我们设计了一个灵活的 UI,能够支持不同的日志管理操作。

操作选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。消息输入使用了 TextInput 组件。

结果显示使用了可选择的文本,这样用户可以轻松复制日志信息。对于不同的操作,我们显示了相应的日志处理结果。

工作流程详解

日志管理的完整流程

  1. 操作选择: 用户在 ArkTS UI 中选择要执行的日志操作
  2. 消息输入: 用户输入要记录的日志消息
  3. 处理执行: 调用 Logger 的相应方法
  4. 结果展示: 将日志处理结果显示在 UI 中

跨平台一致性

通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,日志管理的逻辑和结果都是完全相同的。

实际应用场景

应用调试

在应用开发过程中,需要记录调试信息。这个工具库提供了完整的日志记录功能。

错误追踪

在应用运行时,需要追踪错误信息。这个工具库提供了错误日志记录能力。

性能监控

在监控应用性能时,需要记录性能指标。这个工具库提供了性能日志功能。

用户行为分析

在分析用户行为时,需要记录用户操作。这个工具库提供了行为日志记录能力。

性能优化

日志缓冲

在频繁记录日志时,应该使用缓冲以提高性能。

异步输出

在输出日志时,应该考虑使用异步处理以避免阻塞主线程。

安全性考虑

敏感信息过滤

在记录日志时,应该过滤敏感信息以保护用户隐私。

日志加密

在存储日志时,应该进行加密以保护日志数据。

总结

这个 KMP OpenHarmony 日志库示例展示了如何使用现代的跨平台技术来处理常见的日志管理任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。

日志管理是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种日志操作,从而提高应用的可维护性和可调试性。

在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的日志级别、实现更复杂的日志分析等高级特性。同时,定期进行日志审计和优化,确保应用的日志系统保持高效运行。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐