mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
OPENIDE #49 Implement sending default errors from OpenIDE
(cherry picked from commit 31c0cf21f935f6379cf458e5fab0f211cb6c5aa7) (cherry picked from commit2e3d348b7c) (cherry picked from commit690310ef3c) (cherry picked from commit9428cb6790) (cherry picked from commit8e8bc23388)
This commit is contained in:
Generated
+18
@@ -0,0 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.sentry" type="repository">
|
||||
<properties maven-id="io.sentry:sentry:7.8.0">
|
||||
<verification>
|
||||
<artifact url="file://$MAVEN_REPOSITORY$/io/sentry/sentry/7.8.0/sentry-7.8.0.jar">
|
||||
<sha256sum>927f0dfd76c9262f3d755345d72468138ae1bdba5d8f20c44f79ba2e1efcdb91</sha256sum>
|
||||
</artifact>
|
||||
</verification>
|
||||
</properties>
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/sentry/sentry/7.8.0/sentry-7.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/sentry/sentry/7.8.0/sentry-7.8.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -176,5 +176,6 @@
|
||||
<orderEntry type="module" module-name="intellij.java.guiForms.rt" />
|
||||
<orderEntry type="module" module-name="fleet.rhizomedb" />
|
||||
<orderEntry type="library" name="jetbrains.intellij.deps.java.atk.wrapper.linux" level="project" />
|
||||
<orderEntry type="library" name="io.sentry" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -23,28 +23,28 @@ error.list.message.submitted=Submitted
|
||||
error.list.message.submitted.as.link=Submitted as <a href="{0}">{1}</a>
|
||||
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 <a href="{1}">{0}</a>.
|
||||
error.dialog.foreign.plugin.warning.unnamed=This plugin is not a production of JetBrains. Please report the problem to the <a href="{0}">plugin vendor</a>.
|
||||
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 <a href="{1}">{0}</a>.
|
||||
error.dialog.foreign.plugin.warning.unnamed=This plugin is not a production of OpenIDE. Please report the problem to the <a href="{0}">plugin vendor</a>.
|
||||
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 <a href="#">Log In</a>
|
||||
error.dialog.submit.named=Submit as <b>{0}</b> or <a href="#">change account</a>
|
||||
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 <a href="https://www.jetbrains.com/legal/agreements/exception_analyzer.html">JetBrains Exception Analyzer</a>.
|
||||
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 <a href="https://openide.ru/legal/agreements/exception_analyzer.html">OpenIDE Exception Analyzer</a>.
|
||||
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 <a href="https://www.jetbrains.com/legal/agreements/exception_analyzer.html">Agreement</a>.\
|
||||
\ 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 <a href="https://openide.ru/legal/agreements/exception_analyzer.html">Agreement</a>.\
|
||||
\ 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
|
||||
|
||||
@@ -119,9 +119,6 @@ open class ITNReporter internal constructor(private val postUrl: URI?) : ErrorRe
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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", "<unknown vendor>")
|
||||
val javaVersion = System.getProperty("java.version", "<unknown 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<UniqueComputerId>()
|
||||
|
||||
@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")
|
||||
}
|
||||
}
|
||||
@@ -2081,6 +2081,7 @@ The refresh itself is toggled by (Preferences | Appearance & Behavior | Syst
|
||||
<actionGroupCustomization
|
||||
implementation="com.intellij.openapi.wm.impl.headertoolbar.MainToolbarActionGroupCustomization" />
|
||||
|
||||
<applicationActivity implementation="com.intellij.diagnostic.SentryInitializer"/>
|
||||
<applicationService serviceInterface="com.intellij.platform.ide.provisioner.ProvisionedServiceRegistry"
|
||||
serviceImplementation="com.intellij.platform.ide.provisioner.DefaultProvisionedServiceRegistry"/>
|
||||
<applicationService serviceInterface="com.intellij.platform.ide.provisioner.ProvisionerCompanyBrandingProvider"
|
||||
|
||||
Reference in New Issue
Block a user