Skip to content

Comments

Draft PR: Effect Integration for Sentry Node SDK#17432

Open
Divyanshu7001 wants to merge 1 commit intogetsentry:developfrom
Divyanshu7001:feat/effect-integration
Open

Draft PR: Effect Integration for Sentry Node SDK#17432
Divyanshu7001 wants to merge 1 commit intogetsentry:developfrom
Divyanshu7001:feat/effect-integration

Conversation

@Divyanshu7001
Copy link

@Divyanshu7001 Divyanshu7001 commented Aug 19, 2025

1. Goal

This PR adds support for tracing Effect computations in the Sentry Node SDK. The goal is to automatically create Sentry spans for Effect operations and capture errors that occur within Effect computations as Sentry exceptions, providing better observability for users of the Effect library.

2. Pathway Taken

  • Added a new integration (effectIntegration) and instrumentation (instrumentEffect) under @sentry/node.
  • Followed the robust test/mocking patterns from other integrations to ensure isolated, reliable tests.
  • Updated the SDK’s auto-integration and preload logic to include Effect.
  • Added the effect package as a dependency and provided type definitions for Effect’s tracer API.
  • Wrote comprehensive unit tests for the integration, covering all tracer hooks and error scenarios.

3. How the integration has happened and where it is included now

  • The integration is implemented in packages/node/src/integrations/tracing/effect/.
  • It is exported from the main SDK entrypoint and included in the auto-performance integrations list.
  • The SDK will now automatically register the Effect tracer if the Effect library is present in the user’s project.

4. How users should integrate Effect into their own project

Basic usage:

const Sentry = require('@sentry/node');

Sentry.init({
  dsn: 'YOUR_DSN',
  integrations: [
    Sentry.effectIntegration(),
    // ...other integrations
  ],
  // ...other options
});
  • Just add Sentry.effectIntegration() to your integrations array.
  • The SDK will automatically patch Effect’s tracer and start capturing spans and errors.
  • No manual instrumentation is required for most use cases.

5. Where am i stuck / need help

  • Some test cases are still flaky or failing due to mocking or module loading order (see attached images).
Screenshot 2025-08-19 054036 Screenshot 2025-08-19 054110 Screenshot 2025-08-19 054126 Screenshot 2025-08-19 054055

6. What is my thinking about these errors?

I think these errors are due to me not being able to setup the test environment properly as in most errors..the problem is just Span processes being undefined.
I would appreciate some pointers of where i am going wrong and how should i setup the test environment properly to test.

closes #17126

@Divyanshu7001 Divyanshu7001 requested a review from a team as a code owner August 19, 2025 00:17
@marbemac
Copy link

marbemac commented Sep 8, 2025

Just a quick note - @Divyanshu7001 the auto formatting on your machine resulted in lots of unrelated files being changed that make it more difficult to digest this PR.

@Divyanshu7001
Copy link
Author

Just a quick note - @Divyanshu7001 the auto formatting on your machine resulted in lots of unrelated files being changed that make it more difficult to digest this PR.

I was afraid of the same thing & thats why used github codespaces with no local settings
but yes..i did run yarn fix to do all the linting fixes on the package level , which has caused this issue.😅
@marbemac would you prefer another PR without the linting fixes?
i can do that if that will suit things better.

all open for it.

@s1gr1d
Copy link
Member

s1gr1d commented Sep 16, 2025

Thanks for creating this PR!
Can you please run fix:prettier from the root package.json? And also update the PR (merging or rebasing).

And to make sure we don't forget to review the PR you can assign e.g. me as a reviewer when you are ready.

Copy link

@marbemac marbemac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Divyanshu7001, after actually looking at it, I don't understand how this is supposed to work. Most of the interfaces and methods used are not present in effect.

I created a custom effect tracer for Sentry over here, if you are interested -> https://github.com/marbemac/cloudflare-sentry-effect-tracing/blob/main/worker/effect-sentry-tracer.ts.

Comment on lines +21 to +22
onSpanStart?: (span: EffectSpan) => void;
onSpanEnd?: (span: EffectSpan, exit: EffectExit) => void;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can you find this in effect?

export interface EffectTracer {
onSpanStart?: (span: EffectSpan) => void;
onSpanEnd?: (span: EffectSpan, exit: EffectExit) => void;
span<A>(name: string, f: () => A): A;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The span method on custom tracers takes 6 args, not 2.

onSpanStart?: (span: EffectSpan) => void;
onSpanEnd?: (span: EffectSpan, exit: EffectExit) => void;
span<A>(name: string, f: () => A): A;
withSpan<A>(span: EffectSpan, f: () => A): A;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can you find this in the effect tracer interface?

Comment on lines +114 to +122
if (typeof Effect.Tracer.set === 'function') {
Effect.Tracer.set(sentryTracer);
} else if (typeof Effect.Tracer.register === 'function') {
Effect.Tracer.register(sentryTracer);
} else if (typeof Effect.Tracer.use === 'function') {
Effect.Tracer.use(sentryTracer);
} else {
return;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you find these methods on the effect tracer? I do not think they exist.

@billyvg billyvg removed the request for review from a team February 2, 2026 22:15
Co-Authored-By: Jan Peer Stöcklmair <jan.peer@sentry.io>
@JPeer264 JPeer264 force-pushed the feat/effect-integration branch from 66549ef to 194e173 Compare February 5, 2026 10:14
name: span.name,
op: 'effect.span',
origin: 'auto.effect',
startTime: Number(span.startTime) / 1000000, // Convert nanoseconds to milliseconds
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The Effect integration incorrectly converts span timestamps from nanoseconds to milliseconds instead of seconds, causing all reported span timings to be off by a factor of 1000.
Severity: HIGH

Suggested Fix

Update the timestamp conversion logic to divide the nanosecond value by 1_000_000_000 to correctly convert it to seconds, which is the unit expected by the Sentry startSpan function. The line should be changed to startTime: Number(span.startTime) / 1_000_000_000.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/node/src/integrations/tracing/effect/effectIntegration.ts#L85

Potential issue: The Effect integration converts span start times from nanoseconds to
milliseconds by dividing the `BigInt` timestamp by `1,000,000`. However, the Sentry
`startSpan` function expects a numeric timestamp to be in seconds. As a result, a
timestamp representing 1 second (`1000` milliseconds) will be interpreted by Sentry as
1000 seconds. This will cause all span start times and durations to be reported
incorrectly by a factor of 1000, making the tracing data unusable.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 6 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

startTime: Number(span.startTime),
endTime: span.endTime ? Number(span.endTime) : undefined,
});
captureException(exit.cause);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing mechanism parameter in captureException calls

Medium Severity

The captureException calls at lines 50, 72, and 105 are missing the mechanism parameter with handled and type properties. Other integrations like connect.ts, express.ts, and fastify/index.ts properly include { mechanism: { handled: false, type: 'auto.middleware.connect' } }. The mechanism should use a type like 'auto.effect' to align with the span origin.

Additional Locations (2)

Fix in Cursor Fix in Web

onSpanStart?: (span: EffectSpan) => void;
onSpanEnd?: (span: EffectSpan, exit: EffectExit) => void;
span<A>(name: string, f: () => A): A;
withSpan<A>(span: EffectSpan, f: () => A): A;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect tracer interface method signatures are incorrect

High Severity

The EffectTracer type definitions don't match Effect's actual tracer API. PR reviewer @marbemac noted that the span method takes 6 arguments, not 2, and the withSpan method doesn't exist in Effect's tracer interface. The implementation in effectIntegration.ts uses these incorrect signatures, meaning the integration won't work correctly with the actual Effect library.

Additional Locations (1)

Fix in Cursor Fix in Web


// Set status based on span status
if (span.status && span.status.code !== 0) {
sentrySpan.setStatus('internal_error');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setStatus called with incorrect argument type

High Severity

The setStatus calls pass a plain string 'internal_error' but Span.setStatus() expects a SpanStatus object with code (number: 0, 1, or 2) and optional message properties. Other integrations in the codebase correctly use span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }). This will cause the span status to not be set correctly.

Additional Locations (1)

Fix in Cursor Fix in Web

if (span.status && span.status.code !== 0) {
sentrySpan.setStatus('internal_error');
if (span.status.message) {
sentrySpan.setData('effect.status_message', span.status.message);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setData method does not exist on Span

High Severity

The call to sentrySpan.setData('effect.status_message', span.status.message) uses a method that doesn't exist on the Span interface. The correct method is setAttribute. This will cause a runtime error when this code path is executed.

Fix in Cursor Fix in Web

op: 'effect.span',
origin: 'auto.effect',
startTime: Number(span.startTime) / 1000000, // Convert nanoseconds to milliseconds
data: span.attributes,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Span attributes silently ignored due to wrong property name

Medium Severity

The startSpan call passes data: span.attributes but StartSpanOptions expects attributes, not data. This means the Effect span's attributes will be silently dropped and not transferred to the Sentry span, resulting in loss of trace context.

Fix in Cursor Fix in Web

{
name,
op: 'effect.span',
origin: 'auto.effect',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Span origin ignored because passed as wrong property

Medium Severity

The origin: 'auto.effect' property is passed directly on the startSpan options object, but StartSpanOptions doesn't have an origin property. The origin must be set via attributes using SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN. All Effect spans will incorrectly have origin 'manual' instead of 'auto.effect'.

Additional Locations (1)

Fix in Cursor Fix in Web

@JPeer264
Copy link
Member

JPeer264 commented Feb 5, 2026

Alright a little update here. I was now rebasing the PR so it is easier to review.

I had a quick look at it and given that we need to install "Effect" as a dependency gives me the signal that we might need to add our own sdk for it: @sentry/effect, which means that it is possible that this PR can't be merged as is (even though the tests would pass).

I'll clarify this or, latest, next week how we go forward with this PR.

@JPeer264 JPeer264 self-assigned this Feb 5, 2026
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

Successfully merging this pull request may close these issues.

Add effectIntegration to Node SDK

4 participants