Chuyển đến nội dung chính

Bài 22: Production Monitoring & Best Practices

Error tracking (Sentry), analytics. Structured logging, health checks. Security best practices (CORS, CSP, XSS). Monorepo với pnpm workspace, shared packages. Design system.

💻 Lập trình — Bài 22 Bài 22: Production Monitoring & Best Practices

Vue.js & Nuxt: Từ Cơ bản đến Nâng cao

Phần 6: Testing, Deployment & Production

xdev.asia

1. Error Tracking với Sentry

npm i @sentry/vue @sentry/node
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  Sentry.init({
    app: nuxtApp.vueApp,
    dsn: config.public.sentryDsn,
    environment: config.public.env,
    integrations: [
      Sentry.browserTracingIntegration({
        router: useRouter(),
      }),
      Sentry.replayIntegration(),
    ],
    tracesSampleRate: 0.1,
    replaysSessionSampleRate: 0.05,
  })
})

// server/plugins/sentry.ts
import * as Sentry from '@sentry/node'

export default defineNitroPlugin((nitro) => {
  Sentry.init({
    dsn: useRuntimeConfig().sentryDsn,
    tracesSampleRate: 0.2,
  })

  nitro.hooks.hook('error', (error) => {
    Sentry.captureException(error)
  })
})

2. Structured Logging

// server/utils/logger.ts
import { consola, createConsola } from 'consola'

export const logger = createConsola({
  level: process.env.NODE_ENV === 'production' ? 3 : 4,
  formatOptions: {
    date: true,
    colors: process.env.NODE_ENV !== 'production',
  },
})

// Sử dụng
logger.info('User logged in', { userId: user.id, ip: getRequestIP(event) })
logger.error('Payment failed', { orderId, error: err.message })

3. Health Checks

// server/api/health.get.ts
export default defineEventHandler(async () => {
  const checks = {
    status: 'ok',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    checks: {} as Record<string, string>,
  }

  // Database check
  try {
    await db.execute(sql`SELECT 1`)
    checks.checks.database = 'ok'
  } catch {
    checks.checks.database = 'error'
    checks.status = 'degraded'
  }

  // Redis check
  try {
    await redis.ping()
    checks.checks.redis = 'ok'
  } catch {
    checks.checks.redis = 'error'
    checks.status = 'degraded'
  }

  return checks
})

4. Security Best Practices

// nuxt.config.ts
export default defineNuxtConfig({
  security: {
    headers: {
      contentSecurityPolicy: {
        'default-src': ["'self'"],
        'script-src': ["'self'", "'nonce-{{nonce}}'"],
        'style-src': ["'self'", "'unsafe-inline'"],
        'img-src': ["'self'", 'data:', 'https:'],
      },
      xFrameOptions: 'DENY',
      xContentTypeOptions: 'nosniff',
      referrerPolicy: 'strict-origin-when-cross-origin',
    },
    corsHandler: {
      origin: ['https://myapp.com'],
      methods: ['GET', 'POST', 'PUT', 'DELETE'],
    },
    rateLimiter: {
      tokensPerInterval: 100,
      interval: 60000,
    },
  },
})

// server/middleware/csrf.ts
export default defineEventHandler((event) => {
  if (['POST', 'PUT', 'DELETE'].includes(event.method)) {
    const token = getHeader(event, 'x-csrf-token')
    const sessionToken = getCookie(event, 'csrf-token')
    if (!token || token !== sessionToken) {
      throw createError({ statusCode: 403, message: 'Invalid CSRF token' })
    }
  }
})

5. Monorepo với pnpm Workspace

# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
# Cấu trúc
monorepo/
├── pnpm-workspace.yaml
├── apps/
│   ├── web/          # Nuxt app chính
│   └── admin/        # Nuxt admin panel
├── packages/
│   ├── ui/           # Shared Vue components
│   ├── utils/        # Shared utilities
│   └── config/       # Shared configs (eslint, tsconfig)
// packages/ui/package.json
{
  "name": "@myapp/ui",
  "exports": {
    ".": "./src/index.ts",
    "./components/*": "./src/components/*.vue"
  }
}

// apps/web/package.json
{
  "dependencies": {
    "@myapp/ui": "workspace:*",
    "@myapp/utils": "workspace:*"
  }
}

6. Design System

<!-- packages/ui/src/components/BaseButton.vue -->
<script setup lang="ts">
interface Props {
  variant?: 'primary' | 'secondary' | 'danger'
  size?: 'sm' | 'md' | 'lg'
  loading?: boolean
  disabled?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  variant: 'primary',
  size: 'md',
  loading: false,
  disabled: false,
})

const emit = defineEmits<{ click: [event: MouseEvent] }>()

const classes = computed(() => [
  'btn',
  `btn-${props.variant}`,
  `btn-${props.size}`,
  { 'btn-loading': props.loading },
])
</script>

<template>
  <button
    :class="classes"
    :disabled="disabled || loading"
    @click="emit('click', $event)"
  >
    <span v-if="loading" class="spinner" />
    <slot />
  </button>
</template>

7. Production Checklist

Hạng mụcChecklist
SecurityCSP headers, CSRF, rate limiting, input validation
PerformanceLighthouse ≥ 90, bundle < 200KB, image optimization
MonitoringSentry, health checks, uptime monitoring
SEOMeta tags, sitemap.xml, robots.txt, structured data
CI/CDAutomated tests, staging environment, rollback plan
BackupDatabase backup, env secrets rotation, disaster recovery

🎉 Hoàn thành series Vue.js & Nuxt: Từ Cơ bản đến Nâng cao!