Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Sentry Middleware doesn't split steps into their own spans #662

Open
TaylorFacen opened this issue Jul 23, 2024 · 1 comment
Open

Comments

@TaylorFacen
Copy link

Describe the bug
I copied the code used in the @innest/sentry-middleware package to my codebase to make some modifications. When a function runs, I only see a span for the entire Inngest Function Run span and not spans for each step within a function.

To Reproduce
Steps to reproduce the behavior:

Use the following code:
middleware/sentry-middleware.ts

import { InngestMiddleware } from "inngest";
import * as Sentry from "@sentry/nextjs";

export const sentryMiddleware = new InngestMiddleware({
  name: 'Sentry Middleware',
  init({ client }) {
    return {
      onFunctionRun({ ctx, fn, steps }) {
        const functionId = fn.id(client.id);
        const runId = ctx.runId;
        return Sentry.withScope(scope => {
          const sharedTags: Record<string, string> = {
            "inngest.client.id": client.id,
            "inngest.function.id": functionId,
            "inngest.function.name": fn.name,
            "inngest.event.name": ctx.event.name,
            "inngest.run.id": runId,
            "inngest.run.url": `https://app.inngest.com/env/production/functions/${functionId}/logs/${runId}`
          };

          scope.setTags(sharedTags);

          if (ctx.event.user) { scope.setUser({ id: ctx.event.user.id }) }

          let memoSpan: Sentry.Span;
          let execSpan: Sentry.Span;

          return Sentry.startSpanManual(
            {
              name: "Inngest Function Run",
              op: "run",
              attributes: {
                ...sharedTags,
                "inngest.event": JSON.stringify(ctx.event),
              },
              scope,
            },
            reqSpan => {
              return {
                transformInput() {
                  return {
                    ctx: {
                      runId,
                      functionId,
                      sentry: Sentry,
                    },
                  };
                },
                beforeMemoization() {
                  Sentry.withActiveSpan(reqSpan, (scope) => {
                    Sentry.startSpanManual(
                      {
                        name: "Memoization",
                        op: "memoization",
                        attributes: {
                          ...sharedTags,
                          "inngest.memoization.count": steps.length,
                        },
                        scope,
                      },
                      (_memoSpan) => {
                        memoSpan = _memoSpan;
                      }
                    );
                  });
                },
                afterMemoization() {
                  memoSpan?.end();
                },
                beforeExecution() {
                  Sentry.withActiveSpan(reqSpan, (scope) => {
                    Sentry.startSpanManual(
                      {
                        name: "Execution",
                        op: "execution",
                        attributes: {
                          ...sharedTags,
                        },
                        scope,
                      },
                      (_execSpan) => {
                        execSpan = _execSpan;
                      }
                    );
                  });
                },
                afterExecution() {
                  execSpan?.end();
                },
                transformOutput({ result, step }) {
                  // Set step metadata
                  if (step) {
                    console.log({ step })
                    Sentry.withActiveSpan(reqSpan, (scope) => {
                      sharedTags["inngest.step.name"] =
                        step.displayName ?? "";
                      if ('id' in step) { sharedTags["inngest.step.id"] = step.id as string }
                      sharedTags["inngest.step.op"] = step.op;

                      scope.setTags(sharedTags);
                    });
                  }

                  // Capture step output and log errors
                  if (result.error) {
                    reqSpan.setStatus({
                      code: 2,
                    });

                    Sentry.withActiveSpan(reqSpan, (scope) => {
                      scope.setTags(sharedTags);
                      scope.captureException(result.error);
                    });
                  } else {
                    reqSpan.setStatus({
                      code: 1,
                    });
                  }
                },
                async beforeResponse() {
                  reqSpan.end();
                  await Sentry.flush();
                },
              }
            }
          )
        })
      }
    }
  }
});

and run a function with multiple steps.
user-updated.ts

import { inngest } from "@/lib/inngest/clients";
import { updateCustomer } from '@/lib/stripe-node';
import { upsertPlainCustomerFromUser } from "@/lib/plain";
import { slugify } from "inngest";
import { db } from "@/db";

const name = "User Updated";
export const updated = inngest.createFunction(
  { name, id: slugify(name) },
  { event: 'app/user.updated' },
  async ({ event, step }) => {
    const { data: { data }, user: { id: userId } } = event;
    const updatedFields = data.map(({ field }) => field)

    const user = await step.run({ id: 'fetch-user' }, async () =>
      db.user.findFirstOrThrow({ where: { id: userId } })
    )

    if (updatedFields.includes('name')) {
      await step.run({ id: 'update-stripe-customer' }, async () => {
        // throw new Error('Example Inngest error')
        return updateStripeCustomer({
          customerId: user.stripeCustomerId,
          properties: { name: user.name || "" }
        })
      }

      );

      await step.run({ id: 'update-plain-customer' }, async () =>
        upsertPlainCustomerFromUser({ user, customerId: user.plainCustomerId || undefined })
      )
    }
  }
)

Expected behavior
Each step has an execution span

Code snippets / Logs / Screenshots

CleanShot 2024-07-23 at 16 32 14@2x

System info (please complete the following information):

  • OS: [e.g. Mac, Windows]: Mac
  • npm package Version [e.g. 0.7.0]: 3.19.7
  • Framework [e.g. Next.js, Express]: Nextjs
  • Platform [e.g. Vercel, AWS Lambda]: Problem is happening locally
Copy link

linear bot commented Jul 23, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant