用Kotlin实现KMP鸿蒙日志生成工具
本文档介绍了一个基于Kotlin Multiplatform的跨端日志工具实现方案。该工具具备多级别日志管理(DEBUG/INFO/WARN/ERROR/FATAL)、灵活的格式化输出、日志过滤搜索和性能统计等功能。核心实现包括:1)通过枚举类定义日志级别;2)LogFormatter类支持自定义模板和ANSI彩色输出;3)Logger类提供线程安全记录、级别过滤等基础功能。该方案可编译为Java

目录
概述
本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个完整的日志生成工具系统。日志是应用开发和运维中的一个基础组件,用于记录应用的运行状态、错误信息、性能数据等。无论是进行应用调试、性能分析还是故障排查,一个功能强大的日志工具都能提供必要的支持。
这个案例展示了如何使用 Kotlin 的字符串处理、时间管理和控制台输出来创建一个功能丰富的日志生成工具。日志工具需要能够支持多种日志级别、提供灵活的格式化选项、支持日志过滤和搜索、提供性能统计等。通过 KMP,这个工具可以无缝编译到 JavaScript,在 OpenHarmony 应用中运行,并支持实时日志输出。
在实际应用中,日志生成工具广泛应用于以下场景:应用调试中的信息输出、性能监控中的数据记录、错误追踪中的堆栈信息、用户行为分析中的事件记录等。通过支持多种日志级别、提供详细的时间戳和上下文信息、生成结构化日志,我们可以帮助开发者更好地理解应用的运行情况。同时,通过 KMP 框架的跨端能力,我们可以在不同平台上使用相同的日志逻辑,确保日志的一致性。
工具的特点
- 多级别日志:支持 DEBUG、INFO、WARN、ERROR、FATAL 等多个日志级别
- 灵活的格式化:支持自定义日志格式和输出模板
- 时间戳管理:自动添加精确的时间戳信息
- 日志过滤:支持按级别、标签、内容等过滤日志
- 性能统计:记录日志统计信息和性能数据
- 跨端兼容:一份 Kotlin 代码可同时服务多个平台
工具功能
1. 日志级别管理
日志级别是日志系统的基础概念,用于区分不同严重程度的日志信息。不同的日志级别适用于不同的场景,开发者可以根据需要选择合适的级别。DEBUG 级别用于开发调试,INFO 级别用于一般信息,WARN 级别用于警告信息,ERROR 级别用于错误信息,FATAL 级别用于致命错误。
- DEBUG:用于开发调试的详细信息
- INFO:用于一般信息和重要事件
- WARN:用于警告信息和潜在问题
- ERROR:用于错误信息和异常
- FATAL:用于致命错误和系统崩溃
2. 日志格式化
日志格式化是指将日志信息按照指定的格式进行组织和输出。合理的日志格式可以使日志更加易读和易于分析。日志格式通常包括时间戳、日志级别、标签、线程信息、消息内容等。开发者可以根据需要自定义日志格式。
- 时间戳:记录日志生成的时间
- 日志级别:标识日志的严重程度
- 标签:用于分类和过滤日志
- 线程信息:记录日志所在的线程
- 消息内容:日志的具体内容
3. 日志过滤和搜索
日志过滤和搜索功能允许开发者快速找到感兴趣的日志信息。通过按级别、标签、时间范围等条件过滤,可以大大提高日志分析的效率。搜索功能支持正则表达式和关键词搜索。
- 按级别过滤:只显示指定级别的日志
- 按标签过滤:只显示指定标签的日志
- 按时间范围过滤:只显示指定时间范围内的日志
- 关键词搜索:搜索包含特定关键词的日志
4. 日志统计和分析
日志统计和分析功能提供了日志的汇总信息和性能数据。这些信息可以帮助开发者了解应用的运行情况和性能特征。统计信息包括日志总数、各级别日志数量、最常见的标签等。
- 日志总数:统计所有日志的数量
- 级别统计:统计各级别日志的数量
- 标签统计:统计各标签日志的数量
- 性能数据:记录日志操作的性能指标
5. 控制台输出管理
控制台输出管理功能控制日志的输出方式和目标。开发者可以选择输出到控制台、文件或其他目标。对于控制台输出,可以设置输出的颜色、格式等。
- 控制台输出:输出到标准输出或错误输出
- 颜色输出:为不同级别的日志使用不同的颜色
- 缓冲管理:管理输出缓冲以提高性能
- 输出控制:控制日志的启用和禁用
核心实现
1. 日志级别定义
enum class LogLevel(val level: Int, val name: String) {
DEBUG(0, "DEBUG"),
INFO(1, "INFO"),
WARN(2, "WARN"),
ERROR(3, "ERROR"),
FATAL(4, "FATAL");
companion object {
fun fromString(name: String): LogLevel {
return values().find { it.name == name.uppercase() } ?: INFO
}
}
}
data class LogEntry(
val timestamp: String,
val level: LogLevel,
val tag: String,
val message: String,
val thread: String,
val stackTrace: String? = null
)
代码说明: 日志级别通过枚举定义,每个级别有对应的数值和名称。LogEntry 数据类封装了日志的所有信息,包括时间戳、级别、标签、消息、线程和堆栈跟踪。
2. 日志格式化
class LogFormatter {
private var format = "[%timestamp%] [%level%] [%tag%] %message%"
fun setFormat(newFormat: String) {
format = newFormat
}
fun format(entry: LogEntry): String {
var result = format
result = result.replace("%timestamp%", entry.timestamp)
result = result.replace("%level%", entry.level.name)
result = result.replace("%tag%", entry.tag)
result = result.replace("%message%", entry.message)
result = result.replace("%thread%", entry.thread)
if (entry.stackTrace != null && result.contains("%stacktrace%")) {
result = result.replace("%stacktrace%", entry.stackTrace)
}
return result
}
fun formatWithColor(entry: LogEntry): String {
val colorCode = when (entry.level) {
LogLevel.DEBUG -> "\u001B[36m" // Cyan
LogLevel.INFO -> "\u001B[32m" // Green
LogLevel.WARN -> "\u001B[33m" // Yellow
LogLevel.ERROR -> "\u001B[31m" // Red
LogLevel.FATAL -> "\u001B[35m" // Magenta
}
val resetCode = "\u001B[0m"
val formatted = format(entry)
return "$colorCode$formatted$resetCode"
}
}
代码说明: LogFormatter 类负责将日志条目格式化为字符串。支持自定义格式模板和颜色输出。颜色输出使用 ANSI 转义码,可以在支持的终端中显示彩色输出。
3. 日志记录器
class Logger(val tag: String) {
private val entries = mutableListOf<LogEntry>()
private val formatter = LogFormatter()
private var minLevel = LogLevel.DEBUG
private var enableConsole = true
private var enableColor = true
fun setMinLevel(level: LogLevel) {
minLevel = level
}
fun setConsoleEnabled(enabled: Boolean) {
enableConsole = enabled
}
fun setColorEnabled(enabled: Boolean) {
enableColor = enabled
}
private fun log(level: LogLevel, message: String, throwable: Throwable? = null) {
if (level.level < minLevel.level) return
val timestamp = java.time.LocalDateTime.now().format(
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
)
val thread = Thread.currentThread().name
val stackTrace = throwable?.stackTraceToString()
val entry = LogEntry(timestamp, level, tag, message, thread, stackTrace)
entries.add(entry)
if (enableConsole) {
val output = if (enableColor) {
formatter.formatWithColor(entry)
} else {
formatter.format(entry)
}
if (level == LogLevel.ERROR || level == LogLevel.FATAL) {
System.err.println(output)
} else {
println(output)
}
}
}
fun debug(message: String) = log(LogLevel.DEBUG, message)
fun info(message: String) = log(LogLevel.INFO, message)
fun warn(message: String) = log(LogLevel.WARN, message)
fun error(message: String, throwable: Throwable? = null) = log(LogLevel.ERROR, message, throwable)
fun fatal(message: String, throwable: Throwable? = null) = log(LogLevel.FATAL, message, throwable)
fun getEntries(): List<LogEntry> = entries.toList()
fun clearEntries() {
entries.clear()
}
}
代码说明: Logger 类是日志记录的主要接口。提供了 debug、info、warn、error、fatal 等方法用于记录不同级别的日志。支持设置最小日志级别、控制台输出和颜色输出。
4. 日志过滤和搜索
class LogFilter {
fun filterByLevel(entries: List<LogEntry>, level: LogLevel): List<LogEntry> {
return entries.filter { it.level == level }
}
fun filterByTag(entries: List<LogEntry>, tag: String): List<LogEntry> {
return entries.filter { it.tag == tag }
}
fun filterByLevelRange(entries: List<LogEntry>, minLevel: LogLevel, maxLevel: LogLevel): List<LogEntry> {
return entries.filter { it.level.level in minLevel.level..maxLevel.level }
}
fun filterByTimeRange(entries: List<LogEntry>, startTime: String, endTime: String): List<LogEntry> {
return entries.filter { it.timestamp in startTime..endTime }
}
fun searchByKeyword(entries: List<LogEntry>, keyword: String): List<LogEntry> {
return entries.filter { it.message.contains(keyword, ignoreCase = true) }
}
fun searchByRegex(entries: List<LogEntry>, pattern: String): List<LogEntry> {
val regex = Regex(pattern)
return entries.filter { regex.containsMatchIn(it.message) }
}
}
代码说明: LogFilter 类提供了多种过滤和搜索功能。支持按级别、标签、时间范围过滤,以及关键词和正则表达式搜索。这些功能可以帮助开发者快速定位感兴趣的日志。
5. 日志统计
class LogStatistics(private val entries: List<LogEntry>) {
fun getTotalCount(): Int = entries.size
fun getCountByLevel(): Map<LogLevel, Int> {
return entries.groupingBy { it.level }.eachCount()
}
fun getCountByTag(): Map<String, Int> {
return entries.groupingBy { it.tag }.eachCount()
}
fun getMostCommonTag(): String? {
return entries.groupingBy { it.tag }.eachCount().maxByOrNull { it.value }?.key
}
fun getMostCommonLevel(): LogLevel? {
return entries.groupingBy { it.level }.eachCount().maxByOrNull { it.value }?.key
}
fun getErrorCount(): Int {
return entries.count { it.level == LogLevel.ERROR || it.level == LogLevel.FATAL }
}
fun getWarningCount(): Int {
return entries.count { it.level == LogLevel.WARN }
}
fun getSummary(): Map<String, Any> {
return mapOf(
"totalCount" to getTotalCount(),
"countByLevel" to getCountByLevel(),
"countByTag" to getCountByTag(),
"errorCount" to getErrorCount(),
"warningCount" to getWarningCount(),
"mostCommonTag" to (getMostCommonTag() ?: "unknown"),
"mostCommonLevel" to (getMostCommonLevel()?.name ?: "unknown")
)
}
}
代码说明: LogStatistics 类提供了日志的统计和分析功能。可以统计各级别日志的数量、各标签日志的数量、错误和警告的总数等。getSummary() 方法返回一个包含所有统计信息的映射。
Kotlin 源代码
// Logger.kt
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
enum class LogLevel(val level: Int, val name: String) {
DEBUG(0, "DEBUG"),
INFO(1, "INFO"),
WARN(2, "WARN"),
ERROR(3, "ERROR"),
FATAL(4, "FATAL")
}
data class LogEntry(
val timestamp: String,
val level: LogLevel,
val tag: String,
val message: String,
val thread: String,
val stackTrace: String? = null
)
class LogFormatter {
private var format = "[%timestamp%] [%level%] [%tag%] %message%"
fun setFormat(newFormat: String) {
format = newFormat
}
fun format(entry: LogEntry): String {
var result = format
result = result.replace("%timestamp%", entry.timestamp)
result = result.replace("%level%", entry.level.name)
result = result.replace("%tag%", entry.tag)
result = result.replace("%message%", entry.message)
result = result.replace("%thread%", entry.thread)
if (entry.stackTrace != null && result.contains("%stacktrace%")) {
result = result.replace("%stacktrace%", entry.stackTrace)
}
return result
}
fun formatWithColor(entry: LogEntry): String {
val colorCode = when (entry.level) {
LogLevel.DEBUG -> "\u001B[36m"
LogLevel.INFO -> "\u001B[32m"
LogLevel.WARN -> "\u001B[33m"
LogLevel.ERROR -> "\u001B[31m"
LogLevel.FATAL -> "\u001B[35m"
}
val resetCode = "\u001B[0m"
val formatted = format(entry)
return "$colorCode$formatted$resetCode"
}
}
class Logger(val tag: String) {
private val entries = mutableListOf<LogEntry>()
private val formatter = LogFormatter()
private var minLevel = LogLevel.DEBUG
private var enableConsole = true
private var enableColor = true
fun setMinLevel(level: LogLevel) {
minLevel = level
}
fun setConsoleEnabled(enabled: Boolean) {
enableConsole = enabled
}
fun setColorEnabled(enabled: Boolean) {
enableColor = enabled
}
private fun log(level: LogLevel, message: String, throwable: Throwable? = null) {
if (level.level < minLevel.level) return
val timestamp = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
)
val thread = Thread.currentThread().name
val stackTrace = throwable?.stackTraceToString()
val entry = LogEntry(timestamp, level, tag, message, thread, stackTrace)
entries.add(entry)
if (enableConsole) {
val output = if (enableColor) {
formatter.formatWithColor(entry)
} else {
formatter.format(entry)
}
if (level == LogLevel.ERROR || level == LogLevel.FATAL) {
System.err.println(output)
} else {
println(output)
}
}
}
fun debug(message: String) = log(LogLevel.DEBUG, message)
fun info(message: String) = log(LogLevel.INFO, message)
fun warn(message: String) = log(LogLevel.WARN, message)
fun error(message: String, throwable: Throwable? = null) = log(LogLevel.ERROR, message, throwable)
fun fatal(message: String, throwable: Throwable? = null) = log(LogLevel.FATAL, message, throwable)
fun getEntries(): List<LogEntry> = entries.toList()
fun clearEntries() { entries.clear() }
}
class LogFilter {
fun filterByLevel(entries: List<LogEntry>, level: LogLevel): List<LogEntry> {
return entries.filter { it.level == level }
}
fun filterByTag(entries: List<LogEntry>, tag: String): List<LogEntry> {
return entries.filter { it.tag == tag }
}
fun searchByKeyword(entries: List<LogEntry>, keyword: String): List<LogEntry> {
return entries.filter { it.message.contains(keyword, ignoreCase = true) }
}
}
class LogStatistics(private val entries: List<LogEntry>) {
fun getTotalCount(): Int = entries.size
fun getCountByLevel(): Map<LogLevel, Int> {
return entries.groupingBy { it.level }.eachCount()
}
fun getCountByTag(): Map<String, Int> {
return entries.groupingBy { it.tag }.eachCount()
}
fun getErrorCount(): Int {
return entries.count { it.level == LogLevel.ERROR || it.level == LogLevel.FATAL }
}
fun getSummary(): Map<String, Any> {
return mapOf(
"totalCount" to getTotalCount(),
"countByLevel" to getCountByLevel(),
"errorCount" to getErrorCount()
)
}
}
fun main() {
val logger = Logger("MainActivity")
logger.debug("应用启动")
logger.info("初始化完成")
logger.warn("内存使用率较高")
logger.error("网络连接失败")
val stats = LogStatistics(logger.getEntries())
println("日志统计: ${stats.getSummary()}")
}
Kotlin 代码说明: 这个实现提供了完整的日志功能。Logger 类是主要接口,提供了多个日志级别的方法。LogFormatter 类负责格式化日志,支持颜色输出。LogFilter 类提供了过滤功能,LogStatistics 类提供了统计功能。
JavaScript 编译代码
// Logger.js
const LogLevel = {
DEBUG: { level: 0, name: "DEBUG" },
INFO: { level: 1, name: "INFO" },
WARN: { level: 2, name: "WARN" },
ERROR: { level: 3, name: "ERROR" },
FATAL: { level: 4, name: "FATAL" }
};
class LogEntry {
constructor(timestamp, level, tag, message, thread, stackTrace = null) {
this.timestamp = timestamp;
this.level = level;
this.tag = tag;
this.message = message;
this.thread = thread;
this.stackTrace = stackTrace;
}
}
class LogFormatter {
constructor() {
this.format = "[%timestamp%] [%level%] [%tag%] %message%";
}
setFormat(newFormat) {
this.format = newFormat;
}
format(entry) {
let result = this.format;
result = result.replace("%timestamp%", entry.timestamp);
result = result.replace("%level%", entry.level.name);
result = result.replace("%tag%", entry.tag);
result = result.replace("%message%", entry.message);
result = result.replace("%thread%", entry.thread);
if (entry.stackTrace && result.includes("%stacktrace%")) {
result = result.replace("%stacktrace%", entry.stackTrace);
}
return result;
}
formatWithColor(entry) {
const colorCode = {
DEBUG: "\x1b[36m",
INFO: "\x1b[32m",
WARN: "\x1b[33m",
ERROR: "\x1b[31m",
FATAL: "\x1b[35m"
}[entry.level.name];
const resetCode = "\x1b[0m";
const formatted = this.format(entry);
return `${colorCode}${formatted}${resetCode}`;
}
}
class Logger {
constructor(tag) {
this.tag = tag;
this.entries = [];
this.formatter = new LogFormatter();
this.minLevel = LogLevel.DEBUG;
this.enableConsole = true;
this.enableColor = true;
}
setMinLevel(level) {
this.minLevel = level;
}
setConsoleEnabled(enabled) {
this.enableConsole = enabled;
}
setColorEnabled(enabled) {
this.enableColor = enabled;
}
log(level, message, throwable = null) {
if (level.level < this.minLevel.level) return;
const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 23);
const thread = "main";
const stackTrace = throwable ? throwable.stack : null;
const entry = new LogEntry(timestamp, level, this.tag, message, thread, stackTrace);
this.entries.push(entry);
if (this.enableConsole) {
const output = this.enableColor ?
this.formatter.formatWithColor(entry) :
this.formatter.format(entry);
if (level === LogLevel.ERROR || level === LogLevel.FATAL) {
console.error(output);
} else {
console.log(output);
}
}
}
debug(message) { this.log(LogLevel.DEBUG, message); }
info(message) { this.log(LogLevel.INFO, message); }
warn(message) { this.log(LogLevel.WARN, message); }
error(message, throwable = null) { this.log(LogLevel.ERROR, message, throwable); }
fatal(message, throwable = null) { this.log(LogLevel.FATAL, message, throwable); }
getEntries() { return [...this.entries]; }
clearEntries() { this.entries = []; }
}
class LogFilter {
filterByLevel(entries, level) {
return entries.filter(e => e.level === level);
}
filterByTag(entries, tag) {
return entries.filter(e => e.tag === tag);
}
searchByKeyword(entries, keyword) {
return entries.filter(e => e.message.toLowerCase().includes(keyword.toLowerCase()));
}
}
class LogStatistics {
constructor(entries) {
this.entries = entries;
}
getTotalCount() {
return this.entries.length;
}
getCountByLevel() {
const counts = {};
this.entries.forEach(e => {
counts[e.level.name] = (counts[e.level.name] || 0) + 1;
});
return counts;
}
getErrorCount() {
return this.entries.filter(e =>
e.level === LogLevel.ERROR || e.level === LogLevel.FATAL
).length;
}
getSummary() {
return {
totalCount: this.getTotalCount(),
countByLevel: this.getCountByLevel(),
errorCount: this.getErrorCount()
};
}
}
// 使用示例
const logger = new Logger("MainActivity");
logger.debug("应用启动");
logger.info("初始化完成");
logger.warn("内存使用率较高");
logger.error("网络连接失败");
const stats = new LogStatistics(logger.getEntries());
console.log("日志统计:", stats.getSummary());
JavaScript 代码说明: JavaScript 版本是 Kotlin 代码的直接转译。使用对象模拟枚举,使用类实现日志功能。整体逻辑和算法与 Kotlin 版本保持一致,确保跨平台的一致性。
ArkTS 调用代码
// LoggerPage.ets
import { Logger, LogLevel, LogFilter, LogStatistics } from './Logger';
@Entry
@Component
struct LoggerPage {
@State logOutput: string = '';
@State filterLevel: string = 'ALL';
@State searchKeyword: string = '';
@State logHistory: string[] = [];
@State statistics: string = '';
@State enableColor: boolean = true;
@State minLogLevel: string = 'DEBUG';
private logger: Logger = new Logger("LoggerPage");
private filter: LogFilter = new LogFilter();
private logLevels = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'];
private filterOptions = ['ALL', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'];
addLog(level: string, message: string) {
switch (level) {
case 'DEBUG':
this.logger.debug(message);
break;
case 'INFO':
this.logger.info(message);
break;
case 'WARN':
this.logger.warn(message);
break;
case 'ERROR':
this.logger.error(message);
break;
case 'FATAL':
this.logger.fatal(message);
break;
}
this.updateLogDisplay();
}
updateLogDisplay() {
const entries = this.logger.getEntries();
this.logHistory = entries.map(e =>
`[${e.timestamp}] [${e.level.name}] [${e.tag}] ${e.message}`
);
this.logOutput = this.logHistory.join('\n');
}
filterLogs() {
const entries = this.logger.getEntries();
let filtered = entries;
if (this.filterLevel !== 'ALL') {
filtered = this.filter.filterByLevel(filtered, this.filterLevel);
}
if (this.searchKeyword.length > 0) {
filtered = this.filter.searchByKeyword(filtered, this.searchKeyword);
}
this.logHistory = filtered.map(e =>
`[${e.timestamp}] [${e.level.name}] [${e.tag}] ${e.message}`
);
this.logOutput = this.logHistory.join('\n');
}
updateStatistics() {
const stats = new LogStatistics(this.logger.getEntries());
const summary = stats.getSummary();
this.statistics = `总日志数: ${summary.totalCount}\n错误数: ${summary.errorCount}\n级别分布: ${JSON.stringify(summary.countByLevel)}`;
}
clearLogs() {
this.logger.clearEntries();
this.logOutput = '';
this.logHistory = [];
this.statistics = '';
}
build() {
Column() {
Text('日志生成工具')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
// 日志级别选择
Row() {
Text('日志级别:')
.fontSize(12)
.fontColor('#666666')
.margin({ right: 10 })
Select(this.logLevels)
.value('INFO')
.onSelect((index: number) => {
this.logger.setMinLevel(this.logLevels[index]);
})
.flex(1)
}
.margin({ bottom: 15 })
.width('100%')
// 快速日志按钮
Row() {
Button('Debug')
.flex(1)
.height(40)
.margin({ right: 5 })
.onClick(() => this.addLog('DEBUG', '调试信息'))
Button('Info')
.flex(1)
.height(40)
.margin({ right: 5 })
.onClick(() => this.addLog('INFO', '一般信息'))
Button('Warn')
.flex(1)
.height(40)
.margin({ right: 5 })
.onClick(() => this.addLog('WARN', '警告信息'))
Button('Error')
.flex(1)
.height(40)
.onClick(() => this.addLog('ERROR', '错误信息'))
}
.margin({ bottom: 15 })
.width('100%')
// 过滤选项
Row() {
Text('过滤:')
.fontSize(12)
.fontColor('#666666')
.margin({ right: 10 })
Select(this.filterOptions)
.value('ALL')
.onSelect((index: number) => {
this.filterLevel = this.filterOptions[index];
this.filterLogs();
})
.flex(1)
}
.margin({ bottom: 15 })
.width('100%')
// 搜索框
Row() {
TextInput({ placeholder: '搜索关键词' })
.value(this.searchKeyword)
.onChange((value: string) => {
this.searchKeyword = value;
this.filterLogs();
})
.flex(1)
.margin({ right: 10 })
Button('统计')
.width(60)
.height(40)
.onClick(() => this.updateStatistics())
}
.margin({ bottom: 15 })
.width('100%')
// 日志显示区域
Column() {
Text('日志输出:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Scroll() {
Text(this.logOutput)
.fontSize(11)
.fontColor('#333333')
.fontFamily('monospace')
.selectable(true)
.width('100%')
.padding(10)
}
.height(200)
.width('100%')
.backgroundColor('#f5f5f5')
.borderRadius(4)
}
.width('100%')
.margin({ bottom: 15 })
// 统计信息
if (this.statistics.length > 0) {
Column() {
Text('统计信息:')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Text(this.statistics)
.fontSize(12)
.fontColor('#666666')
.width('100%')
.padding(10)
.backgroundColor('#fafafa')
.borderRadius(4)
}
.width('100%')
.margin({ bottom: 15 })
}
// 操作按钮
Row() {
Button('刷新')
.flex(1)
.height(40)
.margin({ right: 10 })
.onClick(() => this.updateLogDisplay())
Button('清空')
.flex(1)
.height(40)
.onClick(() => this.clearLogs())
}
.width('100%')
}
.padding(20)
.width('100%')
.height('100%')
.backgroundColor('#ffffff')
}
}
ArkTS 代码说明: 这个示例展示了如何在 ArkTS 中构建一个完整的日志管理用户界面。页面包含了日志级别选择、快速日志按钮、过滤选项、搜索框、日志显示区域和统计信息。用户可以快速添加不同级别的日志,并进行过滤和搜索。整个界面采用了现代化的设计,提供了良好的用户体验。
实战案例
案例 1: 应用启动日志
在应用启动时,记录各个阶段的日志,便于调试和性能分析。
val logger = Logger("App")
logger.info("应用启动开始")
logger.debug("初始化数据库")
logger.debug("加载配置文件")
logger.info("应用启动完成")
案例 2: 错误追踪
在发生错误时,记录详细的错误信息和堆栈跟踪。
val logger = Logger("NetworkManager")
try {
// 网络操作
} catch (e: Exception) {
logger.error("网络请求失败", e)
}
案例 3: 性能监控
记录关键操作的性能数据,用于性能分析。
val logger = Logger("PerformanceMonitor")
logger.info("开始数据库查询")
logger.debug("查询耗时: 150ms")
logger.warn("查询耗时超过100ms")
最佳实践
1. 日志级别选择
- DEBUG:仅在开发时使用,记录详细的调试信息
- INFO:记录重要的业务事件和状态变化
- WARN:记录潜在的问题和异常情况
- ERROR:记录错误和异常
- FATAL:记录致命错误和系统崩溃
2. 日志内容
- 简洁明了:日志信息应该清晰简洁
- 包含上下文:提供足够的上下文信息
- 避免敏感信息:不要记录密码、令牌等敏感信息
- 使用结构化日志:使用统一的格式和标签
3. 性能考虑
- 异步输出:对于高频日志,使用异步输出
- 日志缓冲:使用缓冲减少 I/O 操作
- 日志轮转:定期清理旧日志,防止日志文件过大
- 条件日志:根据日志级别条件输出日志
4. 日志管理
- 统一标签:为不同模块使用统一的标签
- 日志搜索:提供日志搜索和过滤功能
- 日志分析:定期分析日志,发现问题
- 日志备份:定期备份重要的日志
5. 跨平台一致性
- 统一格式:在所有平台上使用统一的日志格式
- 一致的级别:在所有平台上使用相同的日志级别
- 共享逻辑:使用 KMP 共享日志逻辑
- 平台适配:根据平台特性进行适配
总结
日志生成工具是现代应用开发中的一个重要组件。通过使用 Kotlin Multiplatform,我们可以编写一次代码,然后在多个平台上运行,大大提高了开发效率和代码的可维护性。这个案例展示了如何实现多级别日志、灵活的格式化、日志过滤和统计等功能。
在实际应用中,应该根据具体的需求选择合适的日志级别和格式,并遵循最佳实践来确保日志的有效性和性能。同时,应该定期进行日志分析和优化,以提高应用的可维护性和可调试性。通过合理使用日志工具,我们可以为应用的开发、测试和运维提供强大的支持。
更多推荐



所有评论(0)