[platform] IJPL-148733: Unify logging for warmup and qodana

GitOrigin-RevId: 2d18e232718583f93230dd2cd91b453d24c28e04
This commit is contained in:
Konstantin Nisht
2024-04-30 17:23:22 +02:00
committed by intellij-monorepo-bot
parent a758147396
commit 432cee2d40
25 changed files with 508 additions and 333 deletions

View File

@@ -26,5 +26,6 @@
<orderEntry type="module" module-name="intellij.platform.backend.workspace" />
<orderEntry type="module" module-name="intellij.platform.diagnostic" />
<orderEntry type="library" name="hash4j" level="project" />
<orderEntry type="module" module-name="intellij.platform.backend.observation" />
</component>
</module>

View File

@@ -28883,8 +28883,6 @@ f:com.intellij.util.PatternValuesIndex
- <init>():V
- s:buildStringIndex(java.util.Collection):java.util.Set
- s:processStringValues(java.util.Collection,com.intellij.util.PairProcessor):Z
f:com.intellij.util.ProjectConfigurationUtil
- sf:awaitCompleteProjectConfiguration(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
a:com.intellij.util.TextFieldCompletionProvider
- com.intellij.openapi.project.PossiblyDumbAware
- com.intellij.util.textCompletion.TextCompletionProvider

View File

@@ -1411,17 +1411,6 @@ com.intellij.ide.warmup.WarmupConfigurator
- a:runWarmup(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
f:com.intellij.ide.warmup.WarmupConfigurator$Companion
- f:getEP_NAME():com.intellij.openapi.extensions.ExtensionPointName
com.intellij.ide.warmup.WarmupLogger
- sf:Companion:com.intellij.ide.warmup.WarmupLogger$Companion
- a:logError(java.lang.String,java.lang.Throwable):V
- a:logFatalError(java.lang.String,java.lang.Throwable):V
- a:logInfo(java.lang.String):V
f:com.intellij.ide.warmup.WarmupLogger$Companion
- f:error(java.lang.String,java.lang.Throwable):V
- bs:error$default(com.intellij.ide.warmup.WarmupLogger$Companion,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:fatalError(java.lang.String,java.lang.Throwable):V
- bs:fatalError$default(com.intellij.ide.warmup.WarmupLogger$Companion,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:message(java.lang.String):V
com.intellij.ide.warmup.WarmupStatus
- sf:Companion:com.intellij.ide.warmup.WarmupStatus$Companion
f:com.intellij.ide.warmup.WarmupStatus$Companion
@@ -3494,6 +3483,71 @@ a:com.intellij.openapi.project.ProjectReloadState
- s:getInstance(com.intellij.openapi.project.Project):com.intellij.openapi.project.ProjectReloadState
- a:isAfterAutomaticReload():Z
- a:onBeforeAutomaticProjectReload():V
f:com.intellij.openapi.project.configuration.HeadlessLogging
- sf:INSTANCE:com.intellij.openapi.project.configuration.HeadlessLogging
- f:logFatalError(java.lang.String):V
- f:logFatalError(java.lang.Throwable):V
- f:logMessage(java.lang.String):V
- f:logWarning(java.lang.String):V
- f:logWarning(java.lang.Throwable):V
- f:loggingFlow():kotlinx.coroutines.flow.SharedFlow
com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
- sf:Companion:com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService$Companion
- a:logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
- a:loggingFlow():kotlinx.coroutines.flow.SharedFlow
f:com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService$Companion
- f:getInstance():com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
f:com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
- <init>(com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message):V
- f:component1():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- f:component2():com.intellij.openapi.project.configuration.HeadlessLogging$Message
- f:copy(com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message):com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
- bs:copy$default(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry,com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind,com.intellij.openapi.project.configuration.HeadlessLogging$Message,I,java.lang.Object):com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry
- equals(java.lang.Object):Z
- f:getMessage():com.intellij.openapi.project.configuration.HeadlessLogging$Message
- f:getSeverity():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- hashCode():I
- toString():java.lang.String
com.intellij.openapi.project.configuration.HeadlessLogging$Message
- a:representation():java.lang.String
f:com.intellij.openapi.project.configuration.HeadlessLogging$Message$Exception
- com.intellij.openapi.project.configuration.HeadlessLogging$Message
- bsf:box-impl(java.lang.Throwable):com.intellij.openapi.project.configuration.HeadlessLogging$Message$Exception
- s:constructor-impl(java.lang.Throwable):java.lang.Throwable
- equals(java.lang.Object):Z
- s:equals-impl(java.lang.Throwable,java.lang.Object):Z
- sf:equals-impl0(java.lang.Throwable,java.lang.Throwable):Z
- f:getException():java.lang.Throwable
- hashCode():I
- s:hashCode-impl(java.lang.Throwable):I
- representation():java.lang.String
- s:representation-impl(java.lang.Throwable):java.lang.String
- toString():java.lang.String
- s:toString-impl(java.lang.Throwable):java.lang.String
- bf:unbox-impl():java.lang.Throwable
f:com.intellij.openapi.project.configuration.HeadlessLogging$Message$Plain
- com.intellij.openapi.project.configuration.HeadlessLogging$Message
- bsf:box-impl(java.lang.String):com.intellij.openapi.project.configuration.HeadlessLogging$Message$Plain
- s:constructor-impl(java.lang.String):java.lang.String
- equals(java.lang.Object):Z
- s:equals-impl(java.lang.String,java.lang.Object):Z
- sf:equals-impl0(java.lang.String,java.lang.String):Z
- f:getMessage():java.lang.String
- hashCode():I
- s:hashCode-impl(java.lang.String):I
- representation():java.lang.String
- s:representation-impl(java.lang.String):java.lang.String
- toString():java.lang.String
- s:toString-impl(java.lang.String):java.lang.String
- bf:unbox-impl():java.lang.String
e:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- java.lang.Enum
- sf:Fatal:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- sf:Info:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- sf:Warning:com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- s:getEntries():kotlin.enums.EnumEntries
- s:valueOf(java.lang.String):com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind
- s:values():com.intellij.openapi.project.configuration.HeadlessLogging$SeverityKind[]
com.intellij.openapi.project.impl.ProjectLifecycleListener
- sf:TOPIC:com.intellij.util.messages.Topic
- afterProjectClosed(com.intellij.openapi.project.Project):V

View File

@@ -1,48 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.warmup
import com.intellij.openapi.extensions.ExtensionPointName
/**
* This class is used when warmup decides to relay important information to the user.
*/
interface WarmupLogger {
/**
* Allows reporting information messages.
*/
fun logInfo(message: String)
/**
* Allows reporting error messages to the user.
* Use this if there is an important error, which is still recoverable.
*/
fun logError(message: String, throwable: Throwable?)
/**
* Allows aborting the process of warmup if something non-recoverable happens.
*/
fun logFatalError(message: String, throwable: Throwable?)
companion object {
private val EP_NAME: ExtensionPointName<WarmupLogger> = ExtensionPointName("com.intellij.warmupLogger")
fun message(message: String) {
for (warmupLogger in EP_NAME.extensionList) {
warmupLogger.logInfo(message)
}
}
fun error(message: String, throwable: Throwable? = null) {
for (warmupLogger in EP_NAME.extensionList) {
warmupLogger.logError(message, throwable)
}
}
fun fatalError(message: String, throwable: Throwable? = null) {
for (warmupLogger in EP_NAME.extensionList) {
warmupLogger.logFatalError(message, throwable)
}
}
}
}

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.warmup
import com.intellij.openapi.application.Application
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.util.Key
import org.jetbrains.annotations.ApiStatus.Internal

View File

@@ -0,0 +1,89 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.configuration
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.configuration.HeadlessLogging.Message.Plain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import javax.print.attribute.standard.Severity
/**
* A sink for information that may be useful during command-line execution of the IDE.
*
* Each subsystem can use the methods in this class to send some information that may be of value to the end user.
*
* We do not provide a distinction between the severity levels here, as all the reported messages are of interest for the user.
* Severity differentiation makes sense for investigation of internal problems, and for this reason we have internal logging.
*/
object HeadlessLogging {
/**
* Reports an informational message about the IDE.
*
* This kind of messages is important to keep track of progress and to assure the user that the execution is not stuck.
*/
fun logMessage(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Info, Plain(message)))
/**
* Reports a warning message.
*
* The collector of the message can decide to react differently to warnings, i.e., print them in different colors in the console,
* or collect and report them batched at the end of the configuration.
*/
fun logWarning(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Warning, Plain(message)))
/**
* Reports a non-fatal exception. An exception is non-fatal if the IDE can recover from it.
*
* This kind of error does not stop the execution of the IDE.
*/
fun logWarning(exception: Throwable) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Warning, Message.Exception(exception)))
/**
* Reports a fatal error during the execution. An error is fatal if the user's actions are required in order to fix the problem.
*
* This kind of errors **has influence on control flow**: once the IDE reports a fatal error,
* the headless execution may stop. An example of this error is a failure in the build system import.
* This
*/
fun logFatalError(exception: Throwable) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Fatal, Message.Exception(exception)))
fun logFatalError(message: String) = HeadlessLoggingService.getInstance().logEntry(LogEntry(SeverityKind.Fatal, Plain(message)))
/**
* Retrieves a hot flow with messages about the IDE.
*/
fun loggingFlow(): SharedFlow<LogEntry> = HeadlessLoggingService.getInstance().loggingFlow()
enum class SeverityKind {
Info,
Warning,
Fatal
}
sealed interface Message {
fun representation(): String
@JvmInline
value class Plain(val message: String) : Message {
override fun representation(): String = message
}
@JvmInline
value class Exception(val exception: Throwable) : Message {
override fun representation(): String = exception.toString()
}
}
data class LogEntry(val severity: SeverityKind, val message: Message)
interface HeadlessLoggingService {
companion object {
fun getInstance(): HeadlessLoggingService {
return ApplicationManager.getApplication().getService(HeadlessLoggingService::class.java)
}
}
fun logEntry(logEntry: LogEntry)
fun loggingFlow(): SharedFlow<LogEntry>
}
}

View File

@@ -18065,6 +18065,29 @@ f:com.intellij.openapi.project.ScanningTracker
- awaitConfiguration(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
- getPresentableName():java.lang.String
- isInProgress(com.intellij.openapi.project.Project,kotlin.coroutines.Continuation):java.lang.Object
f:com.intellij.openapi.project.configuration.ChannelingProgressIndicator
- com.intellij.openapi.progress.util.ProgressIndicatorBase
- <init>(java.lang.String):V
- setFraction(D):V
- setIndeterminate(Z):V
- setText(java.lang.String):V
- setText2(java.lang.String):V
f:com.intellij.openapi.project.configuration.EmptyLoggingService
- com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
- <init>():V
- logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
- loggingFlow():kotlinx.coroutines.flow.SharedFlow
f:com.intellij.openapi.project.configuration.HeadlessLoggingServiceImpl
- com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService
- <init>():V
- logEntry(com.intellij.openapi.project.configuration.HeadlessLogging$LogEntry):V
- loggingFlow():kotlinx.coroutines.flow.SharedFlow
f:com.intellij.openapi.project.configuration.HeadlessProgressListener
- <init>():V
- afterTaskFinished(com.intellij.openapi.progress.Task):V
- beforeTaskStart(com.intellij.openapi.progress.Task,com.intellij.openapi.progress.ProgressIndicator):V
f:com.intellij.openapi.project.configuration.ProjectConfigurationUtil
- sf:awaitCompleteProjectConfiguration(com.intellij.openapi.project.Project,kotlin.jvm.functions.Function1,kotlin.coroutines.Continuation):java.lang.Object
a:com.intellij.openapi.project.impl.DefaultProjectTimed
- com.intellij.util.TimedReference
- dispose():V

View File

@@ -7,6 +7,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,7 +18,9 @@ import java.util.function.Predicate;
/**
* Extension point that helps prepare project for opening in headless or automated environments.
* Implementation must be stateless.
* Consider using com.intellij.platform.backend.observation.ActivityTracker
*/
@ApiStatus.Obsolete(since = "2024.1")
public interface CommandLineInspectionProjectConfigurator {
ExtensionPointName<CommandLineInspectionProjectConfigurator> EP_NAME = ExtensionPointName.create("com.intellij.commandLineInspectionProjectConfigurator");

View File

@@ -6,9 +6,9 @@ import com.fasterxml.jackson.databind.type.CollectionType
import com.intellij.ide.environment.EnvironmentKey
import com.intellij.ide.environment.EnvironmentService
import com.intellij.ide.environment.description
import com.intellij.ide.warmup.WarmupLogger
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.configuration.HeadlessLogging
import kotlinx.coroutines.*
import java.io.IOException
@@ -22,7 +22,8 @@ class HeadlessEnvironmentService(scope: CoroutineScope) : BaseEnvironmentService
return getEnvironmentValueOrNull(key)
?: run {
val throwable = MissingEnvironmentKeyException(key)
WarmupLogger.fatalError("Insufficient project configuration", MissingEnvironmentKeyException(key))
LOG.error(throwable)
HeadlessLogging.logFatalError(MissingEnvironmentKeyException(key))
throw throwable
}
}

View File

@@ -0,0 +1,67 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.configuration
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import java.util.concurrent.atomic.AtomicReference
class ChannelingProgressIndicator(private val prefix: String) : ProgressIndicatorBase() {
override fun setIndeterminate(indeterminate: Boolean) {
super.setIndeterminate(indeterminate)
}
private val lastState = AtomicReference(0.0)
override fun setFraction(fraction: Double) {
super.setFraction(fraction)
val lastStateValue = lastState.get()
if (fraction - lastStateValue > 0.2) {
// we debounce the messages by 20%
if (lastState.compareAndSet(lastStateValue, fraction)) {
offerState()
}
}
}
override fun setText(text: String?) {
super.setText(text)
super.setText2("")
offerState()
}
override fun setText2(text: String?) {
super.setText2(text)
}
private fun trimProgressTextAndNullize(s: String?) = s?.trim()?.trimEnd('.', '\u2026', ' ')?.takeIf { it.isNotBlank() }
private fun progressStateText(fraction: Double?, text: String?, details: String?): String? {
val text = trimProgressTextAndNullize(text)
val text2 = trimProgressTextAndNullize(details)
if (text.isNullOrBlank() && text2.isNullOrBlank()) {
return null
}
val shortText = text ?: ""
val verboseText = shortText + (text2?.let { " ($it)" } ?: "")
if (shortText.isBlank() || fraction == null) {
return verboseText
}
val v = (100.0 * fraction).toInt()
val total = 18
val completed = (total * fraction).toInt().coerceAtLeast(0)
val d = ".".repeat(completed).padEnd(total, ' ')
val verboseReport = verboseText.take(100).padEnd(105) + "$d $v%"
return verboseReport
}
private fun offerState() {
val progressState = progressStateText(
fraction = if (isIndeterminate) null else fraction,
text = text,
details = text2,
) ?: return
val actualPrefix = if (prefix.isEmpty()) "" else "[$prefix]: "
HeadlessLogging.logMessage(actualPrefix + progressState)
}
}

View File

@@ -0,0 +1,15 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.configuration
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
class EmptyLoggingService : HeadlessLogging.HeadlessLoggingService {
private val emptyFlow = MutableSharedFlow<HeadlessLogging.LogEntry>()
override fun logEntry(exception: HeadlessLogging.LogEntry) {
}
override fun loggingFlow(): SharedFlow<HeadlessLogging.LogEntry> {
return emptyFlow
}
}

View File

@@ -0,0 +1,36 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.configuration
import com.intellij.openapi.diagnostic.thisLogger
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
class HeadlessLoggingServiceImpl : HeadlessLogging.HeadlessLoggingService {
private val flow: MutableSharedFlow<HeadlessLogging.LogEntry> = MutableSharedFlow(replay = 0, extraBufferCapacity = 1024, onBufferOverflow = BufferOverflow.SUSPEND)
override fun logEntry(logEntry: HeadlessLogging.LogEntry) {
emitLogEntry(logEntry)
}
private fun emitLogEntry(entry: HeadlessLogging.LogEntry) {
var internalLoggingPerformed = false
while (!flow.tryEmit(entry)) {
// we have some slow collectors
// there is nothing the platform can do, other than report this incident
if (!internalLoggingPerformed) {
thisLogger().warn("Cannot log message: ${entry}. \n" +
"Headless logger has exhausted the buffer of messages. Please speed up the listeners of the Headless logger.")
internalLoggingPerformed = true
}
// Nevertheless, it is important to deliver complete information to the user.
Thread.sleep(100)
}
}
override fun loggingFlow(): SharedFlow<HeadlessLogging.LogEntry> {
return flow
}
}

View File

@@ -0,0 +1,32 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.project.configuration
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManagerListener
import com.intellij.openapi.progress.Task
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import com.intellij.openapi.util.text.Formats
import com.intellij.openapi.wm.ex.ProgressIndicatorEx
import java.util.concurrent.ConcurrentHashMap
class HeadlessProgressListener : ProgressManagerListener {
private val taskDurationMap = ConcurrentHashMap<Int, Long>()
override fun beforeTaskStart(task: Task, indicator: ProgressIndicator) {
if (indicator !is ProgressIndicatorEx) {
return
}
HeadlessLogging.logMessage("[IDE]: Task '${task.title}' started")
taskDurationMap[System.identityHashCode(task)] = System.currentTimeMillis()
indicator.addStateDelegate(ChannelingProgressIndicator("IDE"))
super.beforeTaskStart(task, indicator)
}
override fun afterTaskFinished(task: Task) {
val currentTime = System.currentTimeMillis()
val startTime = taskDurationMap.remove(System.identityHashCode(task))
val elapsedTimeSuffix = if (startTime == null) "" else " in ${Formats.formatDuration(currentTime - startTime)}"
HeadlessLogging.logMessage("[IDE]: Task '${task.title}' ended" + elapsedTimeSuffix)
}
}

View File

@@ -1,9 +1,10 @@
@file:JvmName("ProjectConfigurationUtil")
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util
@file:JvmName("ProjectConfigurationUtil")
package com.intellij.openapi.project.configuration
import com.intellij.configurationStore.StoreUtil.saveSettings
import com.intellij.configurationStore.saveProjectsAndApp
import com.intellij.configurationStore.saveSettings
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.platform.backend.observation.Observation

View File

@@ -511,7 +511,6 @@
<extensionPoint name="beforeRunStartupTasks" interface="com.intellij.execution.startup.BeforeRunStartupTasks" area="IDEA_PROJECT"/>
<extensionPoint name="warmupConfigurator" interface="com.intellij.ide.warmup.WarmupConfigurator" dynamic="true" />
<extensionPoint name="warmupLogger" interface="com.intellij.ide.warmup.WarmupLogger" dynamic="true" />
<extensionPoint name="activityTracker" interface="com.intellij.platform.backend.observation.ActivityTracker"
dynamic="true"/>

View File

@@ -109,6 +109,10 @@
<ideEventQueueDispatcher implementation="com.intellij.openapi.keymap.impl.ModifierKeyDoubleClickHandler$MyEventDispatcher"/>
<applicationService serviceInterface="com.intellij.openapi.project.configuration.HeadlessLogging$HeadlessLoggingService"
serviceImplementation="com.intellij.openapi.project.configuration.EmptyLoggingService"
headlessImplementation="com.intellij.openapi.project.configuration.HeadlessLoggingServiceImpl"/>
<applicationService serviceInterface="com.intellij.openapi.client.ClientSessionsManager"
serviceImplementation="com.intellij.openapi.client.ClientAppSessionsManager"/>
<projectService serviceInterface="com.intellij.openapi.client.ClientSessionsManager"
@@ -200,6 +204,10 @@
<listener class="com.intellij.ide.ui.html.GlobalStyleSheetUpdateListener"
topic="com.intellij.openapi.editor.colors.EditorColorsListener" activeInHeadlessMode="false"/>
<listener class="com.intellij.openapi.project.configuration.HeadlessProgressListener"
topic="com.intellij.openapi.progress.ProgressManagerListener"
activeInHeadlessMode="true"/>
<listener class="com.intellij.openapi.editor.actions.ResetFontSizeEditorActionHandler"
topic="com.intellij.openapi.editor.colors.EditorColorsListener"/>

View File

@@ -25,12 +25,6 @@ f:com.intellij.warmup.util.ConsoleLog
- bs:error$default(com.intellij.warmup.util.ConsoleLog,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:info(java.lang.String):V
- f:warn(java.lang.String):V
f:com.intellij.warmup.util.DefaultWarmupLogger
- com.intellij.ide.warmup.WarmupLogger
- <init>():V
- logError(java.lang.String,java.lang.Throwable):V
- logFatalError(java.lang.String,java.lang.Throwable):V
- logInfo(java.lang.String):V
com.intellij.warmup.util.HeadlessConfigurableArgs
- a:getPathToConfigurationFile():java.nio.file.Path
c:com.intellij.warmup.util.HeadlessConfigurableArgsImpl
@@ -74,10 +68,6 @@ f:com.intellij.warmup.util.WarmupLogger
- f:logError(java.lang.String,java.lang.Throwable):V
- bs:logError$default(com.intellij.warmup.util.WarmupLogger,java.lang.String,java.lang.Throwable,I,java.lang.Object):V
- f:logInfo(java.lang.String):V
f:com.intellij.warmup.util.WarmupProgressListener
- <init>():V
- afterTaskFinished(com.intellij.openapi.progress.Task):V
- beforeTaskStart(com.intellij.openapi.progress.Task,com.intellij.openapi.progress.ProgressIndicator):V
com.intellij.warmup.util.WarmupProjectArgs
- com.intellij.warmup.util.OpenProjectArgs
- a:getBuild():Z

View File

@@ -6,7 +6,6 @@
<extensions defaultExtensionNs="com.intellij">
<appStarter id="warmup" implementation="com.intellij.warmup.ProjectCachesWarmup"/>
<projectBuildWarmupSupport implementation="com.intellij.warmup.PlatformBuildWarmupSupport"/>
<warmupLogger implementation="com.intellij.warmup.util.DefaultWarmupLogger"/>
</extensions>
</idea-plugin>

View File

@@ -58,7 +58,7 @@ internal class ProjectCachesWarmup : ModernApplicationStarter() {
configureVcsIndexing(commandArgs)
runWarmupActivity {
initLogger(args)
val loggingJob = initLogger(args)
waitIndexInitialization()
val project = try {
importOrOpenProjectAsync(commandArgs)
@@ -79,6 +79,7 @@ internal class ProjectCachesWarmup : ModernApplicationStarter() {
waitForRefreshQueue()
}
ProjectManagerEx.getInstanceEx().forceCloseProjectAsync(project, save = true)
loggingJob.cancel()
}
exitApplication()
@@ -225,7 +226,7 @@ private suspend fun buildProject(project: Project, commandArgs: WarmupProjectArg
}
}
private suspend fun runWarmupActivity(action: suspend () -> Unit) {
private suspend fun runWarmupActivity(action: suspend CoroutineScope.() -> Unit) {
val indexedFiles = installStatisticsCollector()
WarmupStatus.statusChanged(WarmupStatus.InProgress)
try {

View File

@@ -4,14 +4,12 @@ package com.intellij.warmup.util
import com.intellij.ide.impl.OpenProjectTask
import com.intellij.ide.impl.ProjectUtil
import com.intellij.openapi.project.Project
import com.intellij.util.awaitCompleteProjectConfiguration
import com.intellij.openapi.project.configuration.HeadlessLogging
import com.intellij.openapi.project.configuration.awaitCompleteProjectConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.job
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.selects.select
suspend fun configureProjectByActivities(args: OpenProjectArgs) : Project {
@@ -39,24 +37,15 @@ suspend fun configureProjectByActivities(args: OpenProjectArgs) : Project {
return project
}
internal val abortFlow : MutableStateFlow<String?> = MutableStateFlow(null)
private fun CoroutineScope.getFailureDeferred() : Deferred<String> {
return async {
while (coroutineContext.job.isActive) {
val message = abortFlow.value
if (message != null) {
return@async message
}
delay(500)
}
error("unreachable")
val firstFatal = HeadlessLogging.loggingFlow().first { (level, _) -> level == HeadlessLogging.SeverityKind.Fatal }
firstFatal.message.representation()
}
}
private fun CoroutineScope.getConfigurationDeferred(project : Project) : Deferred<Unit> {
return async(start = CoroutineStart.UNDISPATCHED) {
return async {
withLoggingProgressReporter {
project.awaitCompleteProjectConfiguration(WarmupLogger::logInfo)
}

View File

@@ -1,19 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.warmup.util
import com.intellij.ide.warmup.WarmupLogger
class DefaultWarmupLogger : WarmupLogger {
override fun logInfo(message: String) {
com.intellij.warmup.util.WarmupLogger.logInfo(message)
}
override fun logError(message: String, throwable: Throwable?) {
com.intellij.warmup.util.WarmupLogger.logError(message, throwable)
}
override fun logFatalError(message: String, throwable: Throwable?) {
logError(message, throwable)
abortFlow.tryEmit(message)
}
}

View File

@@ -2,26 +2,19 @@
package com.intellij.warmup.util
import com.intellij.diagnostic.ThreadDumper
import com.intellij.ide.warmup.WarmupStatus
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.JulLogger
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.RollingFileHandler
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManagerListener
import com.intellij.openapi.progress.Task
import com.intellij.openapi.progress.util.ProgressIndicatorBase
import com.intellij.openapi.project.configuration.ChannelingProgressIndicator
import com.intellij.openapi.project.configuration.HeadlessLogging
import com.intellij.openapi.util.io.findOrCreateFile
import com.intellij.openapi.util.text.Formats
import com.intellij.openapi.wm.ex.ProgressIndicatorEx
import com.intellij.platform.ide.bootstrap.logEssentialInfoAboutIde
import com.intellij.platform.util.progress.createProgressPipe
import com.intellij.util.application
import com.intellij.util.lazyPub
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BufferOverflow
@@ -42,28 +35,33 @@ import kotlin.time.Duration.Companion.milliseconds
object WarmupLogger {
fun logInfo(message: String) {
ConsoleLog.info(message)
warmupLogger?.info(message)
}
fun logError(message: String, t: Throwable? = null) {
ConsoleLog.error(message, t)
warmupLogger?.error(message, t)
}
internal fun logStructured(message: StructuredMessage) {
ConsoleLog.info(message.fullMessage)
warmupLogger?.info(message.contractedMessage)
}
}
internal fun initLogger(args: List<String>) {
val logger = warmupLogger ?: return
internal fun CoroutineScope.initLogger(args: List<String>): Job {
val logger = warmupLogger ?: return Job()
val info = ApplicationInfo.getInstance()
val buildDate = SimpleDateFormat("dd MMM yyyy HH:mm", Locale.US).format(info.buildDate.time)
logEssentialInfoAboutIde(log = logger, appInfo = info, args = args)
val connection = ApplicationManager.getApplication().messageBus.connect()
connection.subscribe(ProgressManagerListener.TOPIC, WarmupProgressListener())
logger.info("IDE: ${ApplicationNamesInfo.getInstance().fullProductName} (build #${info.build.asString()}, ${buildDate})")
return launch {
HeadlessLogging.loggingFlow().collect { (level, message) ->
val messageRepresentation = message.representation()
when (level) {
HeadlessLogging.SeverityKind.Info -> ConsoleLog.info(messageRepresentation)
HeadlessLogging.SeverityKind.Warning -> ConsoleLog.warn(messageRepresentation)
HeadlessLogging.SeverityKind.Fatal -> ConsoleLog.error(messageRepresentation)
}
}
}
}
@@ -99,42 +97,7 @@ internal fun progressStateText(fraction: Double?, text: String?, details: String
return StructuredMessage(verboseReport, shortReport)
}
private class ChannelingProgressIndicator(private val prefix: String) : ProgressIndicatorBase() {
override fun setIndeterminate(indeterminate: Boolean) {
super.setIndeterminate(indeterminate)
offerState()
}
override fun setFraction(fraction: Double) {
super.setFraction(fraction)
offerState()
}
override fun setText(text: String?) {
super.setText(text)
super.setText2("")
offerState()
}
override fun setText2(text: String?) {
super.setText2(text)
offerState()
}
private fun offerState() {
val messages = ApplicationManager.getApplication().service<WarmupLoggingService>().messages
val progressState = progressStateText(
fraction = if (isIndeterminate) null else fraction,
text = text,
details = text2,
) ?: return
val actualPrefix = if (prefix.isEmpty()) "" else "[$prefix]: "
messages.tryEmit(progressState.copy(
contractedMessage = actualPrefix + progressState.contractedMessage,
fullMessage = actualPrefix + progressState.fullMessage,
))
}
}
/**
* Installs a progress reporter that sends the information about progress to the stdout instead of UI.
@@ -191,13 +154,8 @@ private val loggerFactory: WarmupLoggerFactory? by lazyPub {
}
private val warmupLogger: Logger? by lazyPub {
if (WarmupStatus.currentStatus() != WarmupStatus.InProgress) {
null
}
else {
val instance = loggerFactory?.getLoggerInstance("Warmup") ?: return@lazyPub null
instance
}
val instance = loggerFactory?.getLoggerInstance("Warmup") ?: return@lazyPub null
instance
}
internal data class StructuredMessage(
@@ -222,28 +180,6 @@ private class WarmupLoggingService(scope: CoroutineScope) {
}
}
class WarmupProgressListener : ProgressManagerListener {
private val taskDurationMap = ConcurrentHashMap<Int, Long>()
override fun beforeTaskStart(task: Task, indicator: ProgressIndicator) {
if (indicator !is ProgressIndicatorEx) {
return
}
WarmupLogger.logInfo("[IDE]: Task '${task.title}' started")
taskDurationMap[System.identityHashCode(task)] = System.currentTimeMillis()
indicator.addStateDelegate(ChannelingProgressIndicator("IDE"))
super.beforeTaskStart(task, indicator)
}
override fun afterTaskFinished(task: Task) {
val currentTime = System.currentTimeMillis()
val startTime = taskDurationMap.remove(System.identityHashCode(task))
val elapsedTimeSuffix = if (startTime == null) "" else " in ${Formats.formatDuration(currentTime - startTime)}"
WarmupLogger.logInfo("[IDE]: Task '${task.title}' ended" + elapsedTimeSuffix)
}
}
internal fun dumpThreadsAfterConfiguration() {
val dump = ThreadDumper.getThreadDumpInfo(ThreadDumper.getThreadInfos(), false)

View File

@@ -6,26 +6,23 @@ import com.intellij.ide.CommandLineProgressReporterElement
import com.intellij.ide.environment.EnvironmentService
import com.intellij.ide.impl.ProjectOpenKeyProvider
import com.intellij.ide.warmup.WarmupConfigurator
import com.intellij.ide.warmup.WarmupLogger
import com.intellij.ide.warmup.WarmupStatus
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.progress.blockingContextScope
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.io.createParentDirectories
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationListener
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationManager
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsTaskId
import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.LoggingNotificationListener
import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.StateExternalAnnotationNotificationListener
import org.jetbrains.plugins.gradle.service.project.GradleHeadlessLoggingProjectActivity.StateNotificationListener
import org.jetbrains.plugins.gradle.service.project.isGradleProjectResolveTask
import org.jetbrains.plugins.gradle.service.project.open.createLinkSettings
import org.jetbrains.plugins.gradle.settings.GradleImportHintService
import org.jetbrains.plugins.gradle.settings.GradleSettings
@@ -37,14 +34,10 @@ import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import kotlin.coroutines.coroutineContext
import kotlin.io.path.appendText
import kotlin.io.path.createFile
import kotlin.io.path.div
private val LOG = logger<GradleCommandLineProjectConfigurator>()
private val gradleLogWriterPath = Path.of(PathManager.getLogPath()) / "gradle-import.log"
private const val DISABLE_GRADLE_AUTO_IMPORT = "external.system.auto.import.disabled"
private const val DISABLE_GRADLE_JDK_FIX = "gradle.auto.auto.jdk.fix.disabled"
@@ -156,66 +149,9 @@ class GradleWarmupConfigurator : WarmupConfigurator {
return false
}
class StateExternalAnnotationNotificationListener : ExternalAnnotationsProgressNotificationListener {
override fun onStartResolve(id: ExternalAnnotationsTaskId) {
LOG.info("Gradle resolving external annotations started ${id.projectId}")
}
override fun onFinishResolve(id: ExternalAnnotationsTaskId) {
LOG.info("Gradle resolving external annotations completed ${id.projectId}")
}
}
class StateNotificationListener(
private val project: Project, private val scope: CoroutineScope
) : ExternalSystemTaskNotificationListener {
override fun onSuccess(id: ExternalSystemTaskId) {
if (!id.isGradleProjectResolveTask()) return
LOG.info("Gradle resolve stage finished with success: ${id.ideProjectId}")
project.messageBus.connect(scope)
.subscribe(ProjectDataImportListener.TOPIC, object : ProjectDataImportListener {
override fun onImportStarted(projectPath: String?) {
LOG.info("Gradle data import stage started: ${id.ideProjectId}")
}
override fun onImportFinished(projectPath: String?) {
LOG.info("Gradle data import stage finished with success: ${id.ideProjectId}")
}
override fun onFinalTasksFinished(projectPath: String?) {
LOG.info("Gradle data import(final tasks) stage finished: ${id.ideProjectId}")
}
override fun onFinalTasksStarted(projectPath: String?) {
LOG.info("Gradle data import(final tasks) stage started: ${id.ideProjectId}")
}
override fun onImportFailed(projectPath: String?, t: Throwable) {
WarmupLogger.fatalError(t.message ?: "Gradle import finished with error", t)
LOG.info("Gradle data import stage finished with failure: ${id.ideProjectId}")
}
})
}
override fun onFailure(id: ExternalSystemTaskId, e: Exception) {
if (!id.isGradleProjectResolveTask()) return
WarmupLogger.fatalError(e.message ?: "Gradle import finished with error", e)
LOG.error("Gradle resolve stage finished with failure ${id.ideProjectId}", e)
}
override fun onCancel(id: ExternalSystemTaskId) {
if (!id.isGradleProjectResolveTask()) return
LOG.error("Gradle resolve stage canceled ${id.ideProjectId}")
}
override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
if (!id.isGradleProjectResolveTask()) return
LOG.info("Gradle resolve stage started ${id.ideProjectId}, working dir: $workingDir")
}
}
class ImportErrorListener(
@@ -246,39 +182,6 @@ class GradleWarmupConfigurator : WarmupConfigurator {
}
}
class LoggingNotificationListener(val logger: CommandLineInspectionProgressReporter?) : ExternalSystemTaskNotificationListener {
private val logPath = try {
gradleLogWriterPath.createParentDirectories().createFile()
}
catch (e: java.nio.file.FileAlreadyExistsException) {
gradleLogWriterPath
}
override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
val gradleText = (if (stdOut) "" else "STDERR: ") + text
logPath.appendText(gradleText)
val croppedMessage = processMessage(gradleText)
if (croppedMessage != null) {
logger?.reportMessage(1, croppedMessage)
}
}
private fun processMessage(gradleText: String): String? {
if (WarmupStatus.currentStatus() != WarmupStatus.InProgress) {
return gradleText
}
val cropped = gradleText.trimStart('\r').trimEnd('\n')
if (cropped.startsWith("Download")) {
// we don't want to be flooded by a ton of download messages, so we'll print only final message
if (cropped.contains(" took ")) {
return cropped
}
return null
}
return cropped
}
}
}
internal fun prepareGradleConfiguratorEnvironment(logger: CommandLineInspectionProgressReporter?) {
@@ -288,11 +191,9 @@ internal fun prepareGradleConfiguratorEnvironment(logger: CommandLineInspectionP
System.setProperty(DISABLE_UPDATE_ANDROID_SDK_LOCAL_PROPERTIES, true.toString())
val progressManager = ExternalSystemProgressNotificationManager.getInstance()
if (logger != null) {
progressManager.addNotificationListener(GradleWarmupConfigurator.LoggingNotificationListener(logger))
progressManager.addNotificationListener(LoggingNotificationListener())
}
Unit
}
private fun ExternalSystemTaskId.isGradleProjectResolveTask() = this.projectSystemId == GradleConstants.SYSTEM_ID &&
this.type == ExternalSystemTaskType.RESOLVE_PROJECT

View File

@@ -1,22 +1,32 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.gradle.service.project
import com.intellij.ide.CommandLineInspectionProgressReporter
import com.intellij.ide.warmup.WarmupLogger
import com.intellij.ide.warmup.WarmupStatus
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListener
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.configuration.HeadlessLogging
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.util.registry.Registry
import com.intellij.util.application
import com.intellij.util.awaitCancellationAndInvoke
import com.intellij.util.io.createParentDirectories
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.plugins.gradle.GradleWarmupConfigurator
import kotlinx.coroutines.launch
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationListener
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsProgressNotificationManager
import org.jetbrains.plugins.gradle.service.notification.ExternalAnnotationsTaskId
import org.jetbrains.plugins.gradle.util.GradleConstants
import java.nio.file.Path
import kotlin.io.path.appendText
import kotlin.io.path.createFile
import kotlin.io.path.div
class GradleHeadlessLoggingProjectActivity(val scope: CoroutineScope) : ProjectActivity {
override suspend fun execute(project: Project) {
if (WarmupStatus.currentStatus() != WarmupStatus.InProgress || !Registry.`is`("ide.warmup.use.predicates")) {
if (!application.isHeadlessEnvironment || application.isUnitTestMode) {
return
}
val progressManager = ExternalSystemProgressNotificationManager.getInstance()
@@ -26,42 +36,132 @@ class GradleHeadlessLoggingProjectActivity(val scope: CoroutineScope) : ProjectA
}
private fun addTaskNotificationListener(progressManager: ExternalSystemProgressNotificationManager) {
val listener = GradleWarmupConfigurator.LoggingNotificationListener(object : CommandLineInspectionProgressReporter {
override fun reportError(message: String?) {
if (message == null) {
return
}
WarmupLogger.fatalError(message, null)
}
override fun reportMessage(minVerboseLevel: Int, message: String?) {
if (message == null) {
return
}
WarmupLogger.message(message)
}
})
val listener = LoggingNotificationListener()
progressManager.addNotificationListener(listener)
scope.awaitCancellationAndInvoke {
progressManager.removeNotificationListener(listener)
scope.launch {
awaitCancellationAndInvoke {
progressManager.removeNotificationListener(listener)
}
}
}
private fun addStateNotificationListener(project: Project, progressManager: ExternalSystemProgressNotificationManager) {
val notificationListener = GradleWarmupConfigurator.StateNotificationListener(project, scope)
val notificationListener = StateNotificationListener(project, scope)
progressManager.addNotificationListener(notificationListener)
scope.awaitCancellationAndInvoke {
progressManager.removeNotificationListener(notificationListener)
scope.launch {
awaitCancellationAndInvoke {
progressManager.removeNotificationListener(notificationListener)
}
}
}
private fun addAnnotationListener() {
val externalAnnotationsNotificationManager = ExternalAnnotationsProgressNotificationManager.getInstance()
val externalAnnotationsProgressListener = GradleWarmupConfigurator.StateExternalAnnotationNotificationListener()
val externalAnnotationsProgressListener = StateExternalAnnotationNotificationListener()
externalAnnotationsNotificationManager.addNotificationListener(externalAnnotationsProgressListener)
scope.awaitCancellationAndInvoke {
externalAnnotationsNotificationManager.removeNotificationListener(externalAnnotationsProgressListener)
scope.launch {
awaitCancellationAndInvoke {
externalAnnotationsNotificationManager.removeNotificationListener(externalAnnotationsProgressListener)
}
}
}
}
class StateExternalAnnotationNotificationListener : ExternalAnnotationsProgressNotificationListener {
override fun onStartResolve(id: ExternalAnnotationsTaskId) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle resolving external annotations started ${id.projectId}")
}
override fun onFinishResolve(id: ExternalAnnotationsTaskId) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle resolving external annotations finished ${id.projectId}")
}
}
class StateNotificationListener(
private val project: Project, private val scope: CoroutineScope
) : ExternalSystemTaskNotificationListener {
override fun onSuccess(id: ExternalSystemTaskId) {
if (!id.isGradleProjectResolveTask()) return
HeadlessLogging.logMessage(gradlePrefix + "Gradle resolve stage finished with success: ${id.ideProjectId}")
project.messageBus.connect(scope)
.subscribe(ProjectDataImportListener.TOPIC, object : ProjectDataImportListener {
override fun onImportStarted(projectPath: String?) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle data import stage started: ${id.ideProjectId}")
}
override fun onImportFinished(projectPath: String?) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle data import stage finished with success: ${id.ideProjectId}")
}
override fun onFinalTasksFinished(projectPath: String?) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle data import(final tasks) stage finished: ${id.ideProjectId}")
}
override fun onFinalTasksStarted(projectPath: String?) {
HeadlessLogging.logMessage(gradlePrefix + "Gradle data import(final tasks) stage started: ${id.ideProjectId}")
}
override fun onImportFailed(projectPath: String?, t: Throwable) {
HeadlessLogging.logFatalError(t)
}
})
}
override fun onFailure(id: ExternalSystemTaskId, e: Exception) {
if (!id.isGradleProjectResolveTask()) return
HeadlessLogging.logFatalError(e)
}
override fun onCancel(id: ExternalSystemTaskId) {
if (!id.isGradleProjectResolveTask()) return
HeadlessLogging.logWarning(gradlePrefix + "Gradle resolve stage canceled ${id.ideProjectId}")
}
override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
if (!id.isGradleProjectResolveTask()) return
HeadlessLogging.logMessage(gradlePrefix + "Gradle resolve stage started ${id.ideProjectId}, working dir: $workingDir")
}
}
class LoggingNotificationListener : ExternalSystemTaskNotificationListener {
private val logPath = try {
gradleLogWriterPath.createParentDirectories().createFile()
}
catch (e: java.nio.file.FileAlreadyExistsException) {
gradleLogWriterPath
}
override fun onTaskOutput(id: ExternalSystemTaskId, text: String, stdOut: Boolean) {
val gradleText = (if (stdOut) "" else "STDERR: ") + text
logPath.appendText(gradleText)
val croppedMessage = processMessage(gradleText)
if (croppedMessage != null) {
HeadlessLogging.logMessage(gradlePrefix + croppedMessage)
}
}
private fun processMessage(gradleText: String): String? {
val cropped = gradleText.trimStart('\r').trimEnd('\n')
if (cropped.startsWith("Download")) {
// we don't want to be flooded by a ton of download messages, so we'll print only final message
if (cropped.contains(" took ")) {
return cropped
}
return null
}
return cropped
}
}
}
internal fun ExternalSystemTaskId.isGradleProjectResolveTask() = this.projectSystemId == GradleConstants.SYSTEM_ID &&
this.type == ExternalSystemTaskType.RESOLVE_PROJECT
private val gradleLogWriterPath = Path.of(PathManager.getLogPath()) / "gradle-import.log"
private val gradlePrefix = "[Gradle]: "

View File

@@ -2,8 +2,8 @@ package com.jetbrains.performancePlugin.commands
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.configuration.awaitCompleteProjectConfiguration
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.util.awaitCompleteProjectConfiguration
private val LOG: Logger