ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

@crawlee/otel 公共 API 全解析:为 Crawlee 爬虫接入 OpenTelemetry 可观测性

@crawlee/otel 公共 API 全解析:为 Crawlee 爬虫接入 OpenTelemetry 可观测性 crawlee/otel 公共 API 全解析为 Crawlee 爬虫接入 OpenTelemetry 可观测性【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawleecrawlee/otel是 Crawlee 官方提供的 OpenTelemetry 插桩包用于对爬虫的请求处理、日志与自定义方法进行自动或手动追踪从而把爬虫运行时产生的 traces 与 logs 接入标准的可观测性体系如 Jaeger、OpenTelemetry Collector。本文以 docs/public-api/crawlee-otel.api.md 这份 API 报告为骨架结合源码实现与官方指南带你完整掌握该包的公开接口、配置项、默认行为与底层工作原理读完即可在自己的 Crawlee 项目中落地一套可查询、可对比、可告警的分布式追踪方案。一、包定位与公开 API 总览crawlee/otel的定位非常聚焦它是 Crawlee 的 OpenTelemetry 插桩层而不是一个完整的可观测性平台。它只负责两件事——把 Crawlee 内部的关键方法调用包装成 OpenTelemetry span追踪把 Crawlee 的日志转发为 OpenTelemetry log record日志最终的采集、存储与可视化交给 OpenTelemetry SDK 和兼容后端完成。API 报告显示该包对外仅暴露 4 个符号全部为public没有隐藏的内部 API符号类型职责CrawleeInstrumentationclass自动插桩入口继承自opentelemetry/instrumentation的InstrumentationBaseCrawleeInstrumentationConfiginterface插桩配置开关自动请求处理插桩、日志转发、自定义方法插桩ClassMethodToInstrumentinterface描述要插桩的某个模块上某个类的方法的结构wrapWithSpanfunction手动包装任意函数为带 span 的版本用于请求处理器、钩子、错误处理器对应源码入口在 packages/otel/src/index.ts仅三行导出印证了 API 面的精简export { CrawleeInstrumentation } from ./instrumentation.js; export type * from ./types.js; export { wrapWithSpan } from ./wrapWithSpan.js;需要特别说明的是这份.api.md文件是由 API Extractor 自动生成的 API 报告它本身不包含配置示例因此本文的实操细节同时参考了官方指南 docs/guides/trace-and-monitor-crawlers.mdx 以及包内源码。运行环境方面包的 package.json 声明node: 22.0.0且为 ESM 模块type: module使用前请确认 Node 版本。二、CrawleeInstrumentation自动插桩的入口CrawleeInstrumentation继承自opentelemetry/instrumentation的InstrumentationBase构造函数签名是constructor(config?: CrawleeInstrumentationConfig)它把配置合并后传给父类同时在内部维护自己的默认值。源码 packages/otel/src/instrumentation.ts 中的构造函数注释揭示了一个重要的实现细节配置项逐个用??解析而不是用展开运算符合并——因为{ logInstrumentation: options.logs }这种写法在options.logs未设置时会产出显式的undefined展开会把它当成用户明确关闭从而悄悄禁用该功能用??解析则能保证undefined回落到默认值。2.1 配置项与默认值CrawleeInstrumentationConfig在 packages/otel/src/types.ts 中定义默认值在 packages/otel/src/constants.ts 的baseConfig中export const baseConfig: CrawleeInstrumentationConfig { enabled: true, requestHandlingInstrumentation: true, logInstrumentation: true, customInstrumentation: [], } as const;四个配置项的含义如下配置项类型默认值作用enabledbooleantrue是否启用整个插桩继承自InstrumentationConfigrequestHandlingInstrumentationbooleantrue是否自动插桩爬虫核心请求处理方法logInstrumentationbooleantrue是否把 Crawlee 日志转发为 OpenTelemetry 日志记录customInstrumentationClassMethodToInstrument[][]自定义插桩的方法列表2.2 init()插桩是如何生效的InstrumentationBase要求子类实现init()方法返回InstrumentationModuleDefinition[]。CrawleeInstrumentation.init()的逻辑instrumentation.ts分两步方法级插桩把customInstrumentation中的方法与内置的requestHandlingInstrumentationMethods当requestHandlingInstrumentation开启时合并交给buildModuleDefinitions按模块分组、去重然后逐模块创建InstrumentationNodeModuleDefinition。这里有一个关键设计customInstrumentation排在前面因此自定义条目可以覆盖内置的同名方法插桩第一个定义胜出。日志转发插桩当logInstrumentation开启时额外补一个针对crawlee/core的模块定义把BaseCrawleeLogger.prototype上的日志方法逐个_wrap包装。值得注意的版本匹配逻辑模块定义使用SUPPORTED_CRAWLEE_VERSIONS [4.0.0-0 5.0.0-0]并且设置了definition.includePrerelease true。源码注释解释了原因——Crawlee v4 的发布版本都是预发布4.0.0-beta.x、4.0.0-rc.xcaret 范围^4.0.0匹配不到预发布版本所以显式写出下限4.0.0-0并把includePrerelease打开。插桩采用找不到就跳过并告警、绝不抛错的策略getPrototype在类或方法不存在时用this._diag.warn输出告警并返回undefinedunwrapIfWrapped也只还原确实被包装过的方法instrumentation.ts保证被插桩模块的加载不会因为内部结构变化而崩溃。2.3 setTracerProvider()与手动插桩共享 tracerCrawleeInstrumentation重写了setTracerProvider在调用父类实现后把自己的this.tracer通过setSharedTracer共享给模块级的wrapWithSpan。源码注释instrumentation.ts说明了为什么必须在setTracerProvider里做而不是构造函数里做构造函数只能拿到全局 API 的 tracer而通过registerInstrumentations传入的tracerProvider并不会注册到全局如果依赖树里存在两份opentelemetry/api各自的全局也不互通。拿到错误 provider 的 tracer 会让所有 span 静默消失所以必须等 provider 确定后再交接。这个设计的直接收益是你用wrapWithSpan手动包裹的处理器会自动和自动插桩生成的 span 处于同一个插桩作用域instrumentation scope内在追踪后端里它们会出现在同一棵 trace 树中。三、ClassMethodToInstrument描述一个待插桩方法ClassMethodToInstrument是配置customInstrumentation时使用的核心结构packages/otel/src/types.ts字段类型说明moduleNamestring类所在的 Crawlee 包例如crawlee/basicclassNamestring要插桩的类名methodNamestring要插桩的方法名spanNamestring \| ((this, ...args) string)span 名称默认值为className.methodName。传函数时函数会在被插桩方法调用时以该方法参数调用this指向方法所属的实例spanOptionsSpanOptions \| ((this, ...args) SpanOptions)span 的属性等选项调用约定与spanName相同spanName和spanOptions支持函数形式这让你可以根据运行时的参数动态命名 span、动态附加属性。官方指南的自定义示例docs/guides/trace_and_monitor_custom.ts展示了完整用法import { CrawleeInstrumentation } from crawlee/otel; import { ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from opentelemetry/semantic-conventions; const crawleeInstrumentation new CrawleeInstrumentation({ requestHandlingInstrumentation: false, logInstrumentation: false, customInstrumentation: [ { moduleName: crawlee/basic, className: BasicCrawler, methodName: run, spanName: crawler.run, spanOptions() { return { attributes: { crawler.type: this.constructor.name, }, }; }, }, { moduleName: crawlee/basic, className: BasicCrawler, methodName: runRequestHandler, // 动态 span 名称使用上下文参数 spanName(context: any) { return request ${context.request.url}; }, spanOptions(context: any) { return { attributes: { [ATTR_URL_FULL]: context.request.url, [ATTR_HTTP_REQUEST_METHOD]: context.request.method, }, }; }, }, ], });实现层面的两个细节值得了解守卫式回调applyClassMethodPatch通过resolveSpanName/resolveSpanOptions调用这些回调回调抛错时只记录diag.warn并回落默认值绝不让用户回调的 bug 影响被插桩方法的正常执行wrapWithSpan.ts。语义约定合并自动插桩生成的每个 span 都会注入code.function.name属性值为ClassName.methodName你的自定义属性会被合并进 span 属性而code.function.name始终保留instrumentation.ts。另外buildModuleDefinitionsutilities.ts会做两项校验moduleName必须以crawlee/开头否则告警跳过同名同方法的重复条目只保留第一个。这意味着内置已插桩的方法也能被你的customInstrumentation覆盖。四、wrapWithSpan手动插桩的瑞士军刀当自动插桩满足不了需求时例如想给某个 request handler、hook 或自定义工具函数加 span直接使用wrapWithSpan手动包装。它的签名packages/otel/src/wrapWithSpan.tsexport function wrapWithSpanArgs extends unknown[], Return( fn: (...args: Args) Return, options?: { spanName?: string | ((...args: Args) string); spanOptions?: SpanOptions | ((...args: Args) SpanOptions); tracer?: Tracer; }, ): (...args: Args) Return4.1 选项详解选项类型说明spanNamestring \| ((...args) string)静态名称或接收处理器参数并返回名称的函数默认回退到函数名或anonymousspanOptionsSpanOptions \| ((...args) SpanOptions)静态选项或函数可携带 attributes、kind 等tracerTracer自定义 tracer默认使用已注册CrawleeInstrumentation交接的 tracer未注册时回退到全局 provider 的 tracer没有 provider 时则是 no-op tracer包装永不失败4.2 同步与异步的语义保持wrapWithSpan最重要的设计是不改变被包装函数的异步语义同步函数返回同步结果span 在函数返回后立即结束异步函数返回 PromiseLike保持返回 Promisespan 一直保持开启直到 Promise settle包装器会把自身的this转发给原函数所以方法被包装后this仍然有效箭头函数天然忽略this不受影响。错误处理遵循 OpenTelemetry 规范同步抛错与 Promise reject 都会调用recordError——span.recordException(err)记录异常并把 span 状态置为ERROR携带错误消息成功路径不设置状态保持UNSET因为规范要求只在失败时设置状态这样下游消费者可以自行覆盖wrapWithSpan.ts。4.3 在爬虫中的典型用法在请求处理器中包装并访问当前 span来自官方指南 docs/guides/trace-and-monitor-crawlers.mdximport { context, trace } from opentelemetry/api; import { wrapWithSpan } from crawlee/otel; requestHandler: wrapWithSpan( async ({ request, $ }) { const span trace.getSpan(context.active()); const title $(title).text(); if (span) { span.setAttribute(page.title, title); span.addEvent(page_scraped, { url: request.url }); } // ... 其余处理逻辑 }, { spanName: request-handler } ),关于 TypeScript 泛型有一个实用提示Args和Return是分开的两个泛型让参数类型能流入spanName/spanOptions回调。但当你把结果赋给联合类型选项如同时接受 router 与普通 handler 的requestHandler时TypeScript 无法跨函数类型联合推断参数类型此时需要显式标注参数类型requestHandler: wrapWithSpan(async ({ request }: CheerioCrawlingContext) { ... })五、自动插桩清单哪些方法、什么 span当requestHandlingInstrumentation开启默认时constants.ts 中定义了如下内置插桩爬虫类方法span 名称span kindBasicCrawlerruncrawlee.crawler.runinternalBasicCrawlerhandleRequestcrawlee.crawler.handleRequestinternalBasicCrawlerrunRequestHandlercrawlee.crawler.runRequestHandlerinternalBasicCrawlerrequestFunctionErrorHandlercrawlee.crawler.requestFunctionErrorHandlerinternalBasicCrawlerhandleFailedRequestHandlercrawlee.crawler.handleFailedRequestHandlerinternalHttpCrawlermakeHttpRequestcrawlee.http.makeHttpRequestclientBrowserCrawlernavigatecrawlee.browser.navigateclientAdaptivePlaywrightCrawlerrunRequestHandlercrawlee.crawler.runRequestHandlerinternal理解这张表要注意三点继承覆盖所有爬虫都继承自BasicCrawler所以表中BasicCrawler的方法对所有爬虫都生效。为何单独列AdaptivePlaywrightCrawler它整体替换了runRequestHandler而不调用superBasicCrawler的补丁对它不生效而BrowserCrawler会调用super所以不需要自己的条目。运行AdaptivePlaywrightCrawler时每个请求仍然产生一个crawlee.crawler.runRequestHandlerspan源码注释见 constants.ts。client span 语义crawlee.http.makeHttpRequest和crawlee.browser.navigate是离开当前进程的出站调用所以被标记为SpanKind.CLIENT其余是默认的 internal span。5.1 自动 span 的属性所有自动 span都携带code.function.name值为ClassName.methodName用wrapWithSpan自己创建的 span 只有你显式给的属性。crawlee.crawler.run额外携带crawlee.crawler.type值为运行中爬虫的类名this.constructor.name。所有接收 crawling context 的方法请求处理器、导航、错误处理器还附带 4 个请求属性属性来源url.full请求 URLhttp.request.method请求方法crawlee.request.idCrawlee 请求 IDcrawlee.request.retry_count请求已重试次数其中url.full与http.request.method是稳定的 OpenTelemetry 语义约定semantic conventions保证你的 trace 与栈中其他已插桩 HTTP 客户端可比没有语义约定的 Crawlee 专属数据统一使用crawlee.前缀实现在 constants.ts 的requestAttributes。源码对请求对象的读取刻意保持结构化见 internal-types.ts 的RequestLike/CrawlingContextLike因为被插桩的 Crawlee 版本是运行时解析的可能与本包编译时不同所以所有字段都按可选处理。六、日志转发Crawlee 日志 → OpenTelemetry LogslogInstrumentation开启默认时BaseCrawleeLogger.prototype上的日志方法会被包装转发为 OpenTelemetry log recordinstrumentation.ts。被插桩的日志方法及级别映射方法级别映射的 OpenTelemetry 严重级别error1ERRORexception1ERRORsoftFail2WARNwarning3WARNinfo4INFOdebug5DEBUGperf6DEBUG映射表见 constants.tsSOFT_FAIL与WARNING都映射为WARNPERF映射为DEBUGseverityText输出可读级别名如SOFT_FAIL、PERF。转发细节结构化data变为属性日志调用的第二个参数结构化数据对象被展开为 log record 的 attributes其中的Error对象会映射为exception.type、exception.message、exception.stacktrace三个语义约定属性因为 Error 自身的属性不可枚举直接展开会被丢弃见 utilities.ts。与日志实现无关插桩打在BaseCrawleeLogger.prototype上所有派生 logger默认 Apify logger、Winston、Pino 或手写适配器都会被转发warningOnce/deprecated通过warning覆盖。不过滤级别Crawlee 把级别过滤留给底层日志库因此所有消息都会被转发过滤应在你的 OpenTelemetry pipeline 中做。record body 是原始消息不是 logger 打印的行perf记录没有[PERF]前缀exception记录把消息放在 body、错误放在exception.*属性中。匹配日志时要按属性而不是按打印行 grep。应用日志先执行包装器先调用原始日志方法original.apply在finally中再转发因此应用自身的日志输出不受影响也绝不会因为转发失败而影响handleFailedRequestHandler这类在 catch 块中打日志的路径。6.1 重要提醒Jaeger 不接收 OTLP 日志日志记录只有在你给 SDK 添加了 log record processor、且后端实现了 OTLP logs 服务时才会被发送。Jaeger 是追踪后端不实现 OTLP logs——把日志 exporter 指向jaegertracing/all-in-one容器会导致每个日志批次报错UNIMPLEMENTED: unknown service opentelemetry.proto.collector.logs.v1.LogsService。日志应发送给 OpenTelemetry Collector 或支持日志的后端。如需导出日志安装opentelemetry/sdk-logs与日志 exporter并在 setup 文件中添加 processorimport { OTLPLogExporter } from opentelemetry/exporter-logs-otlp-grpc; import { BatchLogRecordProcessor } from opentelemetry/sdk-logs; export const sdk new NodeSDK({ // ... 上述 trace 配置 logRecordProcessors: [ new BatchLogRecordProcessor(new OTLPLogExporter({ url: http://localhost:4317 })), ], });如果想彻底把 Crawlee 日志排除在 OpenTelemetry 之外设置logInstrumentation: false即可。七、端到端接入流程从 Jaeger 到第一个 trace7.1 启动 Jaeger创建docker-compose.yml启动预配置的 Jaeger 容器services: jaeger: image: jaegertracing/all-in-one:1.53 container_name: jaeger ports: # Jaeger UI - 16686:16686 # OTLP gRPC - 4317:4317 # OTLP HTTP - 4318:4318 environment: - COLLECTOR_OTLP_ENABLEDtrue restart: unless-stopped启动docker compose up -d然后通过浏览器访问http://localhost:16686打开 Jaeger UI。7.2 安装依赖npm install crawlee/otel opentelemetry/api opentelemetry/api-logs opentelemetry/sdk-node opentelemetry/sdk-trace-base opentelemetry/resources opentelemetry/semantic-conventions opentelemetry/exporter-trace-otlp-grpc7.3 三个文件的职责划分OpenTelemetry 插桩必须在导入 Crawlee 之前完成官方给出的标准做法是拆成三个文件、按序预加载。① 模块钩子docs/guides/trace_and_monitor_register_hook.tsCrawlee 以 ESM 发布自动插桩必须通过 Node 的模块钩子才能在类被导入时打补丁因此要预先安装钩子import { register } from node:module; import { pathToFileURL } from node:url; register(opentelemetry/instrumentation/hook.mjs, pathToFileURL(./));② SDK 初始化文件docs/guides/trace_and_monitor_setup.ts创建资源、配置 OTLP exporter、注册CrawleeInstrumentation并负责进程退出前冲刷缓冲的遥测数据。其中 gRPC exporter 的 URL 不带信号路径http://localhost:4317而 HTTP exporter 需要用http://localhost:4318/v1/traces。冲刷逻辑要幂等shuttingDown ?? sdk.shutdown()beforeExit用on而非once因为冲刷启动的异步工作会让 Node 再次触发beforeExit同时单独处理SIGINT/SIGTERM它们不触发beforeExit。③ 爬虫主文件正常创建 Crawlee 爬虫即可CrawleeInstrumentation会自动插桩核心方法。7.4 运行npx tsx --import ./src/register-hook.ts --import ./src/setup.ts ./src/main.ts--import标志按顺序在任何业务代码之前执行先装钩子再启动 OpenTelemetry SDK最后才加载并插桩爬虫。在仓库根目录直接运行官方示例则是pnpm exec tsx --import ./docs/guides/trace_and_monitor_register_hook.ts \ --import ./docs/guides/trace_and_monitor_setup.ts \ ./docs/guides/trace_and_monitor_basic.ts7.5 结果分析运行后即可在 Jaeger UI 中按服务名搜索 trace、对比 trace、查看每个 span 的详细属性与耗时。若插桩未生效优先确认模块钩子是否最先被预加载并参考 OpenTelemetry 官方的 ESM 支持文档排查环境差异。八、测试与质量保障仓库为该包配备了完整的测试套件packages/otel/test/覆盖了本文提到的核心行为instrumentation.test.ts验证CrawleeInstrumentation的配置解析与注册流程patching.test.ts验证对 Crawlee 类方法的实际打补丁/还原行为以及isWrapped检查wrap-with-span.test.ts验证同步/异步语义保持、错误记录、span 生命周期constants.test.ts与utilities.test.ts验证日志级别映射与属性构建。集成层面test/otel/ 目录下的测试进一步验证了 hook 投递、日志转发、被插桩方法与 tracer 接线等跨包行为。这些测试是理解crawlee/otel边界行为如缺失方法不抛错只告警重复插桩保留第一个定义的最佳参考。九、小结crawlee/otel用极精简的 API 面一个类、两个接口、一个函数完成了 Crawlee 与 OpenTelemetry 生态的对接CrawleeInstrumentation负责零侵入的自动插桩请求处理 span 日志转发ClassMethodToInstrument让你能精准定制要插桩的方法与 span 名称/属性wrapWithSpan则在需要细粒度控制时为任意函数提供手动包装。从 docs/public-api/crawlee-otel.api.md 的 API 契约到 packages/otel/src/instrumentation.ts 的实现细节再到 docs/guides/trace-and-monitor-crawlers.mdx 的完整接入示例三层资料共同构成了一个可直接落地的爬虫可观测性方案——它不会为你的业务代码增加侵入性却能让每一次请求处理、每一条日志都进入标准的追踪与日志体系。【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawlee创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表