一张适合中文技术文章的手绘笔记风信息图,主题是“HarmonyOS7 详情页错误边界用页面状态兜底”

前言

日志打印不是开发时随手写一句 success。等问题到了测试机、灰度包、线上用户那里,能不能按模块和动作搜到日志,直接决定排查效率。

这篇单独聊 登录流程 这个场景。重点不是堆 API,而是让日志能按模块、动作和关键参数串起一条可追踪链路。

为什么这个问题经常被写乱

日志打印要有模块名 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

场景:登录失败只能靠日志还原

登录流程的问题经常不是“必现”的。测试同学说某台设备偶尔登录失败,用户说验证码收不到,服务端说没有收到请求。这个时候如果日志只有 successfailclick,基本等于没有日志。真正有用的日志应该能回答三个问题:哪个模块、哪个动作、带了哪些非敏感关键参数。

我通常会给每条日志固定一个模块前缀,比如 [Login],再把动作拆成 input_changevalidate_failedrequest_startrequest_success。这样无论是在控制台、hilog 还是测试截图里,都能快速串起流程。

好日志不是越多越好,而是出问题时能顺着同一条线查下去。

日志字段约定

字段示例说明
模块名[Login]方便过滤和归属
动作名request_start描述当前链路节点
请求标识trace=login-001串联一次操作
关键参数accountLength=8只记录非敏感信息
结果result=success记录最终状态

实操步骤

  1. 每个模块定义固定 moduleName,不要临时手写前缀。
  2. 每次提交生成一个 traceId,同一次操作的日志都带上它。
  3. 点击、校验、请求开始、请求结果、页面跳转都打关键日志。
  4. 只打印账号长度、验证码长度、错误码,不打印手机号、token、验证码明文。
  5. 测试环境可以在 UI 上展示最近日志,方便截图复现。
  6. 日志文案保持结构化,避免一会儿中文一句、一会儿英文一句。

先把页面目标想清楚

在真正写代码之前,先别急着盯着 API。更有用的做法是先想清楚:这个页面到底想解决什么问题,用户最在意的反馈是什么,哪些状态必须一直保持一致。

当你先把这条主线想明白,再回头看组件和状态设计,很多选择都会顺理成章。对小白来说,这一步尤其重要,因为它能帮你从“照着抄”慢慢过渡到“看得懂、改得动”。

完整示例:带模块名和 traceId 的登录日志

@Entry
@Component
struct LoginLogPage {
  @State account: string = ''
  @State code: string = ''
  @State lastLog: string = '等待操作'
  @State traceId: string = 'login-idle'

  private moduleName: string = 'Login'
  private submitCount: number = 0

  private log(action: string, detail: string) {
    const message: string = `[${this.moduleName}] ${action} trace=${this.traceId} ${detail}`
    console.info(message)
    this.lastLog = message
  }

  private nextTraceId(): string {
    this.submitCount += 1
    return `login-${this.submitCount}`
  }

  private maskAccountLength(): string {
    return `accountLength=${this.account.length}`
  }

  private submit() {
    this.traceId = this.nextTraceId()
    this.log('submit_click', `${this.maskAccountLength()} codeLength=${this.code.length}`)

    if (this.account.length < 4) {
      this.log('validate_failed', 'field=account reason=too_short')
      return
    }

    if (this.code.length !== 6) {
      this.log('validate_failed', 'field=code reason=length_invalid')
      return
    }

    this.log('request_start', 'api=/login method=POST')
    const success: boolean = this.code === '123456'

    if (success) {
      this.log('request_success', 'next=home')
    } else {
      this.log('request_failed', 'errorCode=LOGIN_CODE_INVALID')
    }
  }

  build() {
    Column({ space: 14 }) {
      Text('登录日志示例')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)

      TextInput({ placeholder: '请输入账号', text: this.account })
        .onChange((value: string) => {
          this.account = value
          this.log('input_change', `field=account accountLength=${value.length}`)
        })

      TextInput({ placeholder: '请输入 6 位验证码', text: this.code })
        .onChange((value: string) => {
          this.code = value
          this.log('input_change', `field=code codeLength=${value.length}`)
        })

      Button('登录')
        .width('100%')
        .onClick(() => this.submit())

      Column({ space: 6 }) {
        Text('最近日志')
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
        Text(this.lastLog)
          .fontSize(12)
          .fontColor('#666666')
      }
      .alignItems(HorizontalAlign.Start)
      .backgroundColor('#F1F3F5')
      .padding(10)
      .borderRadius(8)
    }.padding(16)
  }
}

把关键代码一段段拆开

moduleName 是固定前缀,不应该每次日志手写。手写前缀很容易出现 [login][LoginPage][UserLogin] 混用,后面过滤日志会很痛苦。

traceId 用来串联一次登录动作。用户点一次登录,从点击、校验、请求到结果都带同一个 trace,排查时可以直接搜索 login-3 还原全过程。

maskAccountLength() 只输出账号长度,不输出账号本身。验证码、token、手机号、身份证、地址这类信息都不应该进入普通日志。日志不是越完整越好,安全边界要先守住。

容易踩坑的点

  • 日志没有模块名,多个页面混在一起看不出来源。
  • 只记录成功,不记录前端校验失败,导致“请求没发出去”无法定位。
  • 直接打印手机号、验证码、token,给测试包和线上排查留下风险。
  • 每条日志格式不同,搜索时无法用固定关键词过滤。
  • 只打 request_failed,没有错误码和 traceId。

优化建议

复杂业务可以再加一层轻量日志封装,把模块名、traceId、动作名统一拼好。注意不要过度封装成难用的工具类,日志如果写起来太费劲,最后大家还是会回到 console.info('success')

线上日志要控制量。输入框 onChange 在真实项目里不建议每个字符都打日志,示例是为了演示链路。生产环境更适合记录提交、校验失败、请求结果这些关键节点。

日志要能串起一次操作

真正有用的日志,应该能从用户点击一路追到请求结果。固定模块名解决“日志归谁管”的问题,traceId 解决“这几条是不是同一次操作”的问题,动作名解决“流程走到哪一步”的问题。

示例里把登录点击、校验失败、请求开始和请求结果都打到同一条链路里。排查时只要搜索某个 trace,就能知道请求有没有发出、失败在前端校验还是服务端返回。

写在最后

生产环境还要控制日志边界。账号、手机号、验证码、token 都不应该明文打印;输入框变化这类高频日志也要降级或关闭。日志的目标是定位问题,不是把用户隐私和无效噪声都塞进控制台。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐