Skip to content

Commit

Permalink
feat(telemetry): Add telemetry on signals INTELLIJ-180 (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
kmruiz authored Jan 17, 2025
1 parent a3328ea commit 845d0dc
Show file tree
Hide file tree
Showing 18 changed files with 588 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ MongoDB plugin for IntelliJ IDEA.
## [Unreleased]

### Added
* [INTELLIJ-180](https://jira.mongodb.org/browse/INTELLIJ-180) Telemetry when inspections are shown and resolved.
It can be disabled in the Plugin settings.
* [INTELLIJ-176](https://jira.mongodb.org/browse/INTELLIJ-176) Add support for parsing, inspecting and autocompleting in an unwind stage written using `Aggregation.unwind`.
* [INTELLIJ-173](https://jira.mongodb.org/browse/INTELLIJ-173) Add support for parsing, inspecting and autocompleting in a project stage written using `Aggregation.match` and chained `ProjectionOperations` using `andInclude` and `andExclude`.
* [INTELLIJ-172](https://jira.mongodb.org/browse/INTELLIJ-172) Add support for parsing, inspecting and autocompleting in an aggregation written using Spring Data MongoDB (`MongoTemplate.aggregate`, `MongoTemplate.aggregateStream`) and a match stage written using `Aggregation.match`.
Expand Down
9 changes: 9 additions & 0 deletions etc/inspections.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

./etc/plugin-logs.sh |\
grep 'inspection_id' |\
jq '{ inspection: .inspection_type, id: .inspection_id, status: .error_status, meta: { error_field_type: .error_field_type, actual_field_type: .actual_field_type } }' |\
jq -s |\
jq 'group_by(.id) | map({ id: .[0].id, inspection: .[0].inspection, status: map(.status), meta: map(.meta) })' |
jq '.[]'

3 changes: 3 additions & 0 deletions etc/plugin-logs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

cat ./packages/jetbrains-plugin/build/idea-sandbox/system/log/idea.log | grep "pluginVersion"
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,19 @@ abstract class AbstractMongoDbInspectionBridge(
private val coroutineScope: CoroutineScope,
private val inspection: MongoDbInspection,
) : AbstractBaseJavaLocalInspectionTool() {
protected abstract fun emitFinishedInspectionTelemetryEvent(problemsHolder: ProblemsHolder)

override fun buildVisitor(
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession,
): PsiElementVisitor =
object : JavaElementVisitor() {
override fun visitJavaFile(file: PsiJavaFile) {
super.visitJavaFile(file)
emitFinishedInspectionTelemetryEvent(holder)
}

override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
dispatchIfValidMongoDbQuery(expression)
}
Expand All @@ -53,22 +60,26 @@ abstract class AbstractMongoDbInspectionBridge(
val dialect = expression.containingFile.dialect ?: return@runReadAction

val queryService by expression.project.service<CachedQueryService>()
queryService.queryAt(expression)?.let { query ->
fileInExpression.virtualFile?.let {
val query = queryService.queryAt(expression)

if (query != null) {
if (fileInExpression.virtualFile != null) {
inspection.visitMongoDbQuery(
coroutineScope,
dataSource?.localDataSource,
holder,
query,
dialect.formatter,
)
} ?: inspection.visitMongoDbQuery(
coroutineScope,
null,
holder,
query,
dialect.formatter
)
} else {
inspection.visitMongoDbQuery(
coroutineScope,
null,
holder,
query,
dialect.formatter
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,29 @@ import com.mongodb.jbplugin.linting.FieldCheckingLinter
import com.mongodb.jbplugin.meta.service
import com.mongodb.jbplugin.mql.Node
import com.mongodb.jbplugin.mql.components.HasCollectionReference
import com.mongodb.jbplugin.observability.TelemetryEvent
import com.mongodb.jbplugin.observability.probe.InspectionStatusChangedProbe
import kotlinx.coroutines.CoroutineScope

/**
* @param coroutineScope
*/
@Suppress("MISSING_KDOC_TOP_LEVEL")
class FieldCheckInspectionBridge(coroutineScope: CoroutineScope) :
AbstractMongoDbInspectionBridge(
coroutineScope,
FieldCheckLinterInspection,
)
) {
override fun emitFinishedInspectionTelemetryEvent(problemsHolder: ProblemsHolder) {
val probe by service<InspectionStatusChangedProbe>()

probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.FIELD_DOES_NOT_EXIST,
problemsHolder
)

probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.TYPE_MISMATCH,
problemsHolder
)
}
}

/**
* This inspection object calls the linting engine and transforms the result so they can be rendered in the IntelliJ
Expand Down Expand Up @@ -67,10 +79,11 @@ internal object FieldCheckLinterInspection : MongoDbInspection {
is FieldCheckWarning.FieldDoesNotExist -> registerFieldDoesNotExistProblem(
coroutineScope,
problems,
it
it,
query
)
is FieldCheckWarning.FieldValueTypeMismatch ->
registerFieldValueTypeMismatch(coroutineScope, problems, it, formatter)
registerFieldValueTypeMismatch(coroutineScope, problems, it, formatter, query)
}
}
}
Expand Down Expand Up @@ -121,6 +134,7 @@ internal object FieldCheckLinterInspection : MongoDbInspection {
coroutineScope: CoroutineScope,
problems: ProblemsHolder,
warningInfo: FieldCheckWarning.FieldDoesNotExist<PsiElement>,
query: Node<PsiElement>,
) {
val problemDescription = InspectionsAndInlaysMessages.message(
"inspection.field.checking.error.message",
Expand All @@ -138,18 +152,28 @@ internal object FieldCheckLinterInspection : MongoDbInspection {
),
),
)

val probe by service<InspectionStatusChangedProbe>()
probe.inspectionChanged(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.FIELD_DOES_NOT_EXIST,
query
)
}

private fun registerFieldValueTypeMismatch(
coroutineScope: CoroutineScope,
problems: ProblemsHolder,
warningInfo: FieldCheckWarning.FieldValueTypeMismatch<PsiElement>,
formatter: DialectFormatter,
query: Node<PsiElement>
) {
val expectedType = formatter.formatType(warningInfo.fieldType)
val actualType = formatter.formatType(warningInfo.valueType)

val problemDescription = InspectionsAndInlaysMessages.message(
"inspection.field.checking.error.message.value.type.mismatch",
formatter.formatType(warningInfo.valueType),
formatter.formatType(warningInfo.fieldType),
actualType,
expectedType,
warningInfo.field,
)
problems.registerProblem(
Expand All @@ -163,5 +187,12 @@ internal object FieldCheckLinterInspection : MongoDbInspection {
),
),
)

val probe by service<InspectionStatusChangedProbe>()
probe.typeMismatchInspectionActive(
query,
actualType,
expectedType,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,24 @@ import com.mongodb.jbplugin.linting.IndexCheckWarning
import com.mongodb.jbplugin.linting.IndexCheckingLinter
import com.mongodb.jbplugin.meta.service
import com.mongodb.jbplugin.mql.Node
import com.mongodb.jbplugin.observability.TelemetryEvent
import com.mongodb.jbplugin.observability.probe.CreateIndexIntentionProbe
import com.mongodb.jbplugin.observability.probe.InspectionStatusChangedProbe
import kotlinx.coroutines.CoroutineScope

/**
* @param coroutineScope
*/
class IndexCheckInspectionBridge(coroutineScope: CoroutineScope) :
AbstractMongoDbInspectionBridge(
coroutineScope,
IndexCheckLinterInspection,
)
) {
override fun emitFinishedInspectionTelemetryEvent(problemsHolder: ProblemsHolder) {
val probe by service<InspectionStatusChangedProbe>()
probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.FIELD_DOES_NOT_EXIST,
problemsHolder,
)
}
}

/**
* This inspection object calls the linting engine and transforms the result so they can be rendered in the IntelliJ
Expand Down Expand Up @@ -70,6 +77,12 @@ internal object IndexCheckLinterInspection : MongoDbInspection {
"inspection.index.checking.error.query.not.covered.by.index",
)

val probe by service<InspectionStatusChangedProbe>()
probe.inspectionChanged(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.QUERY_NOT_COVERED_BY_INDEX,
query
)

problems.registerProblem(
query.source,
problemDescription,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,36 @@ import com.mongodb.jbplugin.linting.NamespaceCheckWarning
import com.mongodb.jbplugin.linting.NamespaceCheckingLinter
import com.mongodb.jbplugin.meta.service
import com.mongodb.jbplugin.mql.Node
import com.mongodb.jbplugin.observability.TelemetryEvent
import com.mongodb.jbplugin.observability.probe.InspectionStatusChangedProbe
import kotlinx.coroutines.CoroutineScope

/**
* @param coroutineScope
*/
@Suppress("MISSING_KDOC_TOP_LEVEL")
class NamespaceCheckInspectionBridge(coroutineScope: CoroutineScope) :
AbstractMongoDbInspectionBridge(
coroutineScope,
NamespaceCheckingLinterInspection,
)
) {
override fun emitFinishedInspectionTelemetryEvent(problemsHolder: ProblemsHolder) {
val probe by service<InspectionStatusChangedProbe>()
probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.NO_NAMESPACE_INFERRED,
problemsHolder
)

probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.DATABASE_DOES_NOT_EXIST,
problemsHolder
)

probe.finishedProcessingInspections(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.COLLECTION_DOES_NOT_EXIST,
problemsHolder
)
}
}

/**
* This inspection object calls the linting engine and transforms the result so they can be rendered in the IntelliJ
Expand Down Expand Up @@ -59,26 +78,40 @@ internal object NamespaceCheckingLinterInspection : MongoDbInspection {
result.warnings.forEach {
when (it) {
is NamespaceCheckWarning.NoNamespaceInferred ->
registerNoNamespaceInferred(coroutineScope, problems, it.source)
registerNoNamespaceInferred(coroutineScope, problems, it.source, query)
is NamespaceCheckWarning.CollectionDoesNotExist ->
registerCollectionDoesNotExist(
coroutineScope,
problems,
it.source,
it.database,
it.collection
it.collection,
query
)
is NamespaceCheckWarning.DatabaseDoesNotExist ->
registerDatabaseDoesNotExist(coroutineScope, problems, it.source, it.database)
registerDatabaseDoesNotExist(
coroutineScope,
problems,
it.source,
it.database,
query
)
}
}
}

private fun registerNoNamespaceInferred(
coroutineScope: CoroutineScope,
problems: ProblemsHolder,
source: PsiElement
source: PsiElement,
query: Node<PsiElement>
) {
val probe by service<InspectionStatusChangedProbe>()
probe.inspectionChanged(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.NO_NAMESPACE_INFERRED,
query
)

val problemDescription = InspectionsAndInlaysMessages.message(
"inspection.namespace.checking.error.message",
)
Expand All @@ -100,7 +133,14 @@ internal object NamespaceCheckingLinterInspection : MongoDbInspection {
problems: ProblemsHolder,
source: PsiElement,
dbName: String,
query: Node<PsiElement>
) {
val probe by service<InspectionStatusChangedProbe>()
probe.inspectionChanged(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.DATABASE_DOES_NOT_EXIST,
query
)

val problemDescription = InspectionsAndInlaysMessages.message(
"inspection.namespace.checking.error.message.database.missing",
dbName
Expand All @@ -123,8 +163,15 @@ internal object NamespaceCheckingLinterInspection : MongoDbInspection {
problems: ProblemsHolder,
source: PsiElement,
dbName: String,
collName: String
collName: String,
query: Node<PsiElement>
) {
val probe by service<InspectionStatusChangedProbe>()
probe.inspectionChanged(
TelemetryEvent.InspectionStatusChangeEvent.InspectionType.COLLECTION_DOES_NOT_EXIST,
query
)

val problemDescription = InspectionsAndInlaysMessages.message(
"inspection.namespace.checking.error.message.collection.missing",
collName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ enum class TelemetryProperty(
CONSOLE("console"),
TRIGGER_LOCATION("trigger_location"),
SIGNAL_TYPE("signal_type"),
INSPECTION_TYPE("inspection_type"),
ERROR_FIELD_TYPE("error_field_type"),
ACTUAL_ERROR_TYPE("actual_field_type"),
ERROR_STATUS("error_status")
}

enum class SignalType(
Expand Down Expand Up @@ -227,4 +231,36 @@ sealed class TelemetryEvent(
TelemetryProperty.SIGNAL_TYPE to SignalType.MISSING_INDEX.publicName,
)
)

class InspectionStatusChangeEvent(
dialect: HasSourceDialect.DialectName,
inspectionType: InspectionType,
inspectionStatus: InspectionStatus,
actualFieldType: String?,
expectedFieldType: String?,
) : TelemetryEvent(
name = "Inspection",
properties =
mapOf(
TelemetryProperty.DIALECT to dialect.name.lowercase(),
TelemetryProperty.INSPECTION_TYPE to inspectionType.name.lowercase(),
TelemetryProperty.ERROR_STATUS to inspectionStatus.name.lowercase(),
TelemetryProperty.ERROR_FIELD_TYPE to (actualFieldType ?: "<none>"),
TelemetryProperty.ACTUAL_ERROR_TYPE to (expectedFieldType ?: "<none>"),
)
) {
enum class InspectionType {
FIELD_DOES_NOT_EXIST,
TYPE_MISMATCH,
QUERY_NOT_COVERED_BY_INDEX,
NO_NAMESPACE_INFERRED,
COLLECTION_DOES_NOT_EXIST,
DATABASE_DOES_NOT_EXIST
}

enum class InspectionStatus {
ACTIVE,
RESOLVED
}
}
}
Loading

0 comments on commit 845d0dc

Please sign in to comment.