Skip to content

WithSpan et WithScope decorators #11353

@jeengbe

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));
    };
  };
}

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions