-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Improve user facing integrations configuration #4789
Comments
I do like the idea of |
In a chat with @yordis we discussed having the multiple inits, but we would have to take care to make sure that this is communicated accordingly. We could make it that using a client + hub directly is the only way to treeshake this - but this does require the user to learn what the hub/client is. // client has 0 defaults
const client = new BrowserClient(options);
const hub = new Hub(client);
makeMain(hub) As an aside there is a general thinking of improving the integration API and interface. I'm going to open up a separate issue for this. |
One of the downsides I found with the client + hub approach is that all the SDKs currently have custom code in their So for example with the browser SDK, getting the release from |
Opened #4790 to chat about integrations API specifically |
I would lean on being explicit about how Sentry is being set up instead of going for strategies like As I mentioned to @AbhiPrasad, you barely touch the setup of Sentry after you are done. So the cost of wiring isn't that bad. Proper documentation, and educating the users will be much better. in the long-term (while causing some pain in the midtime). I would lean on one function only import { init, defaultIntegrations, IntegrationA, IntegrationB } from '@sentry/xxx';
// only defaults
init({
dsn: '__DSN__',
integrations: defaultIntegrations,
});
// or
init({
dsn: '__DSN__',
integrations: [
...defaultIntegrations,
makeMyIntegration({ /* ... */})
),
});
// some sentry integrations and custome one
init({
dsn: '__DSN__',
integrations: [
makeIntegrationA(),
makeIntegrationB(),
makeMyIntegration({ /* ... */})
),
}); |
Now I remember why @AbhiPrasad I told you to focus last on this because, in order to improve some areas, you must break change at the edge. For example,
Don't. Be explicit import { init, defaultIntegrations, IntegrationA, IntegrationB } from '@sentry/xxx';
init({ release: '' }) Or import { init, getRelease } from '@sentry/xxx';
// you can always expose more functions
init({ release: getRelease() })
Help me to understand what is the use-case here. I think I remember but I dont. |
While I agree and also personally prefer a fully explicit API, it is at odds with the Sentry SDK guidelines.
My interpretation of these guidelines is that Sentry would like users to have access to as many common features as possible with the most basic initialisation code (ie. This applies to sessions too:
Browser session tracking is currently started automatically here in sentry-javascript/packages/browser/src/sdk.ts Lines 98 to 100 in 689265a
This is no doubt because the SDK guidelines say:
This philosophy runs throughout the SDKs and is why I have suggested two different
So we should offer an explicit API because there is a requirement for tree shaking and there is customer demand. However, it probably shouldn't be the default or only API. Forcing ALL users to migrate to an explicit API also almost certainly falls foul of:
The Client + Hub API is already minimal and explicit but it's far from simple to migrate to from |
If that is the long-term goal, then two functions that allow full tree-shaking is what I would lean on. Being said, you can focus on what the "non-zero-install" wiring will look like, and then focus on tackling such wiring problems. In the worst case, "all problems can be solved with another layer of Even things like // just to make a point, I made up the clients idea.
init({
clients: [
composeMiddleware(
makeRateLimiting({ /*...*/ }),
makeHttpClient({ dsn: 'https://sentry.io/' })
),
composeMiddleware(
makeLogger({ /*...*/ }),
makeHttpBackend({ dsn: 'https://self-hosted-legacy.io/' })
)
]
}); About philosophyI would question such a goal at an all-of-nothing. On the web, every byte counts, and such philosophy doesn't take into consideration the negative consequences. I fully understand the intention, but as a customer first and formal, the user experience is being punished when you don't take into consideration the different personas. Being said, such a goal could be achieved in a way that takes into consideration such personal, just respect power-users and their experiences as well. So please take my push back with the intention to focus on the architecture first and then think about such things. That is why I even told @AbhiPrasad to ignore breaking changes at the edge so you can delay such conversations and whatnot. Most of the other ideas can be accomplished without breaking changes at the edge.
I wouldn't put such configuration in the same bucket as other ones, since that is actually something that will make sure the SDK works. So having it by default makes total sense to me. Being said all that, ake everything I said with a grain of salt, I am wrong more often than not 😅 |
I checked out the Rust SDK since it has to solve a similar issue where defaults could contribute to binary bloat. let _guard = sentry::init(("https://examplePublicKey@o0.ingest.sentry.io/0", sentry::ClientOptions {
release: sentry::release_name!(),
..Default::default()
})); So if that's ok for Rust, maybe this is perfectly ok for the JavaScript SDK: import { init, defaults } from '@sentry/xxx';
init({
...defaults,
dsn: '__DSN__'
}) If you don't care about bundle size (for node.js) you could remove an integration from the defaults like: import { init, defaults } from '@sentry/xxx';
init({
...defaults,
dsn: '__DSN__',
integrations: defaults.integrations.filter(i => i.name !== 'Console')
}) |
I think that is reasonable 🥳 |
I'm going to remove this from the v8 tracking issue given we've partly addressed some of this, but not all the items. |
I will close this issue for now, as we are not planning anything concrete here right now. We can always revisit this if we decide to do work around this in the future! |
Problem Statement
I've briefly chatted to @AbhiPrasad about this but we wanted to open the discussion up and get some feedback and ideas from others...
The current integration options are as follows:
There are a few issues I see with this:
defaultIntegrations: false
is not tree shakeable.defaultIntegrations
allowing an array is an internal implementation detail that has leaked into the public apiintegrations
function option solely exists to allow users to remove default integrations easilyUltimately, we're stuck between a rock and hard place trying to provide a fully featured standard configuration, simple customisability and the ability to fully customise for those who want to reduce bundle size to the bare minimum.
Solution Brainstorm
We could remove
defaultIntegrations
and supply a way to init the SDKs without any defaults and preferably one that allows tree shaking.The obvious way to do this would be to have two different
init
functions, one which includes the defaults and one which doesn't. For now I'm going to call theseinit
andinitRaw
but I'm open to better suggestions!It's also worth noting that
initRaw
could also include no default Transport to allow for greatest "tree shakeability".Adding a custom integration:
Removing an integration:
The
integrations
function option + filtering by name string to remove an integration feels a bit nasty but currently it appears to be the simplest solution to exclude an integration 🤔The text was updated successfully, but these errors were encountered: