-
-
Notifications
You must be signed in to change notification settings - Fork 7
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
rfc(feature): SDK Lifecycle Hooks #34
Conversation
159635f
to
a6b7b99
Compare
I simplified and re-wrote this proposal based on the experience we had adding and using hooks within the JavaScript SDK. It is now ready for another set of reviews (only took a billion months lol) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shippppit
should we think of common hooks across SDKs or is this completely left to SDK authors?
We will define a set of initial ones in the develop docs, but the rest can be left to SDK authors to solve their platform specific problems. Since for now hooks are meant to be used internally I don't think we worry about SDK user concerns yet, but maybe we can re-evaluate this in 2-3 months after more SDKs adopt it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great example of finding a very concise and simple solution to a problem that might be super platform-specific, so being able to extend this on an SDK by SDK basis is 👌
|
||
type HookCallback = (...args: unknown[]): void; | ||
|
||
class Client { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a bit confused, who implements this Client
? What exactly the end-user interacts with?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for showing an interest in our RFC process! 🚀
This is meant to be the client implementation we have in each SDK, see https://develop.sentry.dev/sdk/unified-api/#client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to think a layer higher and across multiple languages and clients. Sorry for the confusion, and thank you so much for your response!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cleptric, I also need some clarification. Does this mean I need to implement a client when I want to add lifecycle hooks? Can you share the code a user must write when implementing a lifecycle hook?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The on
and emit
hooks will be defined on the client interface (much like flush
and capture_event
are at the moment).
Usually users (if writing custom clients) are inheriting from a base client implementation anyway, so they should get the hooks functionality out of the box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification, @AbhiPrasad 😃 .
On mobile, hardly anybody implements a custom client. Most users interact with the static API. Implementing a custom client and binding this to the hub, then binding the hub to the static API, will not be the cleanest API on Mobile. Our users usually only call SentrySDK.start
with the options, and that's it.
`beforeEnvelope`: | ||
|
||
```ts | ||
on('beforeEnvelope', callback: (envelope: Envelope) => void) => void; | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a hook for beforeEnvelope
? Can we make it internal at least? AFAIK this is not exposed for clients and the whole envelope impl. is marked as internal in Mobile SDKs at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we can make some hooks internal. I will clarify this in the RFC.
|
||
SDKs are expected to have a common set of hooks, but can also have SDK specific hooks. | ||
|
||
## Hooks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's about hooks for startSession
and endSession
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was not an exhaustive list, but I will add those.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great RFC and big +1 on the "implementing it as needed per SDK" approach!
text/0034-sdk-lifecycle-hooks.md
Outdated
|
||
Hooks are meant to be mostly internal APIs for integration authors, but we can also expose them to SDK users if there is a use case for it. | ||
|
||
As hook callbacks are not processed by the client, they can be async functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if we should clarify the mutability of the data (events, hints, breadcrumbs, etc) passed to the callbacks. If we allow callbacks to be async, we need to be aware that mutations made to these objects might come in too late, especially for hooks around processing and sending events.
Then again, I feel like we shouldn't prohibit mutation, as outlined by the use cases listed in this RFC (also this practical example).
When we write the dev spec for hooks, we should add that implementers need to think about mutability and async characteristics of the hook(s) they're implementing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I will add notes around this.
I would like to explicitly document the requirements for callbacks with regards to forward compatibility. In particular in Python for instance we should expect them to always catch extra keyword arguments so that additional data can be passed in the future. |
@mitsuhiko, your concern is why I pointed out that having a single argument object is much better, so you avoid breaking changes. You can extend API without requiring different function definitions (to some extent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this @AbhiPrasad 👏 .
|
||
# Proposal | ||
|
||
SDK hooks live on the client, and are **stateless**. They are called in the order they are registered. SDKs can opt-in to whatever hooks they use, and there can be hooks unique to an SDK. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AbhiPrasad, why did we put the hooks on the client and not the options as our existing hooks beforeSend
, beforeBreadcrumb
, etc.?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not meant to be replacements for the top level hooks we have for beforeSend
or beforeBreadcrumb
for the time being, since the hook names are dynamic strings.
The hooks are on the client so they can be registered dynamically. This is basically the event emitter pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to put this info in the RFC or the develop docs, as this was unclear to me when reading the RFC.
I didn't write JavaScript for 6 years or so, but event emitter still rings a bell now. For statically typed languages, we might have to adapt them to the language. For Swift and Java, it would maybe be something like addObserver
and notifyObserver
, or maybe we just stick to the JavaScript syntax. Thanks for explaining.
|
||
To document and approve new hooks, we will create a new page in the develop docs that lists all the hooks, and what they are used for. | ||
|
||
`startTransaction`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should also add our existing hooks as beforeSend
, beforeTransaction
, beforeBreadcrumb
, etc. to have one way to define all hooks, and we should keep the existing hooks for backward compatibility. Otherwise, users need to use two different APIs for a similar use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that makes sense to me - can add some notes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the answer to my question #34 (comment) is yes, then LGTM. Great work @AbhiPrasad 👏
text/0034-sdk-lifecycle-hooks.md
Outdated
|
||
To test out this RFC, we implemented it in the [JavaScript SDK](https://github.com/getsentry/sentry-javascript/blob/7aa20d04a3d61f30600ed6367ca7151d183a8fc9/packages/types/src/client.ts#L153) with great success, so now we are looking to propose and implement it in the other SDKs. | ||
|
||
Lifecyle hooks can be registered on a Sentry client, and allow for integrations/sdk users to have finely grained control over the event lifecycle in the SDK. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
integrations/sdk users
That confused me a bit. I thought this API also targets our users, but as you @AbhiPrasad, explained in the TSC, this API is mostly internal. This information is essential to me as it completely changes how I evaluate this RFC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a remnant from before, forgot to change this!
|
||
# Proposal | ||
|
||
SDK hooks live on the client, and are **stateless**. They are called in the order they are registered. SDKs can opt-in to whatever hooks they use, and there can be hooks unique to an SDK. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to put this info in the RFC or the develop docs, as this was unclear to me when reading the RFC.
I didn't write JavaScript for 6 years or so, but event emitter still rings a bell now. For statically typed languages, we might have to adapt them to the language. For Swift and Java, it would maybe be something like addObserver
and notifyObserver
, or maybe we just stick to the JavaScript syntax. Thanks for explaining.
- Integrations want to add information to envelopes before they are sent to Sentry. | ||
- Integrations want to run code on transaction/span finish (to add additional spans to the transaction, for example). | ||
- Integrations want to mutate an error on `captureException` | ||
- Integrations want to override propagation behaviour (extracing/injecting outgoing headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Mobile, take a screenshot or attach the view hierarchy when capturing an exception. Could we replace our custom logic of attachment processors with that, @AbhiPrasad? We call some code that returns attachments and then bundle it into an envelope when capturing exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes! We could for sure replace that with this, will add your example to the list!
Rendered RFC
This PR proposes the introduction of lifecycle hooks in the SDK, improving extensibility and usability of SDKs at Sentry.
To test out this RFC, we implemented it in the JavaScript SDK with great success, so now we are looking to propose and implement it in the other SDKs.
Lifecyle hooks can be registered on a Sentry client, and allow for integrations/sdk users to have finely grained control over the event lifecycle in the SDK.
JS SDK work: getsentry/sentry-javascript#7262