npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@tecace/nest-opentelemetry

v0.0.3

Published

基于官方 npm 封装 nest-opentelemetry 工具,无侵入式劫持 controller/provider,提供装饰器手动劫持非 nestjs 代码、忽略采集,自动上报 traces 和 metrics

Downloads

1

Readme

@tecace/nest-opentelemetry

description

基于官方 npm 封装 nest-opentelemetry 工具,无侵入式劫持 controller/provider,提供装饰器手动劫持非 nestjs 代码、忽略采集,自动上报 traces 和 metrics

usage

安装

npm install @tecace/nest-opentelemetry

使用


import { OTELModule } from '@tecace/nest-opentelemetry';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';

@Module({
  imports: [
    OTELModule.forRoot({
      serviceName: 'user-service',
      traceExporter: new OTLPTraceExporter({
        url: 'http://otelcol:4318/v1/traces', // 自搭建opentelemetry-collector
      }),
      metricReader: new PeriodicExportingMetricReader({
        exporter: new OTLPMetricExporter({
          url: 'http://otelcol:4317/v1/metrics', // 自搭建opentelemetry-collector
        }),
        exportIntervalMillis: 5000,
      }),
    }),
  ]
})

initOptions

初始化选项在@opentelemetry/sdk-node 初始化选项 NodeSDKConfiguration 基础上扩展了一个 injectProviderPattern,其余选项均与@opentelemetry/sdk-node 初始化选项保持一致

| 选项 | 说明 | 默认 | | ---------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------- | | injectProviderPattern | 通过正则指定哪些 provider 需要追踪,,默认以 Service 结尾的 Provider class 都会被劫持 | injectProviderPattern: [/Service$/] | | 其余选项与 NodeSDKConfiguration 保持一致 | @opentelemetry/sdk-node 初始化选项 | https://www.npmjs.com/package/@opentelemetry/sdk-node |

decorators

@CustomSpan() 独立 Span

适用于 Controller 或 Provider 类中的方法


import { OTELService, CustomSpan } from '@tecace/nest-opentelemetry';

@Controller()
export class UserController {
  constructor(
    private otelService: OTELService,
  ) {}

  @CustomSpan() // 如果需要独立span,必须使用@CustomSpan装饰
  async getUserInfoByName(data: any): SomeUserInfo {
    // 启动一个独立span
    const currentSpan = this.otelService.startSpan(
      'UserController.customGetUserInfo',
    );
    // 业务代码在放这里,业务结束后调用currentSpan.end()
    .......
    currentSpan.end();
    return someUserInfo;
  }
}

@TraceIgnore() 忽略追踪

适用于 Controller,Provider 类,或类中的方法

  • 装饰类的时候,整个类中的所有方法都不会追踪
  • 装饰方法的时候,不会追踪被装饰的方法
import { TraceIgnore } from '@tecace/nest-opentelemetry';

@TraceIgnore() // 忽略此控制器下所有方法的追踪
@Controller()
export class UserController {

  async getUserInfoByName(data: any): SomeUserInfo {
    // 业务代码
    .......
    return someUserInfo;
  }
}
import { TraceIgnore } from '@tecace/nest-opentelemetry';

@Controller()
export class UserController {
  @TraceIgnore() // 忽略getUserInfoByName方法的追踪
  async getUserInfoByName(data: any): SomeUserInfo {
    // 业务代码
    .......
    return someUserInfo;
  }
}

@CustomTrace() 用于非 nestjs 模块的追踪

非侵入式劫持 controller,provider 的原理是基于 nestjs 的依赖注入,获取到@Controller(),@Injectable()装饰的类及其对应的方法

如果我们封装了其他工具(例如基于 sequelize 封装的 dao 层),是不需要依赖注入的,那么就无法进行扫描,为了提供追踪功能,非 nestjs 模块的追踪需要实现以下条件

  • 使用@CustomTrace()装饰
  • 必须使用 class,并且实现单例模式,实现无参数的 static getInstance 方法

注意

如果一个工具(或模块)依赖于其他实例,同时其实例化要求实现一个不带参数的 static getInstance 方法,该怎么实现呢?

举一个例子,假设有一个 dao 工具,它是基于 model 实例封装的一层,其中 userDao.create 方法依赖于 userModel 实例的 create 方法。我们可以设计一个 init 方法,支持链式调用,以传入 userModel 实例并返回一个单例实例。

这种情况为什么会出现呢?在 NestJS 中,Sequelize 的 Model 可以直接在 provider 或 controller 中注入。而对于不使用 NestJS 的工具或模块,如果要基于 Model 封装一层,则需要将 Model 实例作为参数传递。

// user.dao.ts
import { CustomTrace } from '@tecace/nest-opentelemetry';

@CustomTrace() // 1.使用CustomTrace装饰
export class UserDao {
  userModelInstance!: typeof UsersModel;

  static instance: UserDao;
  // 2. 实现无参数的static getInstance方法
  static getInstance() {
    if (!this.instance) this.instance = new UserDao();
    return this.instance;
  }
  // 在工具外部就可以进行链式调用
  // const userDao = UserDao.getIntance().init(userModel)
  // userDao.create()
  init(userModelInstance: typeof UsersModel) {
    this.userModelInstance = userModelInstance;
    return UserDao.getInstance();
  }

  async create(data: any): Promise<any> {
    this.userModelInstance.create(data as Optional<any, any>);
    return await to(this.modelInstance.create(data as Optional<any, any>));
  }
}
// user.service.ts

import UserDao from './user.dao.ts';

@Injectable()
export class UserService {
  private readonly userDao!: UserDao;

  constructor(@InjectModel(UsersModel) private usersModel: typeof UsersModel) {
    this.userDao = UserDao.getInstance().init(this.usersModel); // 链式调用
  }

  async createUser(data: any): any {
    const [err, res] = await this.userDao.create(data);
    if (!err) return res;
  }
}