Skip to content

Commit

Permalink
Start tracing IDE indexing time with Skate plugin (#756)
Browse files Browse the repository at this point in the history
Start tracing IDE indexing time with Skate plugin.

This PR adds a new listener that extends
`ProjectIndexingHistoryListener` to send a trace when indexing is
complete. Example data trace:

<img width="329" alt="image"
src="https://github.com/slackhq/slack-gradle-plugin/assets/12748234/219b43e4-f17b-4905-b435-4414b354f39c">

https://analytics.tinyspeck.com/v3/explore/1000000001254057 




<!--
  ⬆ Put your description above this! ⬆

  Please be descriptive and detailed.
  
Please read our [Contributing
Guidelines](https://github.com/tinyspeck/slack-gradle-plugin/blob/main/.github/CONTRIBUTING.md)
and [Code of Conduct](https://slackhq.github.io/code-of-conduct).

Don't worry about deleting this, it's not visible in the PR!
-->
  • Loading branch information
linhpha authored Mar 6, 2024
1 parent 52eb617 commit bf8a4ed
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.wm.ToolWindowAnchor
import com.intellij.openapi.wm.ToolWindowManager
import com.slack.sgp.intellij.tracing.SkateTraceReporter
import com.slack.sgp.intellij.ui.WhatsNewPanelFactory
import java.util.function.Supplier

interface SkateProjectService {
val traceReporter: SkateTraceReporter

fun showWhatsNewWindow()
}

Expand All @@ -34,6 +37,9 @@ interface SkateProjectService {
* New UI of the Skate Plugin
*/
class SkateProjectServiceImpl(private val project: Project) : SkateProjectService {

override val traceReporter: SkateTraceReporter by lazy { SkateTraceReporter(project) }

override fun showWhatsNewWindow() {

val settings = project.service<SkatePluginSettings>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,18 @@ import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
import com.slack.sgp.intellij.SkateProjectServiceImpl.Companion.WHATS_NEW_PANEL_ID
import com.slack.sgp.intellij.tracing.SkateSpanBuilder
import com.slack.sgp.intellij.tracing.SkateTraceReporter
import com.slack.sgp.intellij.tracing.SkateTracingEvent
import com.slack.sgp.intellij.tracing.SkateTracingEvent.EventType.SKATE_WHATS_NEW_PANEL_CLOSED
import com.slack.sgp.intellij.tracing.SkateTracingEvent.EventType.SKATE_WHATS_NEW_PANEL_OPENED
import com.slack.sgp.intellij.util.getTraceReporter
import com.slack.sgp.intellij.util.isTracingEnabled
import java.time.Instant

/** Custom listener for WhatsNew Tool Window. */
class WhatsNewToolWindowListener(private val project: Project) : ToolWindowManagerListener {
// Initial state of screen should be hidden
private var wasVisible = false
private val startTimestamp = Instant.now()

override fun stateChanged(toolWindowManager: ToolWindowManager) {
val startTimestamp = Instant.now()
super.stateChanged(toolWindowManager)
if (!project.isTracingEnabled()) return

Expand All @@ -44,11 +42,12 @@ class WhatsNewToolWindowListener(private val project: Project) : ToolWindowManag

if (visibilityChanged) {
if (isVisible) {
skateSpanBuilder.addSpanTag("event", SkateTracingEvent(SKATE_WHATS_NEW_PANEL_OPENED))
skateSpanBuilder.addTag("event", SkateTracingEvent.WhatsNew.PANEL_OPENED)
} else {
skateSpanBuilder.addSpanTag("event", SkateTracingEvent(SKATE_WHATS_NEW_PANEL_CLOSED))
skateSpanBuilder.addTag("event", SkateTracingEvent.WhatsNew.PANEL_CLOSED)
}
SkateTraceReporter(project)
project
.getTraceReporter()
.createPluginUsageTraceAndSendTrace(
WHATS_NEW_PANEL_ID.replace('-', '_'),
startTimestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.slack.sgp.intellij.tracing.SkateSpanBuilder
import com.slack.sgp.intellij.tracing.SkateTraceReporter
import com.slack.sgp.intellij.tracing.SkateTracingEvent
import com.slack.sgp.intellij.tracing.SkateTracingEvent.EventType.HOUSTON_FEATURE_FLAG_URL_CLICKED
import com.slack.sgp.intellij.util.featureFlagFilePattern
import com.slack.sgp.intellij.util.getTraceReporter
import com.slack.sgp.intellij.util.isLinkifiedFeatureFlagsEnabled
import com.slack.sgp.intellij.util.isTracingEnabled
import java.net.URI
Expand Down Expand Up @@ -83,17 +82,21 @@ class UrlIntentionAction(private val message: String, private val url: String) :

override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
BrowserUtil.browse(URI(url))
skateSpanBuilder.addSpanTag("event", SkateTracingEvent(HOUSTON_FEATURE_FLAG_URL_CLICKED))
sendUsageTrace(project, project.isTracingEnabled())
skateSpanBuilder.addTag(
"event",
SkateTracingEvent.HoustonFeatureFlag.HOUSTON_FEATURE_FLAG_URL_CLICKED,
)
sendUsageTrace(project)
}

override fun startInWriteAction(): Boolean {
return false
}

fun sendUsageTrace(project: Project, isTracingEnabled: Boolean) {
if (!isTracingEnabled) return
SkateTraceReporter(project)
fun sendUsageTrace(project: Project) {
if (!project.isTracingEnabled()) return
project
.getTraceReporter()
.createPluginUsageTraceAndSendTrace(
"feature_flag_annotator",
startTimestamp,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2024 Slack Technologies, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.slack.sgp.intellij.idemetrics

import com.intellij.util.indexing.diagnostic.ProjectIndexingHistory
import com.intellij.util.indexing.diagnostic.ProjectIndexingHistoryListener
import com.intellij.util.indexing.diagnostic.dto.toMillis
import com.slack.sgp.intellij.tracing.SkateSpanBuilder
import com.slack.sgp.intellij.tracing.SkateTracingEvent
import com.slack.sgp.intellij.util.getTraceReporter
import com.slack.sgp.intellij.util.isTracingEnabled

@Suppress("UnstableApiUsage")
class IndexingListener : ProjectIndexingHistoryListener {
override fun onFinishedIndexing(projectIndexingHistory: ProjectIndexingHistory) {
val currentProject = projectIndexingHistory.project
if (!currentProject.isTracingEnabled()) return
val skateSpanBuilder = SkateSpanBuilder()
skateSpanBuilder.apply {
projectIndexingHistory.indexingReason?.let {
addTag(SkateTracingEvent.Indexing.INDEXING_REASON.name, it.take(200))
}
addTag(
SkateTracingEvent.Indexing.UPDATING_TIME.name,
projectIndexingHistory.times.totalUpdatingTime.toMillis(),
)
addTag(
SkateTracingEvent.Indexing.SCAN_FILES_DURATION.name,
projectIndexingHistory.times.scanFilesDuration.toMillis(),
)
addTag(
SkateTracingEvent.Indexing.INDEXING_DURATION.name,
projectIndexingHistory.times.indexingDuration.toMillis(),
)
addTag(
SkateTracingEvent.Indexing.WAS_INTERRUPTED.name,
projectIndexingHistory.times.wasInterrupted,
)
addTag(
SkateTracingEvent.Indexing.SCANNING_TYPE.name,
projectIndexingHistory.times.scanningType.name,
)
addTag("event", SkateTracingEvent.Indexing.INDEXING_COMPLETED.name)
}
currentProject
.getTraceReporter()
.createPluginUsageTraceAndSendTrace(
"indexing",
projectIndexingHistory.times.updatingStart.toInstant(),
skateSpanBuilder.getKeyValueList(),
)
}

override fun onStartedIndexing(projectIndexingHistory: ProjectIndexingHistory) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import com.slack.sgp.intellij.SkateBundle
import com.slack.sgp.intellij.modeltranslator.helper.TranslatorHelper
import com.slack.sgp.intellij.modeltranslator.model.TranslatorBundle
import com.slack.sgp.intellij.tracing.SkateSpanBuilder
import com.slack.sgp.intellij.tracing.SkateTraceReporter
import com.slack.sgp.intellij.tracing.SkateTracingEvent
import com.slack.sgp.intellij.util.getTraceReporter
import com.slack.sgp.intellij.util.isTracingEnabled
import java.time.Instant

Expand All @@ -40,7 +40,6 @@ class GenerateTranslatorBodyAction(private val bundle: TranslatorBundle) : Inten

override fun invoke(project: Project, editor: Editor?, psiFile: PsiFile?) {
val startTimestamp = Instant.now()

val body = TranslatorHelper.generateBody(bundle)
if (body != null) {
bundle.element.bodyBlockExpression?.replace(body) ?: LOG.warn("Body block expression is null")
Expand All @@ -55,12 +54,10 @@ class GenerateTranslatorBodyAction(private val bundle: TranslatorBundle) : Inten
private fun sendUsageTrace(project: Project, startTimestamp: Instant) {
val skateSpanBuilder =
SkateSpanBuilder().apply {
addSpanTag(
"event",
SkateTracingEvent(SkateTracingEvent.EventType.MODEL_TRANSLATOR_GENERATED),
)
addTag("event", SkateTracingEvent.ModelTranslator.MODEL_TRANSLATOR_GENERATED)
}
SkateTraceReporter(project)
project
.getTraceReporter()
.createPluginUsageTraceAndSendTrace(
"model_translator",
startTimestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,29 @@ import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.Project
import com.slack.sgp.intellij.tracing.SkateSpanBuilder
import com.slack.sgp.intellij.tracing.SkateTraceReporter
import com.slack.sgp.intellij.tracing.SkateTracingEvent
import com.slack.sgp.intellij.tracing.SkateTracingEvent.EventType.PROJECT_GEN_OPENED
import com.slack.sgp.intellij.util.getTraceReporter
import com.slack.sgp.intellij.util.isProjectGenMenuActionEnabled
import com.slack.sgp.intellij.util.isTracingEnabled
import java.time.Instant

class ProjectGenMenuAction @JvmOverloads constructor(private val offline: Boolean = false) :
AnAction() {

private val skateSpanBuilder = SkateSpanBuilder()
private val startTimestamp = Instant.now()

class ProjectGenMenuAction : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val currentProject: Project = e.project ?: return
val currentProject = e.project ?: return
if (!currentProject.isProjectGenMenuActionEnabled()) return
val startTimestamp = Instant.now()
ProjectGenWindow(currentProject, e).show()

if (currentProject.isTracingEnabled()) {
sendUsageTrace(currentProject)
sendUsageTrace(currentProject, startTimestamp)
}
}

fun sendUsageTrace(project: Project) {
skateSpanBuilder.addSpanTag("event", SkateTracingEvent(PROJECT_GEN_OPENED))
SkateTraceReporter(project, offline)
fun sendUsageTrace(project: Project, startTimestamp: Instant) {
val skateSpanBuilder = SkateSpanBuilder()
skateSpanBuilder.addTag("event", SkateTracingEvent.ProjectGen.DIALOG_OPENED)
project
.getTraceReporter()
.createPluginUsageTraceAndSendTrace(
"project_generator",
startTimestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class ProjectGenWindow(private val currentProject: Project?, private val event:
}

override fun createCenterPanel(): JComponent {
setSize(600, 800)
return ComposePanel().apply {
setBounds(0, 0, 600, 800)
setContent { DialogContent() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,28 @@ package com.slack.sgp.intellij.tracing
import com.slack.sgp.tracing.KeyValue
import com.slack.sgp.tracing.model.TagBuilder
import com.slack.sgp.tracing.model.newTagBuilder
import java.util.*

class SkateSpanBuilder {
private val keyValueList: TagBuilder = newTagBuilder()

fun addSpanTag(key: String, value: String) {
keyValueList.apply { key tagTo value }
fun addTag(key: String, value: String) {
keyValueList.apply { key.lowercase(Locale.US) tagTo value }
}

fun addSpanTag(key: String, value: SkateTracingEvent) {
keyValueList.apply { key tagTo value.type.name }
fun addTag(key: String, event: SkateTracingEvent) {
keyValueList.apply { key.lowercase(Locale.US) tagTo event.name }
}

fun getKeyValueList(): List<KeyValue> {
return keyValueList.toList()
fun addTag(key: String, value: Long) {
keyValueList.apply { key.lowercase(Locale.US) tagTo value }
}
}

class SkateTracingEvent(val type: EventType) {
enum class EventType {
PROJECT_GEN_OPENED,
HOUSTON_FEATURE_FLAG_URL_CLICKED,
SKATE_WHATS_NEW_PANEL_OPENED,
SKATE_WHATS_NEW_PANEL_CLOSED,
MODEL_TRANSLATOR_GENERATED
fun addTag(key: String, value: Boolean) {
keyValueList.apply { key.lowercase(Locale.US) tagTo value }
}

fun getKeyValueList(): List<KeyValue> {
return keyValueList.toList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.apache.http.HttpException

class SkateTraceReporter(private val project: Project, private val offline: Boolean = false) :
TraceReporter {
class SkateTraceReporter(val project: Project) : TraceReporter {

private val delegate by lazy {
val okHttpClient = lazy { OkHttpClient.Builder().build() }
Expand All @@ -48,13 +47,12 @@ class SkateTraceReporter(private val project: Project, private val offline: Bool
val loggingClient = lazy {
okHttpClient.value.newBuilder().addInterceptor(loggingInterceptor).build()
}
if (offline) {
NoOpTraceReporter
} else {
val endPoint =
project.tracingEndpoint()
?: throw RuntimeException("Required tracing endpoint to upload analytics")
val endPoint = project.tracingEndpoint()
if (endPoint != null) {
SimpleTraceReporter(endPoint, loggingClient)
} else {
LOG.info("No endpoint set up for tracing")
NoOpTraceReporter
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2024 Slack Technologies, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.slack.sgp.intellij.tracing

sealed interface SkateTracingEvent {
val name: String

enum class ProjectGen : SkateTracingEvent {
DIALOG_OPENED
}

enum class WhatsNew : SkateTracingEvent {
PANEL_OPENED,
PANEL_CLOSED
}

enum class HoustonFeatureFlag : SkateTracingEvent {
HOUSTON_FEATURE_FLAG_URL_CLICKED
}

enum class ModelTranslator : SkateTracingEvent {
MODEL_TRANSLATOR_GENERATED
}

enum class Indexing : SkateTracingEvent {
INDEXING_REASON,
UPDATING_TIME,
SCAN_FILES_DURATION,
INDEXING_DURATION,
WAS_INTERRUPTED,
SCANNING_TYPE,
INDEXING_COMPLETED,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package com.slack.sgp.intellij.util
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.slack.sgp.intellij.SkatePluginSettings
import com.slack.sgp.intellij.SkateProjectService
import com.slack.sgp.intellij.tracing.SkateTraceReporter

fun Project.settings(): SkatePluginSettings = service<SkatePluginSettings>()

Expand All @@ -30,3 +32,5 @@ fun Project.isTracingEnabled(): Boolean = settings().isTracingEnabled
fun Project.isProjectGenMenuActionEnabled(): Boolean = settings().isProjectGenMenuActionEnabled

fun Project.tracingEndpoint(): String? = settings().tracingEndpoint

fun Project.getTraceReporter(): SkateTraceReporter = service<SkateProjectService>().traceReporter
1 change: 1 addition & 0 deletions skate-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<errorHandler implementation="com.slack.sgp.intellij.SkateErrorHandler"/>
<annotator language="kotlin" implementationClass="com.slack.sgp.intellij.modeltranslator.TranslatorAnnotator"/>
<externalAnnotator language="kotlin" implementationClass="com.slack.sgp.intellij.featureflags.FeatureFlagAnnotator"/>
<projectIndexingHistoryListener implementation="com.slack.sgp.intellij.idemetrics.IndexingListener"/>
</extensions>

<applicationListeners>
Expand Down
Loading

0 comments on commit bf8a4ed

Please sign in to comment.