Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

v2t: add v2 telemetry to the client/shared folder#62586

Merged
dadlerj merged 15 commits into
mainfrom
v2t-shared
Jun 3, 2024
Merged

v2t: add v2 telemetry to the client/shared folder#62586
dadlerj merged 15 commits into
mainfrom
v2t-shared

Conversation

@dadlerj

@dadlerj dadlerj commented May 10, 2024

Copy link
Copy Markdown
Member

This is a tough one, given how much abstraction there is here...

V2 telemetry is built on a foundation of using known/safe strings only for event logging, while the /shared folder is almost exclusively filled with extensible abstractions like "action items", "commands", and "extensions" that can be created by any code intelligence client (web, browser extensions).

I've run into this concretely in two places so far, but both of these still need to be updated before I can mark this as ready for review and mergeable:

  • First, in ActionItem.tsx, I am currently simply logging every event as blob.action / executed, with the action ID as a metadata parameter (whereas v1 telemetry logged the action ID as the event name itself. This really doesn't work for us—it means that things as critical as 'go to definition' and 'find references' are all logged as the abstract blob.action / executed... I need to figure out how to enforce the KnownString type on the action ID (or perhaps to add a new action property called eventFeature or similar that could have that constraint), which will require updates to all clients, unfortunately...

  • Second, in commands.ts, I got lazy, and didn't log anything just marked it as TBD. I'm not really sure what an example of a command is yet (given all the abstraction here), but I assume enforcing the same KnownString requirements will be necessary here.

Test plan

  1. sg start
  2. visit page
  3. check if events appear in event_logs table locally

@vovakulikov

Copy link
Copy Markdown
Contributor

@dadlerj

This really doesn't work for us—it means that things as critical as 'go to definition' and 'find references' are all logged as the abstract blob.action / executed

I haven't checked the existing dashboards with these events, but why wouldn't it work for us if we had an event with the same Feature name but differences in action type in eventProperties as dynamic arguments? I think it still would be possible to make a difference in amplitude/looker dashboards. I thought that part of the reason why we migrated to the new telemetry mechanism was partially about having strict/fixed event/feature names to minimize the surface of telemetry names.

Same suggestions about commands.ts, let me know if it wouldn't work I recently did the same thing for svelte prototype on old telemetry rails (by same I mean I used the same events from react app but made difference for svelte by enforcing svelte parameter in even properties, in amplitude at lease there is a way to parse JSON event bodies and select one property and filter datasets by this)

@bobheadxi

Copy link
Copy Markdown
Member

why wouldn't it work for us if we had an event with the same Feature name but differences in action type in eventProperties as dynamic arguments?

Safe metadata only accepts numeric fields and known keys, i.e. a similar problem will arise where we still need to have a known set of all the possible values here (either translated to int enum, or for inclusion in keys), which is by design (we shouldn't be able to record things we don't know about)

@dadlerj

dadlerj commented May 14, 2024

Copy link
Copy Markdown
Member Author

I was able to sort of "solve" this in these two commits in this PR by requiring all features be KnownString types.

(Thanks @bobheadxi for giving me the tip of upgrading the version of the @sourcegraph/telemetry library to make this work!)

@bobheadxi

Copy link
Copy Markdown
Member

@dadlerj I'm 80% sure that won't work - locally I see:

image

I'm going to poke around this this afternoon as well and see if I can come up with something for ya

@dadlerj

dadlerj commented May 14, 2024

Copy link
Copy Markdown
Member Author

Hah, interesting, because I don't get that locally (and I DO get successful events logged). I wonder if the
image

I haven't dug into CI issues yet though and I wouldn't be surprised if my local configuration isn't enforcing the rules correctly due to weird package versioning issues that I'm drowning in.

@bobheadxi

Copy link
Copy Markdown
Member

This problem will only fail the typechecker, but by nature of TS you don't need to fulfill the typechecker for things to run :)

@bobheadxi

Copy link
Copy Markdown
Member

Hm, quickly ran into a dead end with extHostAPI: Promise<Remote<FlatExtensionHostAPI>> which seems to make generics not work, i.e. T is always unknown and Feature extends string is always just string, and when you provide a type parameter:

image

Even when the interface declares a generic parameter:

image

@bobheadxi

bobheadxi commented May 14, 2024

Copy link
Copy Markdown
Member

I think I understand the problem a bit better now - https://github.com/GoogleChromeLabs/comlink has a type Remote that turns an interface into an "over the wire" interface, i.e. for communication between web workers, making changes like:

- interface { foo(): bar }
+ interface { foo(): Promise<Remote<bar>> }

i.e. function call foo() is now async and over the wire

In this process it strips out most typechecking except the most basic primitives, i.e.

- interface { foo<T extends string>(): bar }
+ interface { foo<string>(): Promise<Remote<bar>> }

You can see this in action if you remove Remote wrapping:

- extHostAPI: Promise<Remote<FlatExtensionHostAPI>>
+ extHostAPI: Promise<FlatExtensionHostAPI>

You get tons of compilation errors about Expression etc types where callsites omit the typically required adapters to turn a string into Expression. This kind of makes sense because over the wire it is impossible to enforce types, but makes for an unfortunate situation here.

In Cody agent protocol, we receive over events over the wire as well, and there we forcibly cast types into KnowString<'known'>, i.e. we pretend the string is a const known string. We can do that here as well, but we will completely lose the typechecking; in Cody agent, we still retain the typechecking at the callsite, but we can't do that here because of the use of Remote<>.

@dadlerj

dadlerj commented May 20, 2024

Copy link
Copy Markdown
Member Author

@bobheadxi can you check the commit I just pushed? Obviously lots of issues, but curious if this is roughly the structure of what you were proposing?

Comment thread client/shared/src/actions/ActionItem.tsx Outdated

@bobheadxi bobheadxi May 20, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here is the example I'm thinking of: https://sourcegraph.com/github.com/sourcegraph/cody/-/blob/agent/src/agent.ts?L684-695; this works because 'feature' fulfills the type requirements of KnownString (sorry that my other comments on this were a bit confusing)

Suggested change
action.telemetryProps.feature as KnownString<Feature>,
action.telemetryProps.feature as 'feature',

Note the docstring I put above in particular that explains the usage:

// 👷 HACK: We have no control over what gets sent over JSON RPC,
                // so we depend on client implementations to give type guidance
                // to ensure that we don't accidentally share arbitrary,
                // potentially sensitive string values. In this RPC handler,
                // when passing the provided event to the TelemetryRecorder
                // implementation, we forcibly cast all the inputs below
                // (feature, action, parameters) into known types (strings
                // 'feature', 'action', 'key') so that the recorder will accept
                // it. DO NOT do this elsewhere!

I think we can adapt that docstring to this callsite as well, based on my comment in https://github.com/sourcegraph/sourcegraph/pull/62586#issuecomment-2111022872 (in other words, we can't enforce complex types over the wire)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thank you! This is much more clear now.

Up above you wrote:

in Cody agent, we still retain the typechecking at the callsite, but we can't do that here because of the use of Remote<>.

By this do you mean that it's actually impossible to do here for some reason? Or could I actually enforce that the action.telemetryProps.feature value is a KnownString? It seems like it would do roughly the same thing here as what you describe happens with the agent—the "callers" would then be enforcing the requirement here as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No need to reply to this, the words being used here are hard to parse. I'll work on a next draft and get your take on that.

@dadlerj dadlerj marked this pull request as ready for review May 24, 2024 17:06
@dadlerj dadlerj requested a review from bobheadxi May 27, 2024 19:51
@dadlerj

dadlerj commented May 27, 2024

Copy link
Copy Markdown
Member Author

@vovakulikov @bobheadxi this PR is finally ready for review. I have it working locally and it passes CI! Let me know if you have any questions or comments, I'd love to get this in (and thus getting the pre-Svelte web codebase to ~100% v2 telemetry coverage) as soon as I can.

@bobheadxi bobheadxi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not 100% an expert here, but if CI type checks pass, everything here overall LGTM! Unfortunate that there is so much stuff under /legacy-extensions that still need to be maintained, but thank you for powering through these 💪

Comment thread client/client-api/src/contribution.ts Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should document the expected format here (maybe copied over from the telemetry recorder docstrings)

Comment thread client/shared/src/api/contract.ts Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
* @deprecated use recordEvent instead
* @deprecated use getTelemetryRecorder().recordEvent instead

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very elegant, nice!

this.props.telemetryService.log(action.id)
if (action.telemetryProps) {
this.props.telemetryRecorder.recordEvent(
// 👷 HACK: We have no control over what gets sent over Comlink/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this a Comlink issue though? It looks like that in this place we don't run anything in web-worker, no?
ActionItem is a react component and it's used under WebHoverOverlay, or are you referring that we get
actions through extensions API which is web-workers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's a problem with how the comlink library thing in use here manipulates types - see https://github.com/sourcegraph/sourcegraph/pull/62586#issuecomment-2111022872

For example, in ActionContribution, we make use of a type called TemplateExpression. In a properly type-checked scenario you'll run into type errors if you try to cast a string into TemplateExpression directly - you have to use parse(...) from the package. But, all callsites registerContributions(...) provide strings directly for fields that are TemplateExpression - because Comlink turns the type signature into a string.

Declared type:
image

Post-conversion to Raw from Comlink:
image

Similarly, the type hack we use to try and force callsites to use a static string for feature/action gets removed by the Comlink

@dadlerj dadlerj merged commit 8275054 into main Jun 3, 2024
@dadlerj dadlerj deleted the v2t-shared branch June 3, 2024 23:34
dadlerj referenced this pull request Jun 24, 2024
Codeintel v1 telemetry/event logging was broken in
https://github.com/sourcegraph/sourcegraph/pull/62586 due to the lack of
parens around a ternary operator. This simply fixes that issue.

## Test plan

CI

## Changelog

Co-authored-by: Dan Adler <5589410+dadlerj@users.noreply.github.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants