日志与错误处理基础:别只把失败写进控制台

举报
蓝瘦的蜕变 发表于 2026/07/20 11:56:55 2026/07/20
【摘要】 日志和错误处理是后续系统能力 Demo 的底座。页面 Demo 不能只调用 console 或 hilog 后没有反馈,用户需要能看到当前动作是否成功、失败原因是什么。 本页演示三种最小能力: hilog.info:记录普通流程。 hilog.warn:记录可降级但需要关注的状态。 BusinessError:显示 c

日志与错误处理基础:别只把失败写进控制台

日志和错误处理是后续系统能力 Demo 的底座。页面 Demo 不能只调用 consolehilog 后没有反馈,用户需要能看到当前动作是否成功、失败原因是什么。

本页演示三种最小能力:

  • hilog.info:记录普通流程。
  • hilog.warn:记录可降级但需要关注的状态。
  • BusinessError:显示 code/message,不静默吞掉。

交互预期

  • 点击“写 info”,页面新增 info 记录,最近错误变为“暂无错误”。
  • 点击“写 warn”,页面新增 warn 记录,说明这是可降级状态。
  • 点击“模拟错误”,页面显示 code=401, message=标题不能为空
  • 点击“清空日志”,列表回到等待操作状态。

完整示例代码

import { BusinessError } from '@kit.BasicServicesKit'
import { hilog } from '@kit.PerformanceAnalysisKit'

const ENGINEERING_LOG_DOMAIN: number = 0x1200
const ENGINEERING_LOG_TAG: string = 'EngineeringDemo'

class DemoBusinessError extends Error implements BusinessError {
  code: number

  constructor(code: number, message: string) {
    super(message)
    this.code = code
  }
}

class EngineeringLogEntry {
  level: string
  message: string
  detail: string

  constructor(level: string, message: string, detail: string) {
    this.level = level
    this.message = message
    this.detail = detail
  }
}

@Entry
@Component
struct LogErrorHandlingDemo {
  @State feedback: string = '点击按钮后,页面内会同步展示日志意图和错误处理结果。'
  @State lastError: string = '暂无错误'
  @State logEntries: EngineeringLogEntry[] = [
    new EngineeringLogEntry('info', '等待操作', '日志不要只写到控制台,关键结果也要让 Demo 页面可见。')
  ]

  private errorToText(error: BusinessError): string {
    return `code=${error.code}, message=${error.message}`
  }

  private addLog(level: string, message: string, detail: string): void {
    const entries: EngineeringLogEntry[] = [
      new EngineeringLogEntry(level, message, detail)
    ]
    this.logEntries.forEach((item: EngineeringLogEntry) => {
      if (entries.length < 4) {
        entries.push(item)
      }
    })
    this.logEntries = entries
  }

  private writeInfoLog(): void {
    hilog.info(ENGINEERING_LOG_DOMAIN, ENGINEERING_LOG_TAG, 'User tapped info log')
    this.lastError = '暂无错误'
    this.feedback = '已写入 info 级别日志,并在页面内留下可见记录。'
    this.addLog('info', '记录普通流程', '适合记录用户触发、页面进入、轻量状态变化。')
  }

  private writeWarnLog(): void {
    hilog.warn(ENGINEERING_LOG_DOMAIN, ENGINEERING_LOG_TAG, 'User tapped warning log')
    this.lastError = '暂无错误'
    this.feedback = '已写入 warn 级别日志,用来表示可降级但需要关注的状态。'
    this.addLog('warn', '记录降级状态', '适合记录能力不可用、用户未授权、缓存为空等可恢复状态。')
  }

  private simulateBusinessError(): void {
    try {
      this.validateRequiredTitle('')
    } catch (error) {
      const businessError = error as BusinessError
      const errorText: string = this.errorToText(businessError)
      hilog.error(ENGINEERING_LOG_DOMAIN, ENGINEERING_LOG_TAG, 'BusinessError %{public}s', errorText)
      this.lastError = errorText
      this.feedback = '已捕获 BusinessError,错误码和消息都显示在页面内。'
      this.addLog('error', '捕获业务错误', errorText)
    }
  }

  private validateRequiredTitle(title: string): void {
    if (title.length === 0) {
      throw new DemoBusinessError(401, '标题不能为空')
    }
  }

  private clearLogs(): void {
    this.lastError = '暂无错误'
    this.feedback = '日志列表已清空。'
    this.logEntries = [
      new EngineeringLogEntry('info', '等待操作', '继续点击按钮可重新生成日志记录。')
    ]
  }

  private levelColor(level: string): string {
    if (level === 'error') {
      return '#DC2626'
    }
    if (level === 'warn') {
      return '#B45309'
    }
    return '#2563EB'
  }

  @Builder
  logCard(index: number) {
    Column({ space: 8 }) {
      Row() {
        Text(this.logEntries[index].level)
          .fontSize(12)
          .fontColor('#FFFFFF')
          .padding({ left: 8, right: 8, top: 4, bottom: 4 })
          .backgroundColor(this.levelColor(this.logEntries[index].level))
          .borderRadius(8)
        Blank()
      }
      .width('100%')
      Text(this.logEntries[index].message)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text(this.logEntries[index].detail)
        .fontSize(13)
        .fontColor('#4B5563')
        .lineHeight(20)
    }
    .width('100%')
    .padding(14)
    .borderRadius(12)
    .backgroundColor('#FFFFFF')
    .border({ width: 1, color: '#E5E7EB' })
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Scroll() {
      Column({ space: 20 }) {
        Column({ space: 8 }) {
          Text('日志与错误处理基础')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text('日志分级要表达状态,错误处理要把 code 和 message 留出来。')
            .fontSize(14)
            .fontColor('#4B5563')
            .lineHeight(22)
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        Row({ space: 10 }) {
          Button('写 info')
            .height(46)
            .layoutWeight(1)
            .fontSize(14)
            .backgroundColor('#2563EB')
            .onClick(() => {
              this.writeInfoLog()
            })
          Button('写 warn')
            .height(46)
            .layoutWeight(1)
            .fontSize(14)
            .backgroundColor('#B45309')
            .onClick(() => {
              this.writeWarnLog()
            })
          Button('模拟错误')
            .height(46)
            .layoutWeight(1)
            .fontSize(14)
            .backgroundColor('#DC2626')
            .onClick(() => {
              this.simulateBusinessError()
            })
        }
        .width('100%')

        Button('清空日志')
          .height(46)
          .width('100%')
          .fontSize(15)
          .fontColor('#334155')
          .backgroundColor('#E5E7EB')
          .onClick(() => {
            this.clearLogs()
          })

        Text(this.feedback)
          .width('100%')
          .fontSize(14)
          .fontColor('#1D4ED8')
          .lineHeight(22)
          .padding(12)
          .backgroundColor('#EFF6FF')
          .borderRadius(10)

        Column({ space: 8 }) {
          Text('最近错误')
            .fontSize(17)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text(this.lastError)
            .fontSize(13)
            .fontColor(this.lastError === '暂无错误' ? '#4B5563' : '#DC2626')
            .lineHeight(20)
        }
        .width('100%')
        .padding(16)
        .borderRadius(14)
        .backgroundColor('#FFFFFF')
        .border({ width: 1, color: '#E5E7EB' })
        .alignItems(HorizontalAlign.Start)

        ForEach(this.logEntries, (item: EngineeringLogEntry, index: number) => {
          this.logCard(index)
        }, (item: EngineeringLogEntry) => item.message + item.detail)
      }
      .width('100%')
      .padding(24)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。