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
This commit is contained in:
Daniil.Bubnov
2024-09-26 17:35:56 +02:00
committed by intellij-monorepo-bot
parent 578c631e2a
commit 7d57293565
5 changed files with 41 additions and 16 deletions

View File

@@ -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
) {
/**

View File

@@ -21,7 +21,8 @@ class MLEventLoggerProvider : StatisticsEventLoggerProvider(
sendFrequencyMs = TimeUnit.MINUTES.toMillis(10),
maxFileSizeInBytes = 100 * 1024,
sendLogsOnIdeClose = true,
isCharsEscapingRequired = false
isCharsEscapingRequired = false,
useDefaultRecorderId = true
) {
/**

View File

@@ -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"))

View File

@@ -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)
}
/**

View File

@@ -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()