From 7d572935651c66242f85cb5aaafd3bdc58eea528 Mon Sep 17 00:00:00 2001 From: "Daniil.Bubnov" Date: Thu, 26 Sep 2024 17:35:56 +0200 Subject: [PATCH] LLM-10123 Fix different device_id in FUS and ML v2 logs Add a possibility for other recorders to have same `device_id` and `machine_id` as in `FUS` recorder. This will greatly simplify the process of data analysis. Applicable only for anonymized recorders. (cherry picked from commit 5cee599f48d69ff77a006233b8103879597e8261) GitOrigin-RevId: 18c9763ef8bebb874fa85e720c61c196ea8b9b92 --- .../statistic/IJMapperEventLoggerProvider.kt | 3 +- .../ml/impl/logs/MLEventLoggerProvider.kt | 3 +- .../statistics/EventLogConfigurationTest.kt | 13 ++++++++ .../eventLog/EventLogConfiguration.kt | 30 +++++++++++-------- .../eventLog/StatisticsEventLogger.kt | 8 +++-- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/internal/statistic/IJMapperEventLoggerProvider.kt b/platform/lang-impl/src/com/intellij/internal/statistic/IJMapperEventLoggerProvider.kt index 026064e699a1..496d837946ed 100644 --- a/platform/lang-impl/src/com/intellij/internal/statistic/IJMapperEventLoggerProvider.kt +++ b/platform/lang-impl/src/com/intellij/internal/statistic/IJMapperEventLoggerProvider.kt @@ -20,7 +20,8 @@ class IJMapperEventLoggerProvider : StatisticsEventLoggerProvider( sendFrequencyMs = TimeUnit.HOURS.toMillis(1), maxFileSizeInBytes = 1 * 512, // enough to fill a log file with one entry of the state collector sendLogsOnIdeClose = true, - isCharsEscapingRequired = false + isCharsEscapingRequired = false, + useDefaultRecorderId = true ) { /** diff --git a/platform/ml-impl/src/com/intellij/platform/ml/impl/logs/MLEventLoggerProvider.kt b/platform/ml-impl/src/com/intellij/platform/ml/impl/logs/MLEventLoggerProvider.kt index a2b9c8e70376..e19eadfa4b46 100644 --- a/platform/ml-impl/src/com/intellij/platform/ml/impl/logs/MLEventLoggerProvider.kt +++ b/platform/ml-impl/src/com/intellij/platform/ml/impl/logs/MLEventLoggerProvider.kt @@ -21,7 +21,8 @@ class MLEventLoggerProvider : StatisticsEventLoggerProvider( sendFrequencyMs = TimeUnit.MINUTES.toMillis(10), maxFileSizeInBytes = 100 * 1024, sendLogsOnIdeClose = true, - isCharsEscapingRequired = false + isCharsEscapingRequired = false, + useDefaultRecorderId = true ) { /** diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogConfigurationTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogConfigurationTest.kt index 1a896c9458c3..dc1b60d51a24 100644 --- a/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogConfigurationTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/EventLogConfigurationTest.kt @@ -11,6 +11,19 @@ import com.intellij.testFramework.fixtures.BasePlatformTestCase import kotlin.test.assertNotEquals class EventLogConfigurationTest : BasePlatformTestCase() { + + // Test that two different recorders can have same machine/device id if the alternative recorder id is provided + fun testMachineIdDeviceIdAlternativeRecorder() { + val configuration = EventLogConfiguration.getInstance().getOrCreate("ABC", null) + val alternativeConfiguration = EventLogConfiguration.getInstance().getOrCreate("DEF", "ABC") + + assertNotNull(configuration.deviceId) + assertNotNull(configuration.machineId) + + assertEquals(configuration.deviceId, alternativeConfiguration.deviceId) + assertEquals(configuration.machineId, alternativeConfiguration.machineId) + } + fun testMachineIdRegeneration() { doTestRegenerate({ it.machineId }, hashMapOf(MACHINE_ID_SALT to "newSalt", MACHINE_ID_SALT_REVISION to "2")) diff --git a/platform/statistics/src/com/intellij/internal/statistic/eventLog/EventLogConfiguration.kt b/platform/statistics/src/com/intellij/internal/statistic/eventLog/EventLogConfiguration.kt index 37408d165aae..92abc6e75d5f 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/eventLog/EventLogConfiguration.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/eventLog/EventLogConfiguration.kt @@ -80,6 +80,7 @@ class EventLogConfiguration { } val defaultSessionId: String by lazy { generateSessionId() } + internal fun generateSessionId(): String { val presentableHour = StatisticsUtil.getCurrentHourInUTC() return "$presentableHour-${UUID.randomUUID().toString().shortedUUID()}" @@ -119,11 +120,12 @@ class EventLogConfiguration { return if (str.endsWith(".")) str + "0" else str } - fun getOrCreate(recorderId: String): EventLogRecorderConfiguration { + @JvmOverloads + fun getOrCreate(recorderId: String, alternativeRecorderId: String? = null): EventLogRecorderConfiguration { if (isDefaultRecorderId(recorderId)) return defaultConfiguration synchronized(this) { - return configurations.getOrPut(recorderId) { EventLogRecorderConfiguration(recorderId, this) } + return configurations.getOrPut(recorderId) { EventLogRecorderConfiguration(recorderId = recorderId, eventLogConfiguration = this, alternativeRecorderId = alternativeRecorderId) } } } @@ -149,9 +151,13 @@ class EventLogConfiguration { } +/** + * @param alternativeRecorderId - when provided, machine id and device id will be generated based on it instead of the `recorderId` + */ class EventLogRecorderConfiguration internal constructor(private val recorderId: String, private val eventLogConfiguration: EventLogConfiguration, - val sessionId: String = generateSessionId()) { + val sessionId: String = generateSessionId(), + val alternativeRecorderId: String? = null) { val deviceId: String = getOrGenerateDeviceId() val bucket: Int = deviceId.asBucket() @@ -167,11 +173,11 @@ class EventLogRecorderConfiguration internal constructor(private val recorderId: init { machineIdReference = AtomicLazyValue { - val configOptions = EventLogConfigOptionsService.getInstance().getOptions(recorderId) + val configOptions = EventLogConfigOptionsService.getInstance().getOptions(alternativeRecorderId ?: recorderId) generateMachineId(configOptions.machineIdSalt, configOptions.machineIdRevision) } - EventLogConfigOptionsService.TOPIC.subscribe(null, object : EventLogRecorderConfigOptionsListener(recorderId) { + EventLogConfigOptionsService.TOPIC.subscribe(null, object : EventLogRecorderConfigOptionsListener(alternativeRecorderId ?: recorderId) { override fun onMachineIdConfigurationChanged(salt: @Nullable String?, revision: Int) { machineIdReference.updateAndGet { prevValue -> if (salt != null && revision != -1 && revision > prevValue.revision) { @@ -189,7 +195,7 @@ class EventLogRecorderConfiguration internal constructor(private val recorderId: return MachineId.DISABLED } val revision = if (value >= 0) value else DEFAULT_ID_REVISION - val machineId = MachineIdManager.getAnonymizedMachineId("JetBrains$recorderId", salt) ?: return MachineId.UNKNOWN + val machineId = MachineIdManager.getAnonymizedMachineId("JetBrains${alternativeRecorderId ?: recorderId}", salt) ?: return MachineId.UNKNOWN return MachineId(machineId, revision) } @@ -219,17 +225,17 @@ class EventLogRecorderConfiguration internal constructor(private val recorderId: private fun getOrGenerateDeviceId(): String { val app = ApplicationManager.getApplication() if (app != null && app.isHeadlessEnvironment) { - val property = eventLogConfiguration.getHeadlessDeviceIdProperty(recorderId) + val property = eventLogConfiguration.getHeadlessDeviceIdProperty(alternativeRecorderId ?: recorderId) System.getProperty(property)?.let { return it } } try { - return DeviceIdManager.getOrGenerateId(object : DeviceIdManager.DeviceIdToken {}, recorderId) + return DeviceIdManager.getOrGenerateId(object : DeviceIdManager.DeviceIdToken {}, alternativeRecorderId ?: recorderId) } - catch (e: DeviceIdManager.InvalidDeviceIdTokenException) { - EventLogConfiguration.LOG.warn("Failed retrieving device id for $recorderId") + catch (_: DeviceIdManager.InvalidDeviceIdTokenException) { + EventLogConfiguration.LOG.warn("Failed retrieving device id for ${alternativeRecorderId ?: recorderId}") return UNDEFINED_DEVICE_ID } } @@ -237,13 +243,13 @@ class EventLogRecorderConfiguration internal constructor(private val recorderId: private fun getOrGenerateSalt(): ByteArray { val app = ApplicationManager.getApplication() if (app != null && app.isHeadlessEnvironment) { - val property = eventLogConfiguration.getHeadlessSaltProperty(recorderId) + val property = eventLogConfiguration.getHeadlessSaltProperty(alternativeRecorderId ?: recorderId) System.getProperty(property)?.let { return it.toByteArray(Charsets.UTF_8) } } - return EventLogConfiguration.getOrGenerateSaltFromPrefs(recorderId) + return EventLogConfiguration.getOrGenerateSaltFromPrefs(alternativeRecorderId ?: recorderId) } /** diff --git a/platform/statistics/src/com/intellij/internal/statistic/eventLog/StatisticsEventLogger.kt b/platform/statistics/src/com/intellij/internal/statistic/eventLog/StatisticsEventLogger.kt index 833f377b4f9b..f497e522d525 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/eventLog/StatisticsEventLogger.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/eventLog/StatisticsEventLogger.kt @@ -29,12 +29,16 @@ interface StatisticsEventLogger { fun rollOver() } +/** + * [useDefaultRecorderId] - When enabled, device and machine ids would match FUS(default) recorder. Must NOT be enabled for non-anonymized recorders. + */ abstract class StatisticsEventLoggerProvider(val recorderId: String, val version: Int, val sendFrequencyMs: Long, private val maxFileSizeInBytes: Int, val sendLogsOnIdeClose: Boolean = false, - val isCharsEscapingRequired: Boolean = true) { + val isCharsEscapingRequired: Boolean = true, + val useDefaultRecorderId: Boolean = false) { @Deprecated(message = "Use primary constructor instead") constructor(recorderId: String, @@ -128,7 +132,7 @@ abstract class StatisticsEventLoggerProvider(val recorderId: String, null } val eventLogConfiguration = EventLogConfiguration.getInstance() - val config = eventLogConfiguration.getOrCreate(recorderId) + val config = eventLogConfiguration.getOrCreate(recorderId, if (useDefaultRecorderId) "FUS" else null) val writer = StatisticsEventLogFileWriter(recorderId, this, maxFileSizeInBytes, isEap, eventLogConfiguration.build) val configService = EventLogConfigOptionsService.getInstance()