ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

RedwoodJS 日志体系实战指南:基于 pino 的 API 侧日志、Prisma 查询追踪与云端传输

RedwoodJS 日志体系实战指南:基于 pino 的 API 侧日志、Prisma 查询追踪与云端传输 后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载RedwoodJS 内置了一套以 pino 为核心的“有主见”的日志方案redwoodjs/api/logger覆盖从本地开发的可读性输出到 Serverless 生产环境的第三方日志服务流式传输。本篇指南将带你掌握 RedwoodJS 日志的全部核心能力初始化与快速上手、日志级别与红action 脱敏、LogFormatter 格式化输出、文件与传输流Transport Stream两类 destination 配置以及 Prisma 查询日志与慢查询阈值调优并深入源码验证其默认配置与底层实现。说明RedwoodJS 日志仅针对 api 侧设计浏览器端与 web 侧的错误上报功能计划在后续版本提供。Quick Start三分钟接入 API 侧日志RedwoodJS 在服务service、函数function或任意 lib 中引入logger即可开始记录日志用法与console几乎一致。项目模板中 api/src/lib/logger.ts 已经为你创建好了默认实例import { createLogger } from redwoodjs/api/logger /** * Creates a logger with RedwoodLoggerOptions * * These extend and override default LoggerOptions, * can define a destination like a file or other supported pino log transport stream, * and sets whether or not to show the logger configuration settings (defaults to false) * * param RedwoodLoggerOptions * * RedwoodLoggerOptions have * param {options} LoggerOptions - defines how to log, such as redaction and format * param {string | DestinationStream} destination - defines where to log, such as a transport stream or file * param {boolean} showConfig - whether to display logger configuration on initialization */ export const logger createLogger({})createLogger接收一个RedwoodLoggerOptions对象其结构定义在 packages/api/src/logger/index.tsoptionsLoggerOptions即 pino 的 options定义如何记录日志如级别、脱敏、格式destinationstring | DestinationStream定义记录到何处——标准输出、文件或远程传输流showConfigboolean默认false置为true时会在初始化时把日志配置打印到控制台便于调试。然后在 service、lib 或 function 中按熟悉的console风格使用// then, in your api service, lib, or function import { logger } from src/lib/logger //... logger.trace( items service - About to save item ${item.name}) logger.info(Saving item ${item.name}) logger.debug({ item }, Item ${item.name} detail) logger.warn(item, Item ${item.id} is missing a name) logger.warn({ missing: { name: item.name } }, Item ${item.id} is missing values) logger.error(error, Failed to save item)每个日志方法的第一参数可以是普通元数据对象也可以直接传错误对象第二参数为日志消息字符串。从源码实现看createLogger最终调用 pino 的pino(options, stream)packages/api/src/logger/index.ts因此 pino 丰富的 APIchild、flush、level等在 RedwoodJS 中全部可用。旧版升级手动配置如果你的应用早于 v0.28 且需要补齐日志只需从 Create Redwood Application 模板复制两个文件复制 packages/create-redwood-app/templates/ts/api/src/lib/logger.ts 到api/src/lib/logger.ts必选。该文件定义了 logger 实例之后可逐步用logger.info()/logger.debug()替换原有的console.log()可选复制packages/create-redwood-app/templates/ts/api/src/lib/db.ts替换api/src/lib/db.ts或.js用于配置 Prisma 日志详见下文。Options如何记录日志How to Log日志级别Log Level可选值fatal、error、warn、info、debug、trace或silent。日志级别是最小级别语义——例如级别设为info则fatal、error、warn、info都会被输出。silent则完全关闭日志。RedwoodJS 会根据运行环境自动选择合理的默认最低级别源码逻辑见 packages/api/src/logger/index.ts环境默认级别说明DevelopmentNODE_ENVdevelopmenttrace开发服务器输出详尽Production非 dev/testwarn只保留关键告警与错误TestNODE_ENVtestsilent测试时默认静默可用LOG_LEVEL环境变量或options.level覆盖默认值。isDevelopment/isTest/isProduction三个判定也导出在 packages/api/src/logger/index.ts其中isProduction的定义是“非 development 且非 test”。import { createLogger } from redwoodjs/api/logger /** * Creates a logger with RedwoodLoggerOptions * * These extend and override default LoggerOptions, * can define a destination like a file or other supported pino log transport stream, * and sets whether or not to show the logger configuration settings (defaults to false) * * param RedwoodLoggerOptions * * RedwoodLoggerOptions have * param {options} LoggerOptions - defines how to log, such as redaction and format * param {string | DestinationStream} destination - defines where to log, such as a transport stream or file * param {boolean} showConfig - whether to display logger configuration on initialization */ export const logger createLogger({ options: { level: info } })排障提示部署后看不到日志输出时可考虑把级别临时下调到info或debug例如线上环境默认只输出warn及以上。敏感信息脱敏Redaction日志中泄露邮箱、密码、Token 是常见事故源。RedwoodJS 提供默认脱敏列表redactionsList源码位于 packages/api/src/logger/index.ts除文档中列出的access_token、accessToken、DATABASE_URL、email、event.headers.authorization、host、jwt、JWT、password、params、secret外还额外覆盖了data.*嵌套路径下的email、password、salt、hashedPassword、jwt、secret、access_token等组合基本覆盖了 GraphQL 请求载荷data结构中常见的敏感字段import { createLogger } from redwoodjs/api/logger /** * Custom redaction list */ //... export const logger createLogger({ options: { redact: [...redactionsList, ssn,credit_card_number] }, })注意如果自定义redact请务必展开redactionsList保留默认项否则只会脱敏你列出的键例如仅ssn,credit_card_number。pino 的redact选项支持三种形态见 pino redaction 文档因此在任何未显式覆盖redact的 logger 上这些敏感键默认都会被自动打码。LogFormatter 日志格式化原“Pretty Printing”重要自 v0.41 起RedwoodJS 不再支持 pino 的 “pretty printing”pino-pretty已废弃且生产环境格式化会带来额外开销、无法送往传输流。取而代之的是 RedwoodJS 自研的LogFormatter。LogFormatter基于 pino-colada。把日志管道给格式化器echo {\level\: 30, \message\: \Hello RedwoodJS\} | yarn rw-log-formatter输出11:00:28 Hello RedwoodJS ✨ Done in 0.14s.使用方式yarn rw dev已自动开启格式化rw serve时可手动管道yarn rw dev yarn rw serve | yarn rw-log-formatter yarn rw serve api | yarn rw-log-formatter注意rw serve会把 Node 环境设为production因此默认只输出warn/error级别若想看到更多输出需把日志级别配置为debug或更低。格式化后的输出用 emoji 区分级别如 表示debug、 表示info。格式化器的具体实现与测试可参考 packages/api-server/src/logFormatter/README.md 与 packages/api-server/src/tests/logFormatter.test.ts。自定义日志载荷Custom Payload除了query、data等 GraphQL 预设字段你还可以用custom键记录自己的消息或对象。以下post表示一篇含id、title、commentCount、description的博客文章// 记录单个标题 logger.debug({ custom: post.title }, The title of a Post) // 记录自定义对象载荷 logger.debug( { custom: { title: post.title, comments: post.commentCount, }, }, Post with count of comments ) // 更深的嵌套载荷 logger.debug( { custom: { title: post.title, details: { id: post.id, description: post.description, comments: post.commentCount, }, }, }, Post details ) // 记录整个对象 logger.debug( { custom: post, }, Post details )GraphQL 日志RedwoodJS 的 GraphQL 服务通过useRedwoodLoggerenvelop 插件详见 GraphQL 日志文档注入额外的日志数据Request IdUser-AgentGraphQL Operation NameGraphQL QueryGraphQL Data这些数据在调试 GraphQL 请求链路时非常关键。生产环境日志建议生产环境通常不适用 LogFormatter 格式化而是以 ndjson 格式把日志交给宿主平台的日志处理器或应用监控服务去处理、存储与展示只记录warn和error级别避免info/debug的噪音这些更适合 staging 或集成环境。嵌套日志Nested Logging避免键冲突当元数据键与 pino 或第三方传输流需要的键冲突时可以用nestedKey把元数据嵌套到log、payload等自定义属性下nestedKey: log,注意使用nestedKey后redact路径需要手动加上前缀。例如嵌套键为log时脱敏email应改为log.email。Destination记录到哪里Where to Logdestination选项决定 API 侧日志语句的去向标准输出、文件或传输流。createLogger源码packages/api/src/logger/index.ts会区分三种情况isFiledestination为字符串路径即写文件isStreamdestination为传输流对象均未提供输出到标准输出。此外showConfig: true会在初始化时打印环境判定、logLevel、合并后的options与destination方便确认实际生效配置。开发服务器Dev Server开发环境中日志直接输出到 dev server 的标准输出。写文件Log to File在开发环境或其它可写文件系统的环境中可把destination指向文件路径/** * Log to a File */ export const logger createLogger({ //options: {}, destination: /path/to/file/api.log, })注意部署到 Netlify 或 Vercel 时不允许写文件。源码中当isFile isProduction时会给出警告提示必须确保生产环境具备文件系统访问能力。传输流Transport StreamsServerless 函数的执行是瞬时的日志输出同样转瞬即逝——若不及时监控关键告警、错误或异常很容易丢失。因此生产环境推荐把日志发送到“传输流”以便持久化与检索。pino 的“传输流”是消费 pino 日志的补充工具pino 官方提供多种已知传输见 pino transports。注意并非所有已知 pino 传输都适用于 Serverless 环境。下面给出 Logflare 与 Datadog 的配置示例完整可参考下方配置示例小节。默认配置总览Default Configuration OverviewRedwoodJS 提供的“有主见”默认配置定义于 packages/api/src/logger/index.ts 的defaultLoggerOptions及logLevel使用自定义 LogFormatter 着色并加 emoji忽略hostname、pid等事件属性让日志更干净为日志输出加级别前缀使用省略服务器名的短消息时间以 GMT 人类可读格式呈现开发/测试环境默认级别trace生产环境默认warn可通过LOG_LEVEL环境变量覆盖默认级别通过内置redactionsList脱敏host及其它敏感键。配置示例常见覆盖与自定义覆盖最低日志级别把生产环境的默认warn下调为debug/** * Override minimum log level to debug */ export const logger createLogger({ options: { level: debug }, })自定义脱敏列表追加自定义键my_secret_key到默认列表/** * Customize a redactions list to add my_secret_key */ import { redactionsList } from redwoodjs/api/logger export const logger createLogger({ options: { redact: [...redactionsList, my_secret_key] }, })写物理文件/** * Log to a File */ export const logger createLogger({ options: {}, destination: /path/to/file/api.log, })同样受 Netlify / Vercel 写文件限制约束。自定义传输流以 Honeybadger 为例若 pino 没有对应服务的现成传输包可用 Node.js 内置stream包的Writable类自行实现yarn workspace api add stream yarn workspace api add honeybadger-io/js// api/src/lib/logger.ts import { createLogger } from redwoodjs/api/logger import { Writable } from stream const Honeybadger require(honeybadger-io/js) Honeybadger.configure({ apiKey: process.env.HONEYBADGER_API_KEY, }) const HoneybadgerStream () { const stream new Writable({ write(chunk: any, encoding: BufferEncoding, fnOnFlush: (error?: Error | null) void) { Honeybadger.notify(chunk.toString()) fnOnFlush() }, }) return stream } /** * Creates a logger. Options define how to log. Destination defines where to log. * If no destination, std out. */ export const logger createLogger({ options: { level: debug }, destination: HoneybadgerStream(), })运行前确保环境变量HONEYBADGER_API_KEY已配置。Writable.write(chunk, encoding, callback)的接口说明见 Node.js stream 文档。传输到 Datadogyarn workspace api add pino-datadog// api/src/lib/logger.ts import datadog from pino-datadog /** * Creates a synchronous pino-datadog stream * * param {object} options - Datadog options including your accounts API Key * * typedef {DestinationStream} */ export const stream datadog.createWriteStreamSync({ apiKey: process.env.DATADOG_API_KEY, ddsource: my-source-name, ddtags: tag,not,it, service: my-service-name, size: 1, }) /** * Creates a logger with RedwoodLoggerOptions * * These extend and override default LoggerOptions, * can define a destination like a file or other supported pino log transport stream, * and sets whether or not to show the logger configuration settings (defaults to false) * * param RedwoodLoggerOptions * * RedwoodLoggerOptions have * param {options} LoggerOptions - defines how to log, such as redaction and format * param {string | DestinationStream} destination - defines where to log, such as a transport stream or file * param {boolean} showConfig - whether to display logger configuration on initialization */ export const logger createLogger({ options: {}, destination: stream, })传输到 Logflareyarn workspace api add pino-logflare// api/src/lib/logger.ts import { createWriteStream } from pino-logflare /** * Creates a pino-logflare stream * * param {object} options - Logflare options including * your accounts API Key and source token id * * typedef {DestinationStream} */ export const stream createWriteStream({ apiKey: process.env.LOGFLARE_API_KEY, sourceToken: process.env.LOGFLARE_SOURCE_TOKEN, }) export const logger createLogger({ options: {}, destination: stream, })传输到 logDNAyarn workspace api add pino-logdna// api/src/lib/logger.ts import pinoLogDna from pino-logdna const stream pinoLogDna({ key: process.env.LOGDNA_INGESTION_KEY, onError: console.error, }) /** * Creates a logger with RedwoodLoggerOptions * * These extend and override default LoggerOptions, * can define a destination like a file or other supported pino log transport stream, * and sets whether or not to show the logger configuration settings (defaults to false) * * param RedwoodLoggerOptions * * RedwoodLoggerOptions have * param {options} LoggerOptions - defines how to log, such as redaction and format * param {string | DestinationStream} destination - defines where to log, such as a transport stream or file * param {boolean} showConfig - whether to display logger configuration on initialization */ export const logger createLogger({ options: {}, destination: stream, })传输到 Papertrailyarn workspace api add pino-papertrailimport papertrail from pino-papertrail const stream papertrail.createWriteStream({ appname: my-app, host: *****.papertrailapp.com, port: *****, }) /** * Creates a logger with RedwoodLoggerOptions * * These extend and override default LoggerOptions, * can define a destination like a file or other supported pino log transport stream, * and sets whether or not to show the logger configuration settings (defaults to false) * * param RedwoodLoggerOptions * * RedwoodLoggerOptions have * param {options} LoggerOptions - defines how to log, such as redaction and format * param {string | DestinationStream} destination - defines where to log, such as a transport stream or file * param {boolean} showConfig - whether to display logger configuration on initialization */ export const logger createLogger({ options: {}, destination: stream, })Papertrail 选项说明可在 options 对象中传入以下属性PropertyTypeDescriptionappname默认 pinostring应用名称host默认 localhoststringPapertrail 目标地址port默认 1234numberPapertrail 目标端口connection默认 udpstringPapertrail 连接方式tls/tcp/udpecho默认 trueboolean是否在控制台回显消息message-only默认 falseboolean只发送msg属性作为消息给 Papertrailbackoff-strategy默认new ExponentialStrategy()BackoffStrategytls/tcp 套接字错误的指数退避重试策略Prisma 日志Prisma LoggingRedwoodJS 的日志方案与 Prisma Client 深度集成既能观测数据库连接问题、慢查询也能捕获意外错误。集成分两步模板实现见 packages/create-redwood-app/templates/ts/api/src/lib/db.ts创建 Prisma Client 时用emitLogLevels设置要发出的日志级别emit: event用handlePrismaLogging把 Prisma 发出的事件接入 Redwood logger并传入相同的级别集合。/* * Instance of the Prisma Client */ export const db new PrismaClient({ log: emitLogLevels([info, warn, error]), }) handlePrismaLogging({ db, logger, logLevels: [info, warn, error], })两个工具函数emitLogLevels与handlePrismaLogging均由redwoodjs/api/logger导出实现细节见 packages/api/src/logger/index.tsemitLogLevels(levels)把每个级别映射为{ emit: event, level }的LogDefinitionhandlePrismaLogging内部先创建子 logger 注入prisma.clientVersion读取自db[_clientVersion]再对每个级别通过db.$on(level, handler)订阅事件把 Prisma 事件转成对应的logger.info/logger.warn/logger.error。默认级别为info、warn、errordefaultLogLevelspackages/api/src/logger/index.ts。如需记录每一条查询加上query级别log: emitLogLevels([info, warn, error, query]),如果想去掉info只保留[warn, error]即可。慢查询Slow Queries若启用了 Prismaquery级别且 Logger 处于debug级别则所有查询语句都会被记录。否则超过阈值的查询会以warn级别记录。默认阈值是 2 秒源码常量DEFAULT_SLOW_QUERY_THRESHOLD 2_000即 2000ms见 packages/api/src/logger/index.ts。可通过slowQueryThreshold自定义handlePrismaLogging({ db, logger, logLevels: [query, info, warn, error], slowQueryThreshold: 5_000, // in ms })从源码看query事件处理会依据event.duration slowQueryThreshold分支超阈值记录Slow Query performed in ${duration} msecwarn 级别未超阈值记录Query performed in ${duration} msecdebug 级别。也就是说调低 Logger 级别到debug后配合query级别即可看到全部查询及耗时这为定位 N1 查询与索引问题提供了直接依据。高级用法Advanced Use子 LoggerChild Loggers有时需要为每一条日志附加固定信息此时使用 pino 的子 Logger。子 Logger 会在每条输出中自动带上绑定字段import { db } from src/lib/db import { logger } from src/lib/logger export const userExamples ({}, { info }) { // Adds path to the log const childLogger logger.child({ path: info.fieldName }) childLogger.trace(I am in find many user examples resolver) return db.userExample.findMany() } export const userExample async ({ id }, { info }) { // Adds id and the path to the log const childLogger logger.child({ id, path: info.fieldName }) childLogger.trace(I am in the find a user example by id resolver) const result await db.userExample.findUnique({ where: { id }, }) // Since this is the child logger, here id and path will be included as well childLogger.debug({ ...result }, This is the detail for the user) return result }RedwoodJS 自身的 Prisma 日志集成正是利用子 Logger 注入 Prisma Client 版本号让每条 Prisma 日志都携带版本上下文packages/api/src/logger/index.tslogger.child({ prisma: { clientVersion: db[_clientVersion] }, })刷新日志缓冲Flushing the Log当使用异步 destination可能缓冲日志行时可手动刷新缓冲logger.flush()该场景主要面向异步日志——在其它行写入期间日志可能被缓冲在内存中。总结RedwoodJS 日志方案以createLogger({ options, destination, showConfig })为统一入口围绕“如何记录options”与“记录到何处destination”两条主线展开开发环境用 LogFormatter 获得带颜色与 emoji 的可读输出LOG_LEVEL与默认环境级别保证不同环境的行为可预期redactionsList默认脱敏阻断敏感信息外泄生产环境则通过传输流把 ndjson 日志送往 Datadog、Logflare、logDNA、Papertrail 等监控服务并结合 Prisma 的emitLogLevels/handlePrismaLogging观测查询与慢查询。整套体系基于 pino 构建完整继承了 pino 的 options、子 Logger、传输流生态是 RedwoodJS api 侧可观测性的开箱即用底座。赞分享后端前端Web框架开发工具【免费下载链接】redwoodRedwoodGraphQL项目地址https://gitcode.com/gh_mirrors/re/redwood点击查看免费下载相关推荐RedwoodJS Logger 全指南基于 Pino 的 API 侧日志方案与 Prisma 慢查询监控RedwoodJS Logger 全指南基于 Pino 的 API 侧日志方案与 Prisma 慢查询监控 导读 RedwoodJS 在 redwoodjs后端前端Web框架开发工具RedwoodJS 日志系统完全指南基于 Pino 的 api 侧 Logger 配置与实战RedwoodJS 日志系统完全指南基于 Pino 的 api 侧 Logger 配置与实战 RedwoodJS 为 api 侧服务端提供了一套基于 pi后端前端Web框架开发工具RedwoodJS Logger 实战指南基于 pino 的 API 侧日志系统完整配置手册RedwoodJS Logger 实战指南基于 pino 的 API 侧日志系统完整配置手册 RedwoodJS 在框架内集成了一个基于 pino https后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表