Skip to content

日志与监控:集成 Sentry、Datadog、Prometheus

通过中间件收集指标:req_duration_ms, http_status

在生产环境中,可观测性(Observability) 是保障服务稳定性的核心。Hono 作为轻量级框架,虽不内置监控功能,但其灵活的中间件机制让你能轻松集成主流监控工具如 Sentry(错误追踪)、Datadog(APM & 日志)、Prometheus(指标)。

本文将展示如何通过自定义中间件,统一收集关键指标并上报。

一、核心目标

  1. 记录每个请求的 响应时间req_duration_ms
  2. 记录 HTTP 状态码http_status
  3. 捕获未处理异常并上报
  4. 支持多平台(Sentry / Datadog / Prometheus)

二、通用监控中间件设计

ts
// middleware/monitoring.ts
import { Hono } from 'hono'

type MonitoringOptions = {
  sentry?: boolean
  datadog?: boolean
  prometheus?: boolean
}

export const monitoringMiddleware = (options: MonitoringOptions = {}) => {
  // Prometheus 指标(如果启用)
  let requestCounter: any = null
  let durationHistogram: any = null

  if (options.prometheus) {
    const client = require('prom-client')
    requestCounter = new client.Counter({
      name: 'http_requests_total',
      help: 'Total HTTP requests',
      labelNames: ['method', 'path', 'status']
    })

    durationHistogram = new client.Histogram({
      name: 'http_request_duration_ms',
      help: 'Duration of HTTP requests in ms',
      labelNames: ['method', 'path', 'status'],
      buckets: [10, 50, 100, 200, 500, 1000, 2000]
    })
  }

  return async (c: any, next: () => Promise<any>) => {
    const start = Date.now()

    // 增强 c 对象以记录状态
    let statusCode = 500 // 默认失败
    const originalStatus = c.status
    c.status = (code: number) => {
      statusCode = code
      return originalStatus.call(c, code)
    }

    try {
      await next()
    } catch (error: any) {
      statusCode = 500

      // 上报错误
      if (options.sentry) {
        // Sentry.captureException(error)
        console.error('[Sentry] Captured error:', error.message)
      }

      if (options.datadog) {
        // Datadog.logger.error('Request failed', { error })
        console.error('[Datadog] Error:', error.message)
      }

      throw error
    } finally {
      const duration = Date.now() - start

      // 标签化指标
      const labels = {
        method: c.req.method,
        path: c.req.path,
        status: statusCode.toString()
      }

      // Prometheus
      if (options.prometheus && requestCounter && durationHistogram) {
        requestCounter.inc(labels)
        durationHistogram.observe(duration, labels)
      }

      // Datadog APM / Logs
      if (options.datadog) {
        // Datadog.metrics.increment('http.requests', 1, labels)
        // Datadog.tracer.trace('http.request', { duration, ...labels })
        console.log('[Datadog Metric]', { duration, ...labels })
      }

      // 可选:结构化日志
      console.log({
        level: 'info',
        msg: 'HTTP Request',
        req_method: c.req.method,
        req_path: c.req.path,
        req_query: c.req.query(),
        req_ip: c.req.header('x-forwarded-for') || c.req.ip,
        res_status: statusCode,
        req_duration_ms: duration,
        user_agent: c.req.header('user-agent')
      })
    }
  }
}

三、集成 Sentry(错误追踪)

Sentry 用于捕获异常和性能问题。

1. 安装
bash
npm install @sentry/hono @sentry/serverless
2. 配置
ts
// sentry.ts
import * as Sentry from '@sentry/serverless'
import { Hono } from 'hono'

Sentry.AWSLambda.init({
  dsn: 'YOUR_SENTRY_DSN',
  tracesSampleRate: 0.2,
})

const app = new Hono()

// 使用 Sentry 中间件(自动捕获错误)
app.use('*', Sentry.Handlers.requestHandler())
app.use('*', Sentry.Handlers.tracingHandlers())

// 你的路由
app.get('/error', () => {
  throw new Error('Test Sentry')
})

// 错误处理
app.onError((err, c) => {
  Sentry.captureException(err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

Sentry 会自动捕获未处理异常、慢请求、数据库调用等。

四、集成 Datadog(APM & 日志)

Datadog 提供全栈可观测性。

1. Cloudflare Workers + Datadog

使用 Datadog Log Forwarder 或 RUM。

ts
// 在中间件中发送日志
if (options.datadog) {
  const log = {
    service: 'hono-app',
    http: { method: c.req.method, url: c.req.url, status: statusCode },
    duration_ms: duration,
    timestamp: new Date().toISOString()
  }
  // 通过 fetch 发送到 Datadog Log API 或边车代理
  fetch('https://http-intake.logs.datadoghq.com/v1/input/YOUR_API_KEY', {
    method: 'POST',
    body: JSON.stringify(log),
    headers: { 'Content-Type': 'application/json' }
  }).catch(() => {})
}
2. Node.js 环境
bash
npm install dd-trace
ts
// tracer.js
const tracer = require('dd-trace').init()
const app = new Hono()

app.use(monitoringMiddleware({ datadog: true }))

五、集成 Prometheus(指标监控)

Prometheus 用于时序指标监控。

1. 安装
bash
npm install prom-client
2. 暴露指标端点
ts
// metrics.ts
import client from 'prom-client'

export const register = new client.Registry()

// 暴露指标
app.get('/metrics', async (c) => {
  const metrics = await register.metrics()
  return c.text(metrics, 200, {
    'Content-Type': client.contentType
  })
})
3. 使用中间件
ts
app.use('*', monitoringMiddleware({ 
  prometheus: true 
}))
4. Grafana 面板建议
  • QPSrate(http_requests_total[5m])
  • P95 延迟histogram_quantile(0.95, rate(http_request_duration_ms_bucket[5m]))
  • 错误率sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))

六、最终应用结构

ts
const app = new Hono()

// 监控中间件(可组合)
app.use('*', monitoringMiddleware({
  sentry: true,
  datadog: true,
  prometheus: true
}))

app.get('/', (c) => c.text('Hello World'))
app.get('/error', () => { throw new Error('Oops') })

// Prometheus 指标
app.get('/metrics', serveMetrics)

// Kubernetes 健康检查
app.get('/healthz', (c) => c.text('OK'))

export default app

七、最佳实践总结

实践说明
✅ 使用中间件统一收集指标避免重复代码
✅ 分离关注点错误(Sentry)、指标(Prometheus)、日志(Datadog)
✅ 添加 req_ipuser-agent 等上下文便于排查问题
✅ 避免在 /healthz 中做监控检查防止级联故障
✅ 采样上报高频请求避免打爆监控系统
✅ 设置告警规则如 P95 > 1s 或错误率 > 1%

八、结论

通过一个简单的中间件,你就能为 Hono 应用接入完整的监控体系:

  • Sentry:第一时间发现线上错误;
  • Prometheus + Grafana:可视化性能趋势;
  • Datadog:全链路追踪与日志聚合。

可观测性不是事后补救,而是架构设计的一部分。尽早集成,让你的应用“看得见、管得住”。