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

[v9]: Not possible to remove information in beforeSend (make SentryEvent and related classes mutable) #2672

Open
ColinSchmale opened this issue Feb 7, 2025 · 14 comments · May be fixed by #2818

Comments

@ColinSchmale
Copy link

Platform

Flutter Web

Obfuscation

Enabled

Debug Info

Enabled

Doctor

Version

8.12.0

Steps to Reproduce

Use options.beforeSend to override a value like this:

await Sentry.init((options) {
  options.beforeSend = (event, hint) {
    return event.copyWith(serverName: null); // Don't send server names.
  };
});

Expected Result

The value is not sent (or null).

Actual Result

The value is sent and not null.

This is what the documentation says about how to remove information:

await Sentry.init((options) {
  options.beforeSend = (event, hint) {
    return event.copyWith(serverName: null); // Don't send server names.
  };
});

but when you take a look at the code, this cannot work:

  final String? serverName;

  @override
  SentryEvent copyWith({
    String? serverName,
  }) =>
      SentryEvent(
        serverName: serverName ?? this.serverName,

Replacing the geo object with an empty geo object does also not work. It seems that the data is overwritten afterwards.

await Sentry.init((options) {
  options.beforeSend = (event, hint) {
    return event.copyWith(
        user: event.user?.copyWith(
        geo: SentryGeo(),
      ),
    );
  };
});

What I ended up doing was masking the city value like this:

await Sentry.init((options) {
  options.beforeSend = (event, hint) {
    return event.copyWith(
        user: event.user?.copyWith(
        geo: SentryGeo(city: '****'),
      ),
    );
  };
});

Related to #980

Are you willing to submit a PR?

None

@buenaflor
Copy link
Contributor

hi, thanks for raising this. we'll look into this and see how we can provide an option to make this possible

@buenaflor
Copy link
Contributor

@ColinSchmale I believe setting it to an empty string "" also does the trick

@ColinSchmale
Copy link
Author

**** is also fine for me.

I mostly created this issue because the code in the documentation is wrong and maybe to also get some information why this happens and if my solution is okay.

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 3 Feb 10, 2025
@buenaflor
Copy link
Contributor

buenaflor commented Feb 10, 2025

yeah we gotta update the docs definitely

it's been like this way before my time but afaict it is intended that way. so your method is right, setting null won't do anything you have to set it to some value e.g empty string or how you did it

I don't think it's ideal from a dx perspective but let's see what we can do there

@buenaflor buenaflor moved this from Needs Discussion to Backlog in Mobile SDKs Feb 11, 2025
@buenaflor buenaflor moved this from Backlog to Todo in Mobile SDKs Feb 11, 2025
@buenaflor buenaflor added this to the 9.0.0 milestone Feb 11, 2025
@buenaflor
Copy link
Contributor

buenaflor commented Feb 11, 2025

Since there is no practical use for having these immutable we've decided to refactor these into mutable data classes for the next major v9

@buenaflor buenaflor changed the title Not possible to remove information in beforeSend [v9]: Not possible to remove information in beforeSend Feb 12, 2025
@buenaflor buenaflor changed the title [v9]: Not possible to remove information in beforeSend [v9]: Not possible to remove information in beforeSend (make SentryEvent and related classes mutable) Feb 13, 2025
@buenaflor buenaflor self-assigned this Feb 13, 2025
@buenaflor buenaflor assigned denrase and unassigned buenaflor Feb 21, 2025
@denrase denrase moved this from Todo to In Progress in Mobile SDKs Feb 25, 2025
@denrase
Copy link
Collaborator

denrase commented Feb 25, 2025

Gave this some thought and I think there might also be some risk in changing our immutable data objects. Right now they are thread safe and also hashable, so this would be large a behavioural change. Also having them final is the recommended way: accoridng to Effective Dart Docs.

There would be another way, ut this would change the way we use copyWith.

// Generic wrapper to preserve type information
class Value<T> {
  final T? value;
  const Value(this.value);
  const Value.absent() : value = null;
}

class Person {
  final String? name;
  final int? age;

  const Person({this.name, this.age});

  Person copyWith({
    Value<String>? name,
    Value<int>? age,
  }) {
    return Person(
      name: name != null ? name.value : this.name,
      age: age != null ? age.value : this.age,
    );
  }
}

// Usage
final person = Person(name: "John", age: 30);
final updatedPerson = person.copyWith(
  name: Value(null),  // Sets name to null
  // age remains unchanged
);

@denrase
Copy link
Collaborator

denrase commented Feb 25, 2025

Or we could use nullable functions as parameters, either replacing copyWith or adding an additional method, optionally deprecating copyWith.

class Person {
  final String? name;
  final int? age;

  const Person({this.name, this.age});

  // Original copyWith remains unchanged
  Person copyWith({
    String? name,
    int? age,
  }) {
    return Person(
      name: name ?? this.name,
      age: age ?? this.age,
    );
  }

  // Renamed method that supports setting nulls
  Person nullableCopyWith({
    String? Function()? name,
    int? Function()? age,
  }) => copyWith(
    name: name?.call(),
    age: age?.call(),
  );
}

// Usage
final person = Person(name: "John", age: 30);
final updated1 = person.copyWith(name: "Jane");     // Original usage
final updated2 = person.nullableCopyWith(           // New usage for null setting
  name: () => null,
  age: () => 31,
);

@denrase
Copy link
Collaborator

denrase commented Feb 25, 2025

Updated docs: getsentry/sentry-docs#12832

@denrase denrase moved this from In Progress to Needs Discussion in Mobile SDKs Feb 25, 2025
@ColinSchmale
Copy link
Author

I agree that immutable data objects are better. The solution with the nullable functions as parameters is pretty nice but wouldn't be necessary if this worked:

await Sentry.init((options) {
  options.beforeSend = (event, hint) {
    return event.copyWith(
        user: event.user?.copyWith(
        geo: SentryGeo(),
      ),
    );
  };
});

Do you know why I wasn't able to remove the geo data by overriding geo with a new object? All values of the new SentryGeo() object should be null, right?

@stefanosiano
Copy link
Member

hi @ColinSchmale
The discussion above is for a more general approach into how we can provide a way to modify any value.
However, at a glance, your workaround should work fine.
We'll definitely take a look into why it's not working

@denrase
Copy link
Collaborator

denrase commented Mar 3, 2025

@ColinSchmale Thx for the update! Setting a new geo object should work, i'll investigate this.

@denrase
Copy link
Collaborator

denrase commented Mar 3, 2025

@ColinSchmale So just from the clients perspective, i can set an empty geo object to the user. The geo is empty, so no payload is sent. So i'd say this works as expected from the clients perspective. Could you give us more context on how to reproduce this?

Image Image Image

@denrase
Copy link
Collaborator

denrase commented Mar 3, 2025

@ColinSchmale @stefanosiano Only thing i can imagine is, since we do not send empty geo json, the backend is also "ignoring" it. But I'm missing context here about what is supposed to be the outcome.

@buenaflor
Copy link
Contributor

buenaflor commented Mar 10, 2025

Gave this some thought and I think there might also be some risk in changing our immutable data objects. Right now they are thread safe and also hashable, so this would be large a behavioural change. Also having them final is the recommended way: accoridng to Effective Dart Docs.

  • thread safe -> correct me if I'm wrong but I don't think this really is an issue in dart since dart is singled threaded and isolates dont share memory with one another
  • hashable -> what functionality in the SDK could break here?
  • having them final is the recommended way -> imo this is not a Dart specific thing, you could argue all our other SDKs should do this as well but we are as far as I can tell the only SDK doing it this way.

imo we have a good use case here since we do have to mutate the event frequently

I feel like adding more and more stuff on top to mak setting fields nullabe work seems to me is just adding more complexity compared to having it mutable. of course changing the implementation to mutable will also entail its own workload but imo worth it especially now with an upcoming major it's a good opportunity to improve this

@denrase denrase moved this from Needs Discussion to In Progress in Mobile SDKs Mar 24, 2025
@denrase denrase linked a pull request Mar 24, 2025 that will close this issue
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Status: In Progress
Development

Successfully merging a pull request may close this issue.

5 participants