Skip to content

feat(ourlogs): Adjust 'log' protocol per sdk feedback #4592

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

Merged
merged 22 commits into from
Apr 11, 2025

Conversation

k-fish
Copy link
Member

@k-fish k-fish commented Mar 20, 2025

Summary

Now that we've mostly finalized the logs protocol with sdks (see develop doc for more info), we want to update Relay to allow the 'log' item type to be sent in this format.

SDK's will likely primarily use the log ItemType instead of otel_log since we don't need to change timestamp conventions, can send a simplified level (see OurLogLevel added in this PR).

Schema changes

We've deprecated some fields in the protocol that only exist for OTEL:

  • severity_number and severity_text, we're coercing these to level, but we're keeping the original severity text and number as attributes as OTel allows custom severity text.
  • observed_timestamp_nanos is always set by relay regardless of what is sent because Relay is the 'collector'. We have to leave this as an attribute as well since it's being used by the existing consumer for origin timestamp.
  • timestamp_nanos becomes timestamp: Timestamp
  • trace_flags, this is unused, and the consumer doesn't even store it in the table. Will decide what to do with this later.

Future work

  • The ourlog_merge_otel function can be trimmed down since we won't need to fill in deprecated fields to send the same data to the kafka consumer.
  • We may need to transform the OurLog protocol from json received from sdks to a generic EAP "trace items" kafka message that is essentially a couple fields (eg. traceid) + a KVMap for attributes.

@k-fish k-fish force-pushed the feat/ourlogs/adjust-log-item-type branch 4 times, most recently from 94acbee to e551796 Compare March 20, 2025 15:58
@k-fish k-fish marked this pull request as ready for review March 20, 2025 15:58
@k-fish k-fish requested a review from a team as a code owner March 20, 2025 15:58
@k-fish k-fish force-pushed the feat/ourlogs/adjust-log-item-type branch from 418ea5f to f133144 Compare March 25, 2025 15:19
Copy link
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

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

Just some tiny nits

Now that we've mostly finalized the logs protocol with sdks (see develop doc for more info), we want to update Relay to allow the 'log' item type to be sent in this format.

Also in this PR is some shimming between the updated protocol / event schema and the kafka consumers. This shimming is temporary until the generic EAP items consumers are ready, at which point we'll have to transform the event-schema Relay accepts into the generic eap item kafka.
@k-fish k-fish force-pushed the feat/ourlogs/adjust-log-item-type branch from f64c4c8 to 69b8290 Compare March 27, 2025 15:20
@k-fish k-fish requested a review from Dav1dde March 27, 2025 15:28
Copy link
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

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

The implementation has a differing implementation for FromValue and ToValue with the implicit assumption that the only serialization happens for Kafka. But an event can flow through multiple Relay instances, this will not work. FromValue and ToValue must agree on the format. I recommend updating the consumers first.

On that note, the Attribute implementation is not forward compatible, but it is also very hard to implement. I'll work on this in a separate PR which you then can depend on.

@Dav1dde
Copy link
Member

Dav1dde commented Mar 31, 2025

#4626 implements support for #[metastructure(flatten)] which we can use to model the attributes, this allows for a layout like:

#[derive(Debug, Empty, FromValue, IntoValue, ProcessValue)]
pub struct Attribute {
    #[metastructure(flatten)]
    pub value: AttributeValue,

    /// Additional arbitrary fields for forwards compatibility.
    #[metastructure(additional_properties)]
    pub other: Object<Value>,
}

#[derive(Debug, Empty, FromValue, IntoValue, ProcessValue)]
pub struct AttributeValue {
    #[metastructure(field = "type", required = true, trim = "false")]
    ty: Annotated<String>,
    #[metastructure(required = true pii = "true")]
    value: Annotated<Value>,
}

I experimented with a strongly typed variant in #4627, which 'validates' when converting via FromValue but this runs into a lot of problems (described a bit in that PR). So I think we should not go that approach and use the schema sketched here.

Instead we can invest into not making AttributeValue's fields public and only provide accessors (getters/setters) with strong validation (we can do that in a follow-up).

We will need an additional normalization and validation step during processing.

In any Relay we can validate the validity of the ty field with provided value, does the actual type match the type provided in value.

A lot of that logic I already started implementing (also in #4627), which may be of use:

        match (value.ty, value.value) {
            (Annotated(Some(ty), ty_meta), Annotated(Some(value), mut value_meta)) => {
                match (ty, value) {
                    (AttributeType::Bool, Value::Bool(v)) => Self::Bool(v),
                    (AttributeType::Int, Value::I64(v)) => Self::I64(v),
                    (AttributeType::Int, Value::U64(v)) => Self::U64(v),
                    (AttributeType::Float, Value::F64(v)) => Self::F64(v),
                    (AttributeType::Float, Value::I64(v)) => Self::F64(v as f64),
                    (AttributeType::Float, Value::U64(v)) => Self::F64(v as f64),
                    (AttributeType::String, Value::String(v)) => Self::String(v),
                    (ty @ AttributeType::Unknown(_), value) => Self::Other(AttributeValueRaw {
                        ty: Annotated(Some(ty), ty_meta),
                        value: Annotated(Some(value), value_meta),
                    }),
                    (ty, value) => {
                        value_meta.add_error(ErrorKind::InvalidData);
                        value_meta.set_original_value(Some(value));
                        Self::Other(AttributeValueRaw {
                            ty: Annotated(Some(ty), ty_meta),
                            value: Annotated(None, value_meta),
                        })
                    }
                }
            }
            (mut ty, mut value) => {
                if ty.is_empty() {
                    ty.meta_mut().add_error(ErrorKind::MissingAttribute);
                }
                if value.is_empty() {
                    value.meta_mut().add_error(ErrorKind::MissingAttribute);
                }
                Self::Other(AttributeValueRaw { ty, value })
            }

Then as an additional step we need a processing step, which removes unknown type/value combinations and annotates the value as InvalidData. This step can only run when Relay is ran in processing mode!

It is important to note, that only a processing Relay can make the decision whether a provided type is unknown or not.

Copy link
Member

@cleptric cleptric left a comment

Choose a reason for hiding this comment

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

Just some naming updates

@k-fish k-fish requested a review from Dav1dde April 7, 2025 15:10
Copy link
Member

@cleptric cleptric left a comment

Choose a reason for hiding this comment

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

Protocol in tests looks good to me 👍

Some(OurLogLevel::Warn) => 13,
Some(OurLogLevel::Error) => 17,
Some(OurLogLevel::Fatal) => 21,
_ => 25,
Copy link
Member

Choose a reason for hiding this comment

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

When parsing, we default other to info, is 25 correct here or should it be rather treated as info?

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, actually 0 is default and is essentially 'unmapped'.

@Dav1dde Dav1dde force-pushed the feat/ourlogs/adjust-log-item-type branch from 25c8c8a to defad31 Compare April 10, 2025 15:55
@Dav1dde Dav1dde force-pushed the feat/ourlogs/adjust-log-item-type branch from c4d7ca7 to 53c5ce0 Compare April 11, 2025 10:45
Copy link
Member

@Dav1dde Dav1dde left a comment

Choose a reason for hiding this comment

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

As per discussion, this should still not be used by SDKs, there are more changes planned to the envelope protocol, just not to the log item.

https://www.notion.so/sentry/Performance-Envelope-Containers-1d28b10e4b5d80b3b1c3e661f5eda6b4

@k-fish k-fish merged commit ff0af33 into master Apr 11, 2025
27 checks passed
@k-fish k-fish deleted the feat/ourlogs/adjust-log-item-type branch April 11, 2025 13:55
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.

4 participants