diff --git a/.idea/libraries/io_sentry.xml b/.idea/libraries/io_sentry.xml
new file mode 100644
index 000000000000..79d0127724dd
--- /dev/null
+++ b/.idea/libraries/io_sentry.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ 927f0dfd76c9262f3d755345d72468138ae1bdba5d8f20c44f79ba2e1efcdb91
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/platform-impl/intellij.platform.ide.impl.iml b/platform/platform-impl/intellij.platform.ide.impl.iml
index 3b95af8aba74..a2c372af3653 100644
--- a/platform/platform-impl/intellij.platform.ide.impl.iml
+++ b/platform/platform-impl/intellij.platform.ide.impl.iml
@@ -141,5 +141,6 @@
+
\ No newline at end of file
diff --git a/platform/platform-impl/resources/messages/DiagnosticBundle.properties b/platform/platform-impl/resources/messages/DiagnosticBundle.properties
index b4e7008275ae..f879b82f088a 100644
--- a/platform/platform-impl/resources/messages/DiagnosticBundle.properties
+++ b/platform/platform-impl/resources/messages/DiagnosticBundle.properties
@@ -22,28 +22,28 @@ error.list.message.submitted=Submitted
error.list.message.submitted.as.link=Submitted as {1}
error.list.message.submission.failed=Submission failed
error.list.message.submission.failed.details=Submission failed ({0})
-error.dialog.foreign.plugin.warning=This plugin is not a production of JetBrains. Please report the problem to {0}.
-error.dialog.foreign.plugin.warning.unnamed=This plugin is not a production of JetBrains. Please report the problem to the plugin vendor.
-error.dialog.foreign.plugin.warning.unknown=This plugin is not a production of JetBrains. Please report the problem to the plugin vendor.
+error.dialog.foreign.plugin.warning=This plugin is not a production of OpenIDE. Please report the problem to {0}.
+error.dialog.foreign.plugin.warning.unnamed=This plugin is not a production of OpenIDE. Please report the problem to the plugin vendor.
+error.dialog.foreign.plugin.warning.unknown=This plugin is not a production of OpenIDE. Please report the problem to the plugin vendor.
error.dialog.comment.prompt=Comment on what you were doing when the exception occurred
error.dialog.notice.label=By submitting this report, you agree to the terms and conditions of the privacy policy
error.dialog.notice.label.expanded=By submitting this report, you agree to the terms and conditions of the privacy policy
error.dialog.submit.anonymous=Submit anonymously or Log In
error.dialog.submit.named=Submit as {0} or change account
-error.report.to.jetbrains.action=&Report to JetBrains
+error.report.to.jetbrains.action=&Report to OpenIDE
error.report.all.action=Report All
error.report.and.clear.all.action=Report and Clear All
error.report.impossible.action=Report Exception
error.report.impossible.tooltip=The plugin vendor didn't implement the exception reporting component.
error.dialog.clear.all.action=&Clear all
error.dialog.notice.anonymous=I agree to my hardware configuration, software configuration, product information, and the error details shown above \
- being used by JetBrains s.r.o. ("JetBrains") to let JetBrains improve its products and to provide me with support service \
- in accordance with the end-user agreement of JetBrains Exception Analyzer.
+ being used by OpenIDE to let OpenIDE improve its products and to provide me with support service \
+ in accordance with the end-user agreement of OpenIDE Exception Analyzer.
error.dialog.notice.third-party.plugin.exception=I agree that my hardware configuration, software configuration, product information, and the error details shown above\
- \ might be used by Jetbrains s.r.o. (\u201CJetBrains\u201D) and the author of the plugin to improve their products and provide me with support service by JetBrains\
- \ in accordance with the relevant Agreement.\
- \ If you submit the report containing any personal data, please note that such data will be shared with JetBrains and the plugin author.\
- \ JetBrains is not responsible for any behaviour of any third-party plugins and their vendors, including processing of your personal data.
+ \ might be used by OpenIDE and the author of the plugin to improve their products and provide me with support service by OpenIDE\
+ \ in accordance with the relevant Agreement.\
+ \ If you submit the report containing any personal data, please note that such data will be shared with OpenIDE and the plugin author.\
+ \ OpenIDE is not responsible for any behaviour of any third-party plugins and their vendors, including processing of your personal data.
error.dialog.notice.third-party.plugin.send=Report to the Third-Party Plugin
title.submitting.error.report=Submitting error report
error.report.submitted=Report submitted
diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.kt b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.kt
index 37f98fd441d8..4e56cc836237 100644
--- a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.kt
+++ b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.kt
@@ -94,16 +94,15 @@ open class ITNReporter(private val postUrl: String = "") : ErrorReportSubmitter(
private fun submit(project: Project?, errorBean: ErrorBean, parentComponent: Component, callback: (SubmittedReportInfo) -> Unit): Boolean {
service().coroutineScope.launch {
try {
- val reportId = if (project != null) {
+ if (project != null) {
withBackgroundProgress(project, DiagnosticBundle.message("title.submitting.error.report")) {
- ITNProxy.sendError(errorBean, postUrl)
+ ITNUtils.sendError(errorBean)
}
}
else {
- ITNProxy.sendError(errorBean, postUrl)
+ ITNUtils.sendError(errorBean)
}
- updatePreviousReport(errorBean.event, reportId)
- onSuccess(project, reportId, callback)
+ onSuccess(project, callback)
}
catch (e: Exception) {
onError(project, e, errorBean, parentComponent, callback)
@@ -112,15 +111,11 @@ open class ITNReporter(private val postUrl: String = "") : ErrorReportSubmitter(
return true
}
- private fun onSuccess(project: Project?, reportId: Int, callback: (SubmittedReportInfo) -> Unit) {
- val reportUrl = ITNProxy.getBrowseUrl(reportId)
- callback(SubmittedReportInfo(reportUrl, reportId.toString(), SubmittedReportInfo.SubmissionStatus.NEW_ISSUE))
+ private fun onSuccess(project: Project?, callback: (SubmittedReportInfo) -> Unit) {
+ callback(SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.NEW_ISSUE))
val content = DiagnosticBundle.message("error.report.gratitude")
val title = DiagnosticBundle.message("error.report.submitted")
val notification = Notification("Error Report", title, content, NotificationType.INFORMATION).setImportant(false)
- if (reportUrl != null) {
- notification.addAction(NotificationAction.createSimpleExpiring(DiagnosticBundle.message("error.report.view.action")) { BrowserUtil.browse(reportUrl) })
- }
notification.notify(project)
}
diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ITNUtils.kt b/platform/platform-impl/src/com/intellij/diagnostic/ITNUtils.kt
new file mode 100644
index 000000000000..25b721082c17
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/diagnostic/ITNUtils.kt
@@ -0,0 +1,118 @@
+// Copyright (c) Haulmont 2024. All Rights Reserved.
+// Use is subject to license terms.
+package com.intellij.diagnostic
+
+import com.intellij.ide.ApplicationActivity
+import com.intellij.idea.IdeaLogger
+import com.intellij.openapi.application.ApplicationInfo
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.util.SystemInfo
+import com.intellij.openapi.util.io.FileUtilRt
+import com.intellij.openapi.util.text.StringUtil
+import com.intellij.util.ObjectUtils
+import io.sentry.Sentry
+import io.sentry.SentryEvent
+import io.sentry.SentryLevel
+import io.sentry.SentryOptions
+import io.sentry.protocol.SentryRuntime
+import io.sentry.protocol.User
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+import java.util.concurrent.atomic.AtomicBoolean
+
+object ITNUtils {
+ val log = Logger.getInstance(ITNUtils::class.java)
+
+ internal fun sendError(error: ITNProxy.ErrorBean) {
+ val event = SentryEvent()
+ event.level = SentryLevel.ERROR
+ event.throwable = (error.event as IdeaReportingEvent).data.throwable
+ event.release = ApplicationInfo.getInstance().build.asString()
+
+ event.user = User().apply {
+ id = uniqueComputerId
+ }
+
+ // set server name to empty to avoid tracking personal data
+ event.serverName = SystemInfo.getOsNameAndVersion()
+ event.setExtra("ide.is_eap", ApplicationManager.getApplication().isEAP)
+ event.setExtra("ide.is_internal", ApplicationManager.getApplication().isInternal)
+
+ val sentryRuntime = SentryRuntime()
+ val javaVendor = System.getProperty("java.vendor", "")
+ val javaVersion = System.getProperty("java.version", "")
+ sentryRuntime.name = "$javaVendor $javaVersion"
+ event.contexts.setRuntime(sentryRuntime)
+
+ val lastActionId = IdeaLogger.ourLastActionId
+ if (StringUtil.isNotEmpty(lastActionId)) {
+ event.setExtra("Last action", lastActionId)
+ }
+
+ if (StringUtil.isNotEmpty(error.pluginId)) {
+ event.setExtra("plugin.id", error.pluginId!!)
+ }
+
+ if (StringUtil.isNotEmpty(error.pluginVersion)) {
+ event.setExtra("plugin.version", error.pluginVersion!!)
+ }
+
+ if (StringUtil.isNotEmpty(error.pluginName)) {
+ event.setExtra("plugin.name", error.pluginName!!)
+ }
+
+ event.setExtra("Memory", (Runtime.getRuntime().maxMemory() / FileUtilRt.MEGABYTE).toString() + "M")
+
+ if (SystemInfo.isUnix && !SystemInfo.isMac) {
+ event.setExtra("Current Desktop", ObjectUtils.notNull(System.getenv("XDG_CURRENT_DESKTOP"), "Undefined"))
+ }
+
+ Sentry.captureEvent(event)
+ }
+
+ val uniqueComputerId: String get() = UniqueComputerId.value
+
+ fun getEnvironment() = when {
+ isSnapshot() -> "snapshot"
+ else -> "production"
+ }
+
+ private fun isSnapshot() = ApplicationInfo.getInstance().build.asString()
+ .contains("snapshot", ignoreCase = true)
+}
+
+/**
+ * Makes Sentry initialization.
+ */
+class SentryInitializer : ApplicationActivity {
+
+ private val isHeadless by lazy {
+ ApplicationManager.getApplication().run { isHeadlessEnvironment || isUnitTestMode }
+ }
+
+ private val isInitialized = AtomicBoolean(false)
+
+ override suspend fun execute() {
+ if (isHeadless) return
+
+ val isInitializedBefore = isInitialized.getAndSet(true)
+ if (isInitializedBefore) return
+
+ initComputerId()
+
+ Sentry.init { options: SentryOptions ->
+ options.dsn = "https://73728607f7e2a336e7cade498caaae0c@sentry-amp.haulmont.com/4"
+ options.isEnableUncaughtExceptionHandler = false
+ options.setModulesLoader { emptyMap() }
+ options.environment = ITNUtils.getEnvironment()
+ }
+ }
+
+ private suspend fun initComputerId() = coroutineScope {
+ launch(Dispatchers.IO) {
+ UniqueComputerId.precompute()
+ }
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/diagnostic/UniqueComputerId.kt b/platform/platform-impl/src/com/intellij/diagnostic/UniqueComputerId.kt
new file mode 100644
index 000000000000..af7458a8d8cf
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/diagnostic/UniqueComputerId.kt
@@ -0,0 +1,71 @@
+// Copyright (c) Haulmont 2024. All Rights Reserved.
+// Use is subject to license terms.
+package com.intellij.diagnostic
+
+import com.intellij.internal.statistic.eventLog.fus.MachineIdManager
+import com.intellij.openapi.diagnostic.logger
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.util.SystemProperties
+import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
+import com.intellij.util.io.DigestUtil
+import com.intellij.util.io.bytesToHex
+import java.io.File
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+import java.util.*
+
+object UniqueComputerId {
+
+ private val log = logger()
+
+ @RequiresBackgroundThread
+ fun precompute() {
+ // call our lazy value
+ value
+ }
+
+ val value by lazy {
+ val configFile = resolveUserIdFile()
+ loadIdFromFile(configFile) ?: generateIdAndWrite(configFile)
+ }
+
+ private fun loadIdFromFile(configFile: File): String? {
+ if (!configFile.exists()) {
+ return null
+ }
+ return try {
+ val fileSource = FileUtil.loadFile(configFile, StandardCharsets.UTF_8)
+ fileSource.trim().ifEmpty { null }
+ } catch (e: IOException) {
+ log.warn("Exception during read user id", e)
+ null
+ }
+ }
+
+ private fun generateIdAndWrite(configFile: File): String {
+ val id = getAnonymizedMachineId()
+ try {
+ FileUtil.writeToFile(configFile, id, StandardCharsets.UTF_8)
+ } catch (e: IOException) {
+ log.warn("Exception during write user id", e)
+ }
+ return id
+ }
+
+ private fun getAnonymizedMachineId(): String {
+ var machineId = MachineIdManager.getAnonymizedMachineId("openide.unique.computer.id")
+ if (machineId != null) {
+ return machineId
+ }
+ log.warn("Getting machine id was failed")
+ machineId = UUID.randomUUID().toString()
+ val result = "${System.getProperty("user.name")}-$machineId"
+ val md = DigestUtil.sha256()
+ md.update(result.toByteArray())
+ return bytesToHex(md.digest())
+ }
+
+ private fun resolveUserIdFile(): File {
+ return File(SystemProperties.getUserHome(), ".openide/user-id.txt")
+ }
+}
\ No newline at end of file
diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
index c3174a0ad69f..ed2392b33fa5 100644
--- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml
+++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml
@@ -1870,6 +1870,7 @@
+