Skip to content

Conversation

@limhjgrace
Copy link
Contributor

@limhjgrace limhjgrace commented Sep 30, 2025

Fixes #2830

Changes

This PR proposes the a span definition for an application screen's time to first draw following spans (spanName = span.app.screen.time_to_first_draw.internal)

Revisions

  • Rev2: Rev1-Rev2 diff
    1. App start span with only cold and warm values defined
    2. Time to first appear client span --> time to first draw internal span
    4. Screen load client span --> screen load internal span. Definition also changed: Time to main thread idle --> time to first stable frame
    5. Screen visible client span --> screen visible event
  • Rev3: Rev2-Rev3 diff
    1. App start client span --> app start internal span. Clarified end time to be the moment before UI rendering begins.
    2. Removed screen load span. Will raise separate PR to get other proposals unblocked.
    3. Unit fix (ns -> s)
    4. Type fix (int -> double)
    6. Rename app.screen.first_draw --> app.screen.time_to_first_draw
    7. app.visible event -> app.time_on_screen internal span
  • Rev4: Rev3-Rev4 diff
    1. Removed *.duration attributes
    2. Removed app start span so that it is reviewed in a separate PR: feat: Add span for app start #2965
    3. Removed span.app.time_on_screen.internal

Merge requirement checklist

  • CONTRIBUTING.md guidelines followed.
  • Change log entry added, according to the guidelines in When to add a changelog entry.
    • If your PR does not need a change log, start the PR title with [chore]
  • Links to the prototypes or existing instrumentations (when adding or changing conventions)

requirement_level: opt_in
- ref: app.screen.main_thread_busy_time
requirement_level: opt_in
- id: span.app.screen.visible.client
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be an event or even a metric to avoid long running span.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will convert to an event

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually reopening this as I don't think it should be an event given it has a duration.

My understanding with long running spans is that the concern comes from the risk of data loss since it can result in an all or none scenario, assuming the span is being used as the root span for many child spans. For example, the session span. Having a long running session span is risky assuming all the spans within the session with have this as its parent span. If this span isn't sent for some reason, all the child spans are lost.

For this span, however, the idea is to create the span only once at the end of the screen's visibility, like how we would if we were to create a log. We can store the timestamps locally and avoid creating a long span by doing span.setStartTime(prev).end(now) once the screen is no longer visible.

@lmolkova lmolkova moved this from Untriaged to Awaiting codeowners approval in Semantic Conventions Triage Sep 30, 2025
Copy link
Contributor

@LikeTheSalad LikeTheSalad left a comment

Choose a reason for hiding this comment

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

Thank you for this contribution, @limhjgrace. I believe it's nice to have a platform-agnostic way of describing screen-related events, as you've proposed here. I've added some comments with some concerns, but generally speaking, I think we should have something like this in the spec.

One note I'd like to add is that, by nature, it seems like some of the events defined here have some sort of relation or overlap (for example the app.start.client one with one of the app.screen.(first_time|load) ones) which I think it would be nice to clarify on their descriptions, and also to name the events (if possible) in a way that these connections/roles can become a little more intuitive. For example, if I understand correctly, the difference between app.screen.first_time and app.screen.load is that the former measures the time a screen takes to render the UI, whereas the latter also takes into account the time a user might have to wait until they can interact with the screen, even though the UI is rendered (maybe due to some ongoing network request that's needed to add data into the screen). So in that sense, the former might be implicit in the latter, so maybe the names might be something like screen.pre_load and screen.load. Or, maybe making them a bit more specific could probably help too, with something like screen.ui_rendered and screen.ready. Names aren't my strong suit, so I wouldn't take them literally, but my point is that the current ones don't seem to intuitively describe these connections, and I think it would be great to do so.

@@ -0,0 +1,97 @@
groups:
- id: span.app.screen.first_appear.client
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks to me that this could better suit an event, rather than a span, considering that it's mostly about gathering the duration attribute and that it doesn't seem to matter much when the process started and ended.

Copy link
Contributor Author

@limhjgrace limhjgrace Oct 8, 2025

Choose a reason for hiding this comment

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

No, I think there are use cases where process start and end is still important. For example, if I wanted to create a visualization of all the telemetry in chronological order I would need these start and end times.

Maybe this can be achieved by using the duration and the observedTimestamp but with that aside, and maybe I've oversimplified the definition, I thought we should be treating telemetry with a duration as a span and logs to be things that happened in an instance in time? Unless it's a long-running span that has a high risk of not gracefully being ended. Or is the correct definition that we should consider a span to be telemetry with start/end times of significance?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the fact that we gather a duration attribute makes it more suitable for an event rather than a span. To me, a span is useful to track the start/end of a process, from which the duration can be computed. Also, to be able to keep track of children spans in a way that they can be displayed together in a waterfall UI. Unless I missed something, the use case we're discussing here doesn't need those features, though please let me know if that's not the case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree with @LikeTheSalad description that spans are useful for tracking a process and seeing all the child processes triggered as a result. Wheras events are useful for knowing x happened at time Y.

I do however see the use of having one span to describe the navigation process which can act as the root but none currently do that for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To me, a span is useful to track the start/end of a process, from which the duration can be computed.

+1. But for the duration calculation, I wanted to do this client side instead of server-side or query-time as this adds scalability, performance, and technical concerns. Is there a concern in adding a duration attribute to spans even though it can be derived from the start/endtime?

Also, to be able to keep track of children spans in a way that they can be displayed together in a waterfall UI. Unless I missed something, the use case we're discussing here doesn't need those features, though please let me know if that's not the case.

Yes, this is a valid use case for services who want to provide a way to visualize an entire session. There was an on going discussion on how we could provide this view by having the specs support a session span but given the concerns of a long running span, the community didn't align on this.

spans are useful for tracking a process and seeing all the child processes triggered as a result

+1. To me, a screen load and time to first draw are both processes and is analogous to a web page load, not vitals to a screen. I know below that they've have been compared to the web vitals but I don't think that is an apples to apples comparison, especially when not all the web vitals have a timing related component to it e.g. CLS. It was also mentioned that we could have a span for the screen but I'm thinking this will have similar long running concerns as a session span and won't be viable. That and I'm assuming the suggestion was to add these as span events? But I thought those are being deprecated?

Maybe this is a question of what is a span and what is a log event given there is some overlap between the two where a log event should be used in place of creating long running spans. Technically, most spans could also just be defined as a log event and the discussion feels like it's heading towards that direction. If so, are we defining spans to be used only for tracking a process for its child processes? Or is the suggestion to combine both of these spans into one and have the timing defined as attributes (e.g. attributes.time_to_first_draw and attributes.time_to_first_stable_draw)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the detailed response and all the thought you're putting into this! I definitely agree that having to support deprecated schemas becomes painful very quickly.

I have two main use cases:

  1. Session timeline, kind of like the network requests in the console network tab where you can see horizontal bars for the requests over time and what DataDog currently supports (https://docs.datadoghq.com/tracing/trace_explorer/visualize/). In addition, I want to include both spans and events so that I can see everything that happened in the session in one glance. If something occurred and it had a duration, I want to see the horizontal bar represent its duration. If it was just a moment in time, I'll see it as just a line to indicate something happened even though it had 0 duration.
  2. I want to be able to aggregate data across sessions. For example, I want to know the p99 time to first draw duration, the average time on screen, the max app launch time, etc. I need one singular duration value to do these aggregations so if the start and endtime is the only thing provided, I would have to calculate and store this duration server side so that I can support these aggregations. However, I'm okay with removing this from p0 if it will unblock PR.

In terms of a working implementations, I could point to the current OTel Android SDK where:

  1. AppStart is a span (ref)
  2. TimeToFirstDraw is being contributed by Leo as a span (feat(instrumentation): Add FirstDraw spans to activity instrumentation opentelemetry-android#1281)

As for the visibility span, I see that Embrace is also recording this as a span: https://github.com/embrace-io/embrace-apple-sdk/blob/836f3f46d7cdb30cc3a3ef83dfc83a6b3803fd52/Sources/EmbraceCore/Capture/UX/View/UIViewControllerHandler.swift#L299-L309

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @limhjgrace

Thank you for your patience and for explaining your use cases. Based on what you mentioned, you definitely need a span to show it as a trace, and you also need some value to aggregate durations, and you'd like to provide this for individual screen loading as well as the app launch process, the latter I think makes reviewing this PR less straightforward because it implies that the same approach for screens can be extrapolated to the app launch, which is mostly where I have concerns. Also, the proposal seems to mix not only different use cases, but it also seems to make signals step on each other's toes, which I agree is not always easy to find the right one for the right job, but if, on top of that, we add different use cases into the mix, the conversation can become difficult to enable a way forward.

So to move forward, I'd suggest the following:

  • Pick a single use case between the screen load and the app launch, preferably the screen load one, so that we can focus on it properly and then address the app launch one in a separate PR.
  • Regarding the "working implementations" that you mentioned, @Doohl's PR might not entirely fit what I mentioned about "a working implementation that has been used for a while", but still, I think there is some prior work that addresses similar issues, so it's not an entirely new use case, so I think we can try and make this work by checking on both PRs at the same time. I'll follow up with @Doohl's here to see if we can support this semconv PR from there.
  • Regarding the other working implementation that you mentioned from OTel Android (AppStart), it might not be what its name suggests, unfortunately. It seems to cover only the creation of the first Activity, which ignores the previous part of the app creation that covers an app's launch. This is one of the reasons why I think it'd be better to focus on the screen load use case first, because the app launch one might take a while to get sorted, so I think it deserves its own dedicated place to discuss it.
  • Lastly, regarding the screen load scenario, I think what you need sounds quite similar to what's already done for HTTP calls, for example, in the sense that they also need to be shown as a trace, and there's also a duration attribute for them that serves the aggregation purposes. I think we should try to be as consistent as possible, so it makes sense to me to try and follow such scenarios as a baseline for similar new ones. For the case of HTTP, they use 2 signals, spans and metrics. The duration attribute is captured as a metric, so that would be my initial thought to go about it. However, since metrics aren't something that can be tracked per session, I think the next best option would be an event. So I'd suggest defining both for the screen load use case.

Copy link
Contributor Author

@limhjgrace limhjgrace Oct 23, 2025

Choose a reason for hiding this comment

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

preferably the screen load one

Done

I'll follow up with @Doohl's open-telemetry/opentelemetry-android#1281 to see if we can support this semconv PR from there.

Took a look at Leo's PR and also left comments re. semConv

its own dedicated place to discuss it.

That's fair. Created separate PR for this: #2965

However, since metrics aren't something that can be tracked per session, I think the next best option would be an event. So I'd suggest defining both for the screen load use case.

I disagree about adding it as both a span and event. But as I mentioned before, I'm fine with dropping duration as this can be calculated server side. Removed *.duration attributes in next revision to unblock PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for descoping the pr and focusing on a single use case. As it stands the biggest question to be addresses is when a screen load span should end and based on that update the name.

My feeling based on comments in linked pr is that it should encompass the time until fully drawn. Then in subsequent pr's associate details about time taken to get to first draw etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, my intention with this PR is to just define the span for time to first draw. I wanted to descope defining the end of a screen load span since getting alignment on this is looking to be a lot more significant than I expected.

**Span kind** MUST be `CLIENT`.
This span captures the time from user initiation (e.g., tapping the app icon or opening a link)
to the moment the app is ready for interaction.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this description somewhat links this event to the "screen load" or "screen first time", in the sense that the duration is gathered up to the point where the "root screen" is either finished rendering, or finished fully loading. Though it's not clear which one of those cases it refers to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, the intention was not to link it to either as technically an application can be considered launched/started before the first screen has been loaded. My understanding is that for the Android SDK, the end of an app start is defined to be when the "Activity Resumed" life cycle event occurs for the first activity. Similarly, Apple provides didBecomeActiveNotification which could be used but doesn't necessarily mean a screen has loaded.

Copy link
Contributor

Choose a reason for hiding this comment

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

technically an application can be considered launched/started before the first screen has been loaded

The description reads:

"to the moment the app is ready for interaction"

How can it be possible that the app is ready for interaction without the first screen being loaded?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's fair, I'll update to remove this specification and make it more broad/applicable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, to completely decouple the app start with the screen load, I've updated the definition so that app start completes the moment before UI rendering begins. This works for both Android (AppStart span ends after the first Activity finishes creating which is before UI rendering) and iOS according to their app launch sequence doc.

@limhjgrace limhjgrace changed the title feat: Add spans for app launch, screen load/visible/time to first appear feat: Add spans for app start, screen load/visible/time to first draw Oct 8, 2025
Copy link
Contributor

@thompson-tomo thompson-tomo left a comment

Choose a reason for hiding this comment

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

Blocking due to the time measurement units being used & attributes used on the event.

I still agree with @LikeTheSalad that most if not all the spans would be better suited as events. An argument could be made to have 1 span definition to represent a user requesting a screen as a root span but 3 seems over the top. In terms of capturing vitals of that span, I think referring to prior art (https://opentelemetry.io/docs/specs/semconv/browser/events/#webvital-event) is helpful to ensure a consistent experience, perhaps we call the event app.screen_vital.

@limhjgrace limhjgrace changed the title feat: Add spans for app start, screen load/visible/time to first draw feat: Add spans for app start, time to first draw, time on screen Oct 17, 2025
@github-actions
Copy link

This PR contains changes to area(s) that do not have an active SIG/project and will be auto-closed:

  • app

Such changes may be rejected or put on hold until a new SIG/project is established.

Please refer to the Semantic Convention Areas
document to see the current active SIGs and also to learn how to kick start a new one.

@github-actions github-actions bot closed this Oct 23, 2025
@limhjgrace
Copy link
Contributor Author

limhjgrace commented Oct 23, 2025

It looks like the PR was auto closed because there's no dedicated SIG for area:app. @thompson-tomo could I know how to proceed forward in getting this PR reviewed? Similar scenario for the PR I created for app start: #2965

@limhjgrace limhjgrace changed the title feat: Add spans for app start, time to first draw, time on screen feat: Add spans for time to first draw and time on screen Oct 23, 2025
@lmolkova
Copy link
Member

lmolkova commented Oct 23, 2025

@limhjgrace I believe this should fix the automation: #2968 (but until it's merged any push would close this PR again, sorry about this)

@lmolkova lmolkova reopened this Oct 23, 2025
@limhjgrace limhjgrace changed the title feat: Add spans for time to first draw and time on screen feat: Add time to first draw span for app screen Oct 24, 2025
Comment on lines +11 to +12
This span captures the time from screen initialization to the first frame
of an application UI screen being drawn.
Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding from this description is that the span covers an initialization process done before drawing a screen's UI? Is that correct? I'm asking because at first I thought it was about covering the UI first draw, which may be the case and I'm not properly understanding the description, so I wanted to double-check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Add app spans

5 participants