Skip to content

WithSpan et WithScope decorators #11353

Description

@jeengbe

Problem Statement

It is a lot more convenient to attach decorators to class methods than to wrap their contents in a callback and bump up nesting by one or two.

class ThingDoer {
  doThing() {
    Sentry.withScope((scope) => {
      Sentry.startSpan(
        {
          name: 'ThingDoer.doThing',
        },
        () => {
          scope.setExtra('extra', 'extra');

          console.log('doing thing');
        },
      );
    });
  }
}

vs.

class ThingDoer {
  @Sentry.WithScope()
  @Sentry.WithSpan()
  doThing() {
    const scope = Sentry.getCurrentScope();
    scope.setExtra('extra', 'extra');

    console.log('doing thing');
  }

Also, should Sentry.startSpan() not be named Sentry.withSpan()?

Solution Brainstorm

Rudimentary span implementation:

export function WithSpan(
  spanConfig?: SentryTypes.StartSpanOptions,
): MethodDecorator {
  // @ts-expect-error -- Not sure what the error is
  return function <T extends (...args: readonly unknown[]) => Promise<unknown>>(
    target: object,
    propertyKey: string | symbol,
    descriptor: TypedPropertyDescriptor<T>,
  ) {
    const originalMethod = descriptor.value;

    // istanbul ignore next
    if (!originalMethod) return;

    const config: SentryTypes.StartSpanOptions = spanConfig ?? {
      name: target.constructor.name + '.' + propertyKey.toString(),
      op: 'method',
    };

    // @ts-expect-error -- Not sure what the error is
    descriptor.value = function (...args: unknown[]): Promise<unknown> {
      return Sentry.startSpan(config, () => originalMethod.apply(this, args));
    };
  };
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions