Use `LogLevelConfigurationManager` from plugin (currently marked Internal)

I would like configure debug logs for a plugin programmatically and at runtime. I found the class mentioned above which seems nice because the items would show up in the Debug Log Settings dialog.

However, they are marked as Internal currently.

Would you consider opening it up for use from a plugin?

It feels rather internal and barely can be exposed, low chances unfortunately

If you could rectify simpler API request we probably can introduce new API dedicated for the task you need

I have this approach using the internals.

Could this be easily done without?

I like especially that the user can see what is logged via the usual Debug Log Settings.

package x

import com.intellij.diagnostic.logs.DebugLogLevel
import com.intellij.diagnostic.logs.LogCategory
import com.intellij.diagnostic.logs.LogLevelConfigurationManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger

/**
 * Service for managing debug logging for plugin features.
 */
@Service
class FeatureLoggingService {

    private val logManager: LogLevelConfigurationManager
        get() = LogLevelConfigurationManager.getInstance()

    private val LOG = Logger.getInstance(FeatureLoggingService::class.java)

    fun enableLogging(feature: FeatureRegistry.FeatureInfo) {
        if (feature.loggingCategories.isEmpty()) return

        val categories = feature.loggingCategories.map { category ->
            LogCategory(category, DebugLogLevel.DEBUG)
        }
        logManager.addCategories(categories)
        LOG.warn(
            "Debug logging enabled for feature '${feature.displayName}' (${feature.id}): ${
                feature.loggingCategories.joinToString(
                    ", "
                )
            }"
        )
    }

    fun disableLogging(feature: FeatureRegistry.FeatureInfo) {
        if (feature.loggingCategories.isEmpty()) return

        val currentCategories = logManager.state.categories.toMutableList()
        currentCategories.removeAll { logCategory ->
            feature.loggingCategories.contains(logCategory.category)
        }
        logManager.setCategories(currentCategories)
        LOG.warn(
            "Debug logging disabled for feature '${feature.displayName}' (${feature.id}): ${
                feature.loggingCategories.joinToString(
                    ", "
                )
            }"
        )
    }

    fun isLoggingEnabled(feature: FeatureRegistry.FeatureInfo): Boolean {
        if (feature.loggingCategories.isEmpty()) return false

        val enabledCategories = logManager.state.categories.map { it.category }.toSet()
        return feature.loggingCategories.any { category ->
            enabledCategories.contains(category)
        }
    }

    companion object {
        @JvmStatic
        fun instance(): FeatureLoggingService = service()
    }
}

This seems too wide use of internals indeed, basically plugin tries to control very low level mechanism of the platform. But why do you need to switch levels programmatically instead of having good default levels sufficient to submit issues from users?

I see LogLevelConfigurationManager.addCategories and LogLevelConfigurationManager.setCategories are actually public APIs so far. So nothing should prevent its use at least for now.

But it may change in the future as LogLevelConfigurationManager is one of api-dump-unreviewed.txt APIs.

Thanks for pointing this out. The parameter type they take, however, is set to internal (LogCategory).

Then, adding programmatically but not being able to remove seems strange to me.

More about my use case: A plugin that has 30+ improvements for a JetBrains product. Each feature is available via a settings toggle and relies on different classes. If a problem occurs, I’d like to be able to specifically turn on debug logs but I prefer to not have them logged always.

From a developer view, this also seems quite convenient.

So the least change required would be making LogCategory stable.

Ideally, there must be API that only allows plugins to manage their own categories and do not remove logging setups of other plugins.

But for the practical purposes it seems we at least need to fix that internal class used in public signatures.

1 Like

Should be fixed in 2026.1 EAP builds

1 Like

Does it mean I can use these classes also in 253 builds? Or do I need to disable such features when running in non 261+ builds?

Yes, we will allow list them (in all versions) once the first EAP build appears for the verifier