L3 : journalisation efficace
LogLifePlay + ULPSessionLogSubsystem (sans modification de Blueprint) : resume de demarrage (versions, modules, fichiers par type) et bilan de session (signatures d'erreurs dedoublonnees avec pile d'appel, commande console LifePlay.LogSummary). ModdingExtra : LogLPModdingTrace -> LogModdingExtra en Verbose (format conserve pour l'A/B 4.27), echec de LoadTxt signale une fois, echec de SaveTxt signale. Tests : editeur et jeu compilent sans avertissement ; session -game : 58 lignes LogLifePlay, 0 ligne de trace au niveau par defaut ; A/B de format avec la build 4.27 Development ; T-STAT-02..04 au vert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4747778d2c
commit
3abf03e4be
12 changed files with 356 additions and 21 deletions
|
|
@ -169,7 +169,7 @@ Tout le hasard passe par `KismetMathLibrary` (`RandomFloat`, `RandomBool`, `Rand
|
|||
|
||||
## 7. Inconnues à lever (lot L5)
|
||||
|
||||
1. Appelants de `ReadOutfits`, `populateClothes4`, `PopulateCharGenPage`, `MakeDummyPersons` (probablement `PC`, `MainMenu` ou une minuterie).
|
||||
1. Appelants de `ReadOutfits`, `PopulateCharGenPage`, `MakeDummyPersons` (probablement `PC`, `MainMenu` ou une minuterie). `populateClothes4` : **appelée par `Person.ReceiveBeginPlay`** (pile d'appel du journal L3).
|
||||
2. Format exact de `ModLauncher.config` (déduit, jamais observé sur disque).
|
||||
3. Types de valeur de `PerksUnlocked`, `VoiceTypes`, `ActionCount` (`Map<string,?>`).
|
||||
4. Fin de `GeneratePerson` après `fertility_npc` (conditions de branche vers `ChooseNPCBodyShape`).
|
||||
|
|
|
|||
|
|
@ -82,6 +82,18 @@ Construction : `RunUAT BuildCookRun -project="$Proj" -platform=Win64 -clientconf
|
|||
Chaque test T-RUN est exécuté sur la build 5.5 **et** sur la référence 4.27 avec le même scénario.
|
||||
`Tools\LogStats.ps1 -Compare <journal 4.27> <journal 5.5>` produit le tableau normalisé.
|
||||
|
||||
Depuis L3, la trace d'entrée/sortie de `ModdingExtra` (catégorie `LogModdingExtra`, même format de
|
||||
ligne que l'ancienne `LogLPModdingTrace` de la build 4.27) est en `Verbose`. Pour un A/B, lancer la
|
||||
build 5.5 avec :
|
||||
|
||||
```
|
||||
LifePlay.exe -LogCmds="LogModdingExtra Verbose"
|
||||
```
|
||||
|
||||
Journal de session 5.5 : chercher `===== LifePlay : demarrage =====` (résumé des données) et
|
||||
`===== LifePlay : bilan de session` (signatures d'erreurs dédoublonnées, écrit à la fermeture ou par
|
||||
la commande console `LifePlay.LogSummary`).
|
||||
|
||||
## 4. Scénario d'exécution de référence (T-RUN-04)
|
||||
|
||||
1. Lancer la build, nouvelle partie avec le personnage par défaut.
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Traitement du `Registre_Erreurs.md` **par ordre de fréquence × gravité**, un
|
|||
régénération des fichiers de projet ; compilation éditeur et jeu.
|
||||
- **Sortie** : T-BUILD-01, T-BUILD-02, T-LOAD-04.
|
||||
|
||||
### L3 — Journalisation efficace
|
||||
### L3 — Journalisation efficace ✅ (2026-09-29)
|
||||
- **Objectif** : un journal qui sert au diagnostic.
|
||||
- **Périmètre** :
|
||||
- `Source/LifePlay` : catégorie `LogLifePlay` déclarée dans le module ; résumé de démarrage
|
||||
|
|
|
|||
|
|
@ -81,3 +81,37 @@ Tag d'entrée `lot-L2-avant`. Sauvegarde : `D:\Game\LifePlay_Backup_5.5\L2\` (`.
|
|||
| Non-régression `LPCheckMeshAssignment` | ✅ relevé identique à L1 (10 erreurs, 285 avertissements, mêmes signatures) |
|
||||
| ERR-023 (`AndroidFileServer`) | ✅ `DefaultEngine.ini` non réécrit après lancement |
|
||||
| T-STAT-02, 03, 04 | ✅ |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Lot L3 — Journalisation efficace (2026-09-29)
|
||||
|
||||
Tag d'entrée `lot-L3-avant`. Sauvegarde : `D:\Game\LifePlay_Backup_5.5\L3\` (6 entrées, dont 2 fichiers
|
||||
créés que `Restore.ps1` retirerait).
|
||||
|
||||
| ID | Date | Lot | Fichiers | Raison | Retour arrière |
|
||||
|---|---|---|---|---|---|
|
||||
| CHG-014 | 2026-09-29 | L3 | `Source/LifePlay/LifePlay.h`, `LifePlay.cpp` | catégorie `LogLifePlay` | `Tools\Restore.ps1 -Lot L3` |
|
||||
| CHG-015 | 2026-09-29 | L3 | `Source/LifePlay/LPSessionLogSubsystem.h/.cpp` (nouveaux), `LifePlay.Build.cs` (+ `EngineSettings`) | sous-système de `GameInstance`, **sans modification de Blueprint** : résumé de démarrage (versions, modules, fichiers par type) ; bilan de session à la fermeture ou via la commande console `LifePlay.LogSummary` (messages par catégorie, 25 signatures d'avertissement/erreur les plus fréquentes, chiffres normalisés en `#`, pile d'appel Blueprint incluse, plafond de 4 000 signatures). Les messages du moteur ne sont **pas** modifiés (comparabilité A/B avec 4.27) | idem |
|
||||
| CHG-016 | 2026-09-29 | L3 | `Plugins/ModdingExtra/.../ModdingExtraBPLibrary.cpp` | instrumentation `LogLPModdingTrace` (Warning) → `LogModdingExtra` (Verbose, même format de ligne) ; échec de `LoadTxt` signalé une seule fois par fichier ; échec de `SaveTxt` désormais signalé (il était silencieux) | idem |
|
||||
| CHG-017 | 2026-09-29 | L3 | `Tools/LogStats.ps1`, `Docs/Migration/03_Plan_Migration_Tests.md` | prise en compte de `LogModdingExtra` ; procédure A/B (`-LogCmds="LogModdingExtra Verbose"`) | `git revert` |
|
||||
|
||||
### Vérifications de sortie L3
|
||||
|
||||
Sessions `UnrealEditor -game` (valables pour le journal, pas pour l'affichage des personnages —
|
||||
règle §2 du document 03) : `Saved/Logs/portage/L3_game_20260929-012748.log` (niveau par défaut) et
|
||||
`L3_game_verbose_20260929-013032.log` (trace détaillée).
|
||||
|
||||
| Test | Résultat |
|
||||
|---|---|
|
||||
| T-BUILD-01, T-BUILD-02 | ✅ 0 erreur, 0 avertissement |
|
||||
| T-LOG-01 résumé de démarrage | ✅ 20 lignes `LogLifePlay` ; 16 modules ; comptes par type identiques à l'inventaire 4.27 (`vin_Base` : 752 `.lpscene`, 286 `.lpaction`…) |
|
||||
| T-LOG-01 bilan de session | ✅ écrit à la fermeture ; signatures dédoublonnées avec leur pile d'appel |
|
||||
| Bruit d'instrumentation | ✅ `LogLPModdingTrace` : 0 ligne au niveau par défaut (1 300 à 5 400 avant) ; 58 lignes `LogLifePlay` pour toute la session |
|
||||
| Trace détaillée | ✅ 1 284 lignes `LogModdingExtra` avec `-LogCmds` ; `ModLauncher.config` absent signalé **une** fois |
|
||||
| A/B de format avec la build 4.27 | ✅ `LogStats -Compare` lit les deux journaux ; toutes les énumérations communes aux deux sessions sont identiques (écarts limités aux dossiers que seule l'une des sessions a atteints) |
|
||||
| T-STAT-02, 03, 04 | ✅ |
|
||||
|
||||
Découverte au passage : les erreurs `ParseIntoArray` (ERR-004) viennent de `GI.populateClothes4`,
|
||||
appelée par `Person.ReceiveBeginPlay`.
|
||||
|
|
|
|||
|
|
@ -13,17 +13,17 @@
|
|||
| ERR-024 | `McpAutomationBridge` (outil de développement) impose des plugins d'exécution (`GameplayAbilities`, `SmartObjects`, `Niagara`, `PythonScriptPlugin`…) et dépend de `StructUtils`, déprécié en 5.5 | UBT L2 | — | faible (livraison) | L7g | ouvert |
|
||||
| ERR-002 | `generatePerson()` sans argument : personnages identiques (« Tyrone »), nus, invisibles ; en PIE 5.5 chaque `index SPAWN` pointe vers `Person_C_0` | rapport 2026-09-28, journal PIE 09-28 | 16,6 `Accessed None` maillage/perso (4.27 : 0,3) | bloquante | L5 | ouvert |
|
||||
| ERR-003 | `Attempted to access index 0 from array Float_… of length 0` dans `GI::ReturnStatIfNeeded` (variable DSL `Float` vide) | build 5.5 Development | 2 (4.27 : 0) | haute | L5 | ouvert |
|
||||
| ERR-004 | `index N from CallFunc_ParseIntoArray_ReturnValue of length N` (`GI` EventGraph) | PIE 09-28 | 6 510 | moyenne | L7b | ouvert |
|
||||
| ERR-004 | `index N from CallFunc_ParseIntoArray_ReturnValue of length N` (`GI` EventGraph) | PIE 09-28 | 6 510 | moyenne | L7b | ouvert — origine localisée (L3) : `GI.populateClothes4` appelée par `Person.ReceiveBeginPlay` |
|
||||
| ERR-005 | `getBeautyValue` : `Floats_*` index hors bornes | PIE 09-28 | 1 297 | moyenne | L7b | ouvert |
|
||||
| ERR-006 | `Accès à None` `NearVec` dans `Person::standIdle` | PIE 09-28 | 273 | moyenne | L7b | ouvert |
|
||||
| ERR-007 | `Accès à None` `ContactMenu` dans `UpdateDailyPose` | PIE 09-28 | 257 | moyenne | L7b | ouvert |
|
||||
| ERR-008 | `CallFunc_Array_Get_Item` à None dans `GI` | PIE 09-28 | 229 | moyenne | L5 | ouvert |
|
||||
| ERR-009 | `SpawnActor failed because no class was specified` | PIE 09-28 | 11 | moyenne | L6 | ouvert |
|
||||
| ERR-010 | `LogLPModdingTrace` : instrumentation de portage à chaque `LoadTxt` | tous journaux | ≈ 1 300 à 5 400 / session | faible (bruit) | L3 | ouvert |
|
||||
| ERR-010 | `LogLPModdingTrace` : instrumentation de portage à chaque `LoadTxt` | tous journaux | ≈ 1 300 à 5 400 / session | faible (bruit) | L3 | clos (CHG-016) : `LogModdingExtra` en Verbose |
|
||||
| ERR-011 | `GetSocketInfoByName … No SkeletalMesh for CharacterMesh` | tous journaux | 80/perso en 4.27, 76 en 5.5 | bruit (existe en 4.27) | — | accepté |
|
||||
| ERR-012 | Nœud `SkeletalMesh` déprécié (`GI` 52, `Person` 24) | `LogBlueprint` | 153 | faible (dette) | L7a | ouvert |
|
||||
| ERR-013 | `Daz/Michael/hairs/other/Pubes05-06` introuvable (assets dans `Daz/Bridget/Hairs/Pubic/`) | journaux 4.27 et 5.5 | 56 / session | faible (existe en 4.27) | L7b | ouvert |
|
||||
| ERR-014 | `Modules/ModLauncher.config` absent : `LoadTxt ECHEC` | journaux 4.27 et 5.5 | 2 / session | faible (existe en 4.27) | L3 | ouvert |
|
||||
| ERR-014 | `Modules/ModLauncher.config` absent : `LoadTxt ECHEC` | journaux 4.27 et 5.5 | 2 / session | faible (existe en 4.27) | L3 | accepté : signalé une seule fois (CHG-016) ; absence = tous les modules actifs |
|
||||
| ERR-015 | `InputMode:UIOnly … Non-Focusable widget` | PIE 09-28 | 25 | faible | L7b | ouvert |
|
||||
| ERR-016 | Matériau `FntMat` sans `bUsedWithStaticLighting` → matériau par défaut | rapport 09-28 | — | faible (visuel) | L7d | ouvert |
|
||||
| ERR-017 | `LogStreaming Failed to read file` (icônes moteur VisionOS, Interchange, Slate) | PIE 09-28 | 37 | cosmétique | — | accepté |
|
||||
|
|
|
|||
|
|
@ -6,10 +6,15 @@
|
|||
#include "Misc/FileHelper.h"
|
||||
#include "Misc/Paths.h"
|
||||
|
||||
// Instrumentation temporaire (diagnostic randomisation, 2026-09-28).
|
||||
// Presente a l'identique dans les arbres 4.27 et 5.5 pour permettre un A/B.
|
||||
// A RETIRER une fois le diagnostic clos.
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogLPModdingTrace, Log, All);
|
||||
// Journal des entrees/sorties de donnees (lot L3 du portage 5.5, remplace l'instrumentation
|
||||
// temporaire LogLPModdingTrace du 2026-09-28).
|
||||
// - Chaque lecture/enumeration est tracee en Verbose, au format de l'instrumentation 4.27 pour
|
||||
// que Tools/LogStats.ps1 compare les deux versions. Activation :
|
||||
// -LogCmds="LogModdingExtra Verbose"
|
||||
// - Un echec de lecture est signale en Warning une seule fois par fichier (ModLauncher.config
|
||||
// absent est un cas normal, lu plusieurs fois par session).
|
||||
// - Un echec d'ecriture est toujours signale en Warning.
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogModdingExtra, Log, All);
|
||||
|
||||
UModdingExtraBPLibrary::UModdingExtraBPLibrary(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
|
|
@ -50,7 +55,7 @@ float UModdingExtraBPLibrary::ModdingExtraSampleFunction(float Param)
|
|||
//FileManager.FindFiles(Files, *FinalPath, true, false);
|
||||
FileManager.FindFilesRecursive(Files, *FinalPathWithoutExt, *Ext, true, false, false);
|
||||
|
||||
UE_LOG(LogLPModdingTrace, Warning, TEXT("GetAllFilesFromFolder | '%s' ext='%s' -> %d fichier(s)"),
|
||||
UE_LOG(LogModdingExtra, Verbose, TEXT("GetAllFilesFromFolder | '%s' ext='%s' -> %d fichier(s)"),
|
||||
*RootFolderFullPath, *Ext, Files.Num());
|
||||
|
||||
return true;
|
||||
|
|
@ -77,23 +82,46 @@ float UModdingExtraBPLibrary::ModdingExtraSampleFunction(float Param)
|
|||
{
|
||||
const bool bOk = FFileHelper::LoadFileToString(SaveTextA, *(FPaths::ProjectContentDir() + FileNameA));
|
||||
|
||||
// Trace : nom, succes, taille, et decompte des fins de ligne. Un ecart de decompte
|
||||
// entre 4.27 et 5.5 sur le meme fichier signalerait un probleme de lecture ou
|
||||
// d'encodage, seule variable restante apres verification des graphes et des donnees.
|
||||
int32 CRLF = 0, LF = 0;
|
||||
for (int32 i = 0; i < SaveTextA.Len(); ++i)
|
||||
if (!bOk)
|
||||
{
|
||||
if (SaveTextA[i] == TEXT('\n')) { ++LF; if (i > 0 && SaveTextA[i-1] == TEXT('\r')) { ++CRLF; } }
|
||||
// Appele uniquement depuis les Blueprints, donc sur le thread de jeu.
|
||||
static TSet<FString> ReportedFailures;
|
||||
bool bAlreadyReported = false;
|
||||
ReportedFailures.Add(FileNameA, &bAlreadyReported);
|
||||
if (!bAlreadyReported)
|
||||
{
|
||||
UE_LOG(LogModdingExtra, Warning, TEXT("LoadTxt ECHEC | '%s' introuvable ou illisible (signale une seule fois)"), *FileNameA);
|
||||
}
|
||||
}
|
||||
|
||||
// Nom, succes, taille et fins de ligne : un ecart de decompte entre 4.27 et 5.5 sur le
|
||||
// meme fichier signalerait un probleme de lecture ou d'encodage.
|
||||
if (UE_LOG_ACTIVE(LogModdingExtra, Verbose))
|
||||
{
|
||||
int32 CRLF = 0, LF = 0;
|
||||
for (int32 i = 0; i < SaveTextA.Len(); ++i)
|
||||
{
|
||||
if (SaveTextA[i] == TEXT('\n')) { ++LF; if (i > 0 && SaveTextA[i-1] == TEXT('\r')) { ++CRLF; } }
|
||||
}
|
||||
UE_LOG(LogModdingExtra, Verbose, TEXT("LoadTxt %s | '%s' | %d car. | %d LF (dont %d CRLF)"),
|
||||
bOk ? TEXT("OK ") : TEXT("ECHEC"), *FileNameA, SaveTextA.Len(), LF, CRLF);
|
||||
}
|
||||
UE_LOG(LogLPModdingTrace, Warning, TEXT("LoadTxt %s | '%s' | %d car. | %d LF (dont %d CRLF)"),
|
||||
bOk ? TEXT("OK ") : TEXT("ECHEC"), *FileNameA, SaveTextA.Len(), LF, CRLF);
|
||||
|
||||
return bOk;
|
||||
}
|
||||
|
||||
bool UModdingExtraBPLibrary::SaveTxt(FString SaveTextB, FString FileNameB)
|
||||
{
|
||||
return FFileHelper::SaveStringToFile(SaveTextB, *(FPaths::ProjectContentDir() + FileNameB));
|
||||
const bool bOk = FFileHelper::SaveStringToFile(SaveTextB, *(FPaths::ProjectContentDir() + FileNameB));
|
||||
if (!bOk)
|
||||
{
|
||||
UE_LOG(LogModdingExtra, Warning, TEXT("SaveTxt ECHEC | '%s' (%d car.) : ecriture impossible"), *FileNameB, SaveTextB.Len());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogModdingExtra, Verbose, TEXT("SaveTxt OK | '%s' | %d car."), *FileNameB, SaveTextB.Len());
|
||||
}
|
||||
return bOk;
|
||||
}
|
||||
|
||||
FString UModdingExtraBPLibrary::ContentDirectory()
|
||||
|
|
|
|||
219
Source/LifePlay/LPSessionLogSubsystem.cpp
Normal file
219
Source/LifePlay/LPSessionLogSubsystem.cpp
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// Portage UE 5.5 - lot L3 (journalisation). Voir LPSessionLogSubsystem.h.
|
||||
|
||||
#include "LPSessionLogSubsystem.h"
|
||||
|
||||
#include "LifePlay.h"
|
||||
#include "GeneralProjectSettings.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Misc/App.h"
|
||||
#include "Misc/EngineVersion.h"
|
||||
#include "Misc/OutputDevice.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/ScopeLock.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Au-dela, les nouvelles signatures sont comptees ensemble : la memoire reste bornee
|
||||
// meme si un Blueprint emet des messages tous differents pendant des heures.
|
||||
constexpr int32 MaxSignatures = 4000;
|
||||
constexpr int32 SignatureLength = 240;
|
||||
constexpr int32 TopSignatures = 25;
|
||||
|
||||
// Remplace chaque suite de chiffres par '#' : "Person_C_2147482340" et
|
||||
// "index 12 ... length 12" se regroupent sous une seule signature. Les sauts de ligne
|
||||
// deviennent " | " : la pile d'appel Blueprint ("Script call stack:") reste lisible.
|
||||
FString MakeSignature(const TCHAR* Message)
|
||||
{
|
||||
FString Out;
|
||||
Out.Reserve(SignatureLength);
|
||||
bool bInDigits = false;
|
||||
for (const TCHAR* C = Message; *C && Out.Len() < SignatureLength; ++C)
|
||||
{
|
||||
if (*C == TEXT('\r') || *C == TEXT('\n') || *C == TEXT('\t'))
|
||||
{
|
||||
if (!Out.EndsWith(TEXT(" | ")) && !Out.IsEmpty()) { Out += TEXT(" | "); }
|
||||
bInDigits = false;
|
||||
}
|
||||
else if (FChar::IsDigit(*C))
|
||||
{
|
||||
if (!bInDigits) { Out.AppendChar(TEXT('#')); }
|
||||
bInDigits = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Out.AppendChar(*C);
|
||||
bInDigits = false;
|
||||
}
|
||||
}
|
||||
return Out;
|
||||
}
|
||||
|
||||
// Une seule commande console pour le processus, portee par la premiere instance de jeu
|
||||
// (en PIE plusieurs GameInstances peuvent coexister).
|
||||
FAutoConsoleCommand* SummaryCommand = nullptr;
|
||||
const void* SummaryCommandOwner = nullptr;
|
||||
}
|
||||
|
||||
/** Compte les messages du journal. N'ecrit jamais dans le journal lui-meme. */
|
||||
class FLPLogCounter : public FOutputDevice
|
||||
{
|
||||
public:
|
||||
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
|
||||
{
|
||||
const ELogVerbosity::Type Level = (ELogVerbosity::Type)(Verbosity & ELogVerbosity::VerbosityMask);
|
||||
if (Category == LogLifePlay.GetCategoryName())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FScopeLock Lock(&Mutex);
|
||||
++PerCategory.FindOrAdd(FString::Printf(TEXT("%s %s"), *Category.ToString(), ToString(Level)));
|
||||
|
||||
if (Level <= ELogVerbosity::Warning)
|
||||
{
|
||||
const FString Key = FString::Printf(TEXT("[%s %s] %s"), *Category.ToString(), ToString(Level), *MakeSignature(V));
|
||||
if (int32* Count = Signatures.Find(Key))
|
||||
{
|
||||
++*Count;
|
||||
}
|
||||
else if (Signatures.Num() < MaxSignatures)
|
||||
{
|
||||
Signatures.Add(Key, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
++Overflow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool CanBeUsedOnAnyThread() const override { return true; }
|
||||
virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||
|
||||
void Snapshot(TArray<TPair<FString, int32>>& OutCategories, TArray<TPair<FString, int32>>& OutSignatures, int32& OutOverflow) const
|
||||
{
|
||||
FScopeLock Lock(&Mutex);
|
||||
for (const auto& It : PerCategory) { OutCategories.Emplace(It.Key, It.Value); }
|
||||
for (const auto& It : Signatures) { OutSignatures.Emplace(It.Key, It.Value); }
|
||||
OutOverflow = Overflow;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable FCriticalSection Mutex;
|
||||
TMap<FString, int32> PerCategory;
|
||||
TMap<FString, int32> Signatures;
|
||||
int32 Overflow = 0;
|
||||
};
|
||||
|
||||
void ULPSessionLogSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::Initialize(Collection);
|
||||
|
||||
StartTime = FPlatformTime::Seconds();
|
||||
Counter = MakeShared<FLPLogCounter>();
|
||||
GLog->AddOutputDevice(Counter.Get());
|
||||
|
||||
if (!SummaryCommand)
|
||||
{
|
||||
SummaryCommand = new FAutoConsoleCommand(
|
||||
TEXT("LifePlay.LogSummary"),
|
||||
TEXT("Ecrit dans le journal le bilan de session LifePlay (messages par categorie, signatures les plus frequentes)."),
|
||||
FConsoleCommandDelegate::CreateWeakLambda(this, [this]() { LogSessionSummary(); }));
|
||||
SummaryCommandOwner = this;
|
||||
}
|
||||
|
||||
LogStartupSummary();
|
||||
}
|
||||
|
||||
void ULPSessionLogSubsystem::Deinitialize()
|
||||
{
|
||||
LogSessionSummary();
|
||||
|
||||
if (Counter.IsValid())
|
||||
{
|
||||
GLog->RemoveOutputDevice(Counter.Get());
|
||||
// Un autre thread peut encore etre dans Serialize : on vide la file avant liberation.
|
||||
GLog->Flush();
|
||||
Counter.Reset();
|
||||
}
|
||||
if (SummaryCommandOwner == this)
|
||||
{
|
||||
delete SummaryCommand;
|
||||
SummaryCommand = nullptr;
|
||||
SummaryCommandOwner = nullptr;
|
||||
}
|
||||
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
void ULPSessionLogSubsystem::LogStartupSummary() const
|
||||
{
|
||||
const UGeneralProjectSettings* Project = GetDefault<UGeneralProjectSettings>();
|
||||
UE_LOG(LogLifePlay, Display, TEXT("===== LifePlay : demarrage ====="));
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Jeu %s %s | moteur %s | configuration %s"),
|
||||
*Project->ProjectName, *Project->ProjectVersion,
|
||||
*FEngineVersion::Current().ToString(), LexToString(FApp::GetBuildConfiguration()));
|
||||
|
||||
// Meme racine que ModdingExtra::LoadTxt : ProjectContentDir()/Modules
|
||||
const FString ModulesDir = FPaths::Combine(FPaths::ProjectContentDir(), TEXT("Modules"));
|
||||
IFileManager& FM = IFileManager::Get();
|
||||
if (!FM.DirectoryExists(*ModulesDir))
|
||||
{
|
||||
UE_LOG(LogLifePlay, Error, TEXT("Dossier de donnees absent : %s (verifier DirectoriesToAlwaysStageAsNonUFS)"),
|
||||
*FPaths::ConvertRelativePathToFull(ModulesDir));
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FString> ModFiles;
|
||||
FM.FindFilesRecursive(ModFiles, *ModulesDir, TEXT("*.lpmod"), true, false);
|
||||
ModFiles.Sort();
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Donnees : %s | %d module(s) | ModLauncher.config %s"),
|
||||
*FPaths::ConvertRelativePathToFull(ModulesDir), ModFiles.Num(),
|
||||
FM.FileExists(*FPaths::Combine(ModulesDir, TEXT("ModLauncher.config"))) ? TEXT("present") : TEXT("absent (tous les modules par defaut)"));
|
||||
|
||||
for (const FString& ModFile : ModFiles)
|
||||
{
|
||||
const FString ModDir = FPaths::GetPath(ModFile);
|
||||
TArray<FString> Files;
|
||||
FM.FindFilesRecursive(Files, *ModDir, TEXT("*.*"), true, false);
|
||||
|
||||
TMap<FString, int32> PerExt;
|
||||
for (const FString& F : Files) { ++PerExt.FindOrAdd(FPaths::GetExtension(F).ToLower()); }
|
||||
PerExt.ValueSort([](int32 A, int32 B) { return A > B; });
|
||||
|
||||
FString Detail;
|
||||
for (const auto& It : PerExt) { Detail += FString::Printf(TEXT(" %s=%d"), *It.Key, It.Value); }
|
||||
UE_LOG(LogLifePlay, Display, TEXT(" %-22s %5d fichier(s) :%s"), *FPaths::GetBaseFilename(ModFile), Files.Num(), *Detail);
|
||||
}
|
||||
}
|
||||
|
||||
void ULPSessionLogSubsystem::LogSessionSummary() const
|
||||
{
|
||||
if (!Counter.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<TPair<FString, int32>> Categories, Signatures;
|
||||
int32 Overflow = 0;
|
||||
Counter->Snapshot(Categories, Signatures, Overflow);
|
||||
|
||||
const auto ByCountDesc = [](const TPair<FString, int32>& A, const TPair<FString, int32>& B) { return A.Value > B.Value; };
|
||||
Categories.Sort(ByCountDesc);
|
||||
Signatures.Sort(ByCountDesc);
|
||||
|
||||
UE_LOG(LogLifePlay, Display, TEXT("===== LifePlay : bilan de session (%.0f s) ====="), FPlatformTime::Seconds() - StartTime);
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Messages par categorie et verbosite (20 premieres) :"));
|
||||
for (int32 i = 0; i < FMath::Min(20, Categories.Num()); ++i)
|
||||
{
|
||||
UE_LOG(LogLifePlay, Display, TEXT(" %8d %s"), Categories[i].Value, *Categories[i].Key);
|
||||
}
|
||||
|
||||
UE_LOG(LogLifePlay, Display, TEXT("Avertissements et erreurs : %d signature(s) distincte(s)%s ; les %d plus frequentes :"),
|
||||
Signatures.Num(), Overflow ? *FString::Printf(TEXT(", %d message(s) hors plafond"), Overflow) : TEXT(""), TopSignatures);
|
||||
for (int32 i = 0; i < FMath::Min(TopSignatures, Signatures.Num()); ++i)
|
||||
{
|
||||
UE_LOG(LogLifePlay, Display, TEXT(" %8d %s"), Signatures[i].Value, *Signatures[i].Key);
|
||||
}
|
||||
}
|
||||
36
Source/LifePlay/LPSessionLogSubsystem.h
Normal file
36
Source/LifePlay/LPSessionLogSubsystem.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Portage UE 5.5 - lot L3 (journalisation). Voir Docs/Migration/04_Cahier_Des_Charges.md.
|
||||
//
|
||||
// Sous-systeme d'instance de jeu : cree et detruit avec GI, sans aucune modification de
|
||||
// Blueprint. Il n'altere aucun message existant (les comparaisons A/B avec la build 4.27
|
||||
// reposent sur ces messages) ; il AJOUTE :
|
||||
// - au demarrage, un resume : versions, modules de donnees presents, fichiers par type ;
|
||||
// - en fin de session, un bilan : volume de journal par categorie et verbosite, et les
|
||||
// signatures d'avertissements/erreurs les plus frequentes, dedoublonnees.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Subsystems/GameInstanceSubsystem.h"
|
||||
#include "LPSessionLogSubsystem.generated.h"
|
||||
|
||||
class FLPLogCounter;
|
||||
|
||||
UCLASS()
|
||||
class LIFEPLAY_API ULPSessionLogSubsystem : public UGameInstanceSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
virtual void Deinitialize() override;
|
||||
|
||||
/** Ecrit le bilan courant sans attendre la fin de session (console : LifePlay.LogSummary). */
|
||||
UFUNCTION(BlueprintCallable, Category = "LifePlay|Log")
|
||||
void LogSessionSummary() const;
|
||||
|
||||
private:
|
||||
void LogStartupSummary() const;
|
||||
|
||||
TSharedPtr<FLPLogCounter> Counter;
|
||||
double StartTime = 0.0;
|
||||
};
|
||||
|
|
@ -24,7 +24,8 @@ public class LifePlay : ModuleRules
|
|||
// UNavigationQueryFilter dans sa signature, et LifePlayEditorTools l'inclut.
|
||||
PublicDependencyModuleNames.Add("NavigationSystem");
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||
// Portage 5.5 (L3) : ULPSessionLogSubsystem lit UGeneralProjectSettings (nom et version du jeu)
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { "EngineSettings" });
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@
|
|||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, LifePlay, "LifePlay" );
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogLifePlay);
|
||||
|
|
|
|||
|
|
@ -4,3 +4,6 @@
|
|||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
// Portage 5.5 (lot L3) : categorie de journal propre au jeu. Resume de demarrage et bilan
|
||||
// de session : ULPSessionLogSubsystem.
|
||||
LIFEPLAY_API DECLARE_LOG_CATEGORY_EXTERN(LogLifePlay, Log, All);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ function Get-LogStats([string]$Path) {
|
|||
Erreurs = @($lines -match ': Error: ').Count
|
||||
Avertissements = @($lines -match ': Warning: ').Count
|
||||
LogLifePlay = @($lines -match 'LogLifePlay').Count
|
||||
TraceModding = @($lines -match 'LogLPModdingTrace').Count
|
||||
TraceModding = @($lines -match 'LogLPModdingTrace|LogModdingExtra').Count
|
||||
_Proprietes = $noneProps | Group-Object | Sort-Object Count -Descending | Select-Object -First $Top Count, Name
|
||||
_Fichiers = $files
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue