diff --git a/ApplicationLibCode/Application/Tools/Telemetry/RiaOpenTelemetryManager.cpp b/ApplicationLibCode/Application/Tools/Telemetry/RiaOpenTelemetryManager.cpp index a81169fd77..0b8c61bef0 100644 --- a/ApplicationLibCode/Application/Tools/Telemetry/RiaOpenTelemetryManager.cpp +++ b/ApplicationLibCode/Application/Tools/Telemetry/RiaOpenTelemetryManager.cpp @@ -22,6 +22,7 @@ #include "RiaPreferencesOpenTelemetry.h" #include "RifJsonEncodeDecode.h" +#include #include #include #include @@ -198,6 +199,24 @@ bool RiaOpenTelemetryManager::reinitialize() return initialize(); } +//-------------------------------------------------------------------------------------------------- +/// Create a stable, anonymized hash of username for privacy-preserving telemetry +/// Uses SHA-256 to ensure consistent hashing across sessions while protecting user identity +//-------------------------------------------------------------------------------------------------- +static std::string hashUsername( const std::string& username ) +{ + if ( username.empty() ) + { + return ""; + } + + QByteArray usernameBytes = QString::fromStdString( username ).toUtf8(); + QByteArray hash = QCryptographicHash::hash( usernameBytes, QCryptographicHash::Sha256 ); + + // Convert to hex string for readability in telemetry + return hash.toHex().toStdString(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -217,7 +236,21 @@ void RiaOpenTelemetryManager::reportEventAsync( const std::string& eventName, co return; } - m_eventQueue.emplace( eventName, attributes ); + // Create mutable copy and add hashed username if configured (privacy-preserving for regular events) + // Note: crash events already have real username added in reportCrash() + std::map enrichedAttributes = attributes; + + bool isCrashEvent = ( eventName == "crash.signal_handler" ); + if ( !isCrashEvent ) + { + std::lock_guard configLock( m_configMutex ); + if ( !m_username.empty() ) + { + enrichedAttributes["user.hash"] = hashUsername( m_username ); + } + } + + m_eventQueue.emplace( eventName, enrichedAttributes ); m_healthMetrics.eventsQueued++; } @@ -289,6 +322,20 @@ void RiaOpenTelemetryManager::reportCrash( int signalCode, const std::stacktrace attributes["service.name"] = RiaPreferencesOpenTelemetry::current()->serviceName().toStdString(); attributes["service.version"] = RiaPreferencesOpenTelemetry::current()->serviceVersion().toStdString(); + // Add system information + attributes["os.type"] = QSysInfo::productType().toStdString(); + attributes["os.version"] = QSysInfo::productVersion().toStdString(); + attributes["os.name"] = QSysInfo::prettyProductName().toStdString(); + + // Add real username for crash reports (not hashed - needed for debugging critical issues) + { + std::lock_guard lock( m_configMutex ); + if ( !m_username.empty() ) + { + attributes["user.name"] = m_username; + } + } + reportEventAsync( "crash.signal_handler", attributes ); flushPendingEvents(); @@ -326,6 +373,7 @@ void RiaOpenTelemetryManager::setErrorCallback( ErrorCallback callback ) void RiaOpenTelemetryManager::setUsername( const std::string& username ) { std::lock_guard lock( m_configMutex ); + // Store original username - will be hashed for regular events but kept plain for crash reports m_username = username; } @@ -570,20 +618,6 @@ void RiaOpenTelemetryManager::processEvent( const Event& event ) properties[QString::fromStdString( key )] = QString::fromStdString( value ); } - // Add system information - properties["os.type"] = QSysInfo::productType(); - properties["os.version"] = QSysInfo::productVersion(); - properties["os.name"] = QSysInfo::prettyProductName(); - - // Add username if configured - { - std::lock_guard lock( m_configMutex ); - if ( !m_username.empty() ) - { - properties["user.name"] = QString::fromStdString( m_username ); - } - } - // Determine if this is a crash event bool isCrashEvent = ( event.name == "crash.signal_handler" );