文章

Lyra日志

Lyra日志

Lyra日志

虚幻日志学习

1.Lyra创建日志定义

在.h里面写

1
LYRAGAME_API DECLARE_LOG_CATEGORY_EXTERN(LogLyra, Log, All);

在.cpp里面写

1
DEFINE_LOG_CATEGORY(LogLyra);

这样定义的日志可以在其他文件进行使用. 需要修改的文件有:

头文件(.h)

LyraEditor.h

1
2
3
#pragma once
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(LogLyraEditor, Log, All);

LyraGamePhaseLog.h

1
2
3
#pragma once
#include "Logging/LogMacros.h"
DECLARE_LOG_CATEGORY_EXTERN(LogLyraGamePhase, Log, All);

LyraLogChannels.h

1
2
3
4
5
//自定义日志分类,快速定义问题所在
LYRAGAME_API DECLARE_LOG_CATEGORY_EXTERN(LogLyra, Log, All);
LYRAGAME_API DECLARE_LOG_CATEGORY_EXTERN(LogLyraExperience, Log, All);
LYRAGAME_API DECLARE_LOG_CATEGORY_EXTERN(LogLyraAbilitySystem, Log, All);
LYRAGAME_API DECLARE_LOG_CATEGORY_EXTERN(LogLyraTeams, Log, All);

LyraCheatManager.h

1
DECLARE_LOG_CATEGORY_EXTERN(LogLyraCheat, Log, All);

LyraGameSettingRegistry.h

1
DECLARE_LOG_CATEGORY_EXTERN(LogLyraGameSettingRegistry, Log, Log);

LyraReplicationGraph.h

1
DECLARE_LOG_CATEGORY_EXTERN(LogLyraRepGraph, Display, All);

源文件(.cpp)

LyraEditor.cpp

1
DEFINE_LOG_CATEGORY(LogLyraEditor);

LyraLogChannels.cpp

1
2
3
4
5
6
#include "LyraLogChannels.h"

DEFINE_LOG_CATEGORY(LogLyra);
DEFINE_LOG_CATEGORY(LogLyraExperience);
DEFINE_LOG_CATEGORY(LogLyraAbilitySystem);
DEFINE_LOG_CATEGORY(LogLyraTeams);

LyraCheatManager.cpp

1
DEFINE_LOG_CATEGORY(LogLyraCheat);

LyraGameSettingRegistry.cpp

1
DEFINE_LOG_CATEGORY(LogLyraGameSettingRegistry);

LyraReplicationGraph.cpp

1
E_LOG_CATEGORY( LogLyraRepGraph );

2.复习部分知识点

该日志文件位于引擎源码位置: \Epic\UE\UE_5.6\Engine\Source\Runtime\Core\Public\Logging\LogMacros.h

1.带有static的区分

注意区分

1
DEFINE_LOG_CATEGORY_STATIC(SourceFilterPresets, Display, Display);

带有Static的日志定义宏DEFINE_LOG_CATEGORY_STATIC 一个宏,用于将日志类别定义为 C++ 中的“静态”变量。此宏应仅在源文件中声明。仅能在该单个文件中访问。

DEFINE_LOG_CATEGORY_STATIC

2.多线程不安全

你可以继承 FOutputDevice,实现你自己的输出类。

3.日志详细级别

日志详细级别打印在控制台中打印在编辑器日志中文本颜色其他信息
致命(Fatal)✅ 是❌ 不适用❌ 不适用会话崩溃
错误(Error)✅ 是✅ 是🔴 红色❌ 不适用
警告(Warning)✅ 是✅ 是🟡 黄色❌ 不适用
显示(Display)✅ 是✅ 是⚫ 灰色❌ 不适用
日志(Log)❌ 否✅ 是⚫ 灰色❌ 不适用
冗长(Verbose)❌ 否❌ 否❌ 不适用❌ 不适用
极其冗长(VeryVerbose)❌ 否❌ 否❌ 不适用可使用日志掩码和特殊枚举值设置文本颜色

注意Fatal会导致崩溃 对应的日志定义在E:\Epic\UE\UE_5.6\Engine\Source\Runtime\Core\Public\Logging\LogVerbosity.h

3.讲解获取上下文对象方法

1
FString GetClientServerContextString(UObject* ContextObject = nullptr);

该函数在LyraExperienceManagerComponent.h中调用,用来打印Experience的加载日志.传递的对象是GameState.该对象具有网络同步的属性.

比较有意思的是这里面有一个全局变量GPlayInEditorContextString

1
2
3
4
5
#if WITH_EDITOR
// A debugging aid set when we switch out different play worlds during Play In Editor / PIE
// 在“编辑器中游戏”(PIE)模式下切换不同的游戏场景时会启用此调试辅助工具集
ENGINE_API FString GPlayInEditorContextString(TEXT("invalid"));
#endif

它主要是在编辑器下通过通过上下文对象获取调试字段.

1
2
3
4
5
6
7
// Update the debugging aid GPlayInEditorContextString based on the current world context (does nothing in WITH_EDITOR=0 builds)
// 根据当前的世界环境更新调试辅助工具 GPlayInEditorContextString(在 WITH_EDITOR=0 构建模式下不执行任何操作)
ENGINE_API void UpdatePlayInEditorWorldDebugString(const FWorldContext* WorldContext);

// Returns the Debug string for a given world (Standalone, Listen Server, Client #, etc)
// 返回指定世界的调试字符串(如独立运行模式、监听服务器模式、客户端编号等)
ENGINE_API FString GetDebugStringForWorld(const UWorld* World);

回到Lyra项目中,UpdatePlayInEditorWorldDebugString()这个函数的主要调用时间都是在场景世界切换,加载地图时调用.

回到Lyra项目中,GPlayInEditorContextStringUGameplayMessageSubsystem::BroadcastMessageInternal也有访问.主要为了调试时,获取消息发生的世界类型.

此处补充一段关于FWorldContext的注释,来自引擎源码:

/** FWorldContext
 *	A context for dealing with UWorlds at the engine level. As the engine brings up and destroys world, we need a way to keep straight
 *	what world belongs to what.
 *
 *	WorldContexts can be thought of as a track. By default we have 1 track that we load and unload levels on. Adding a second context is adding
 *	a second track; another track of progression for worlds to live on. 
 *
 *	For the GameEngine, there will be one WorldContext until we decide to support multiple simultaneous worlds.
 *	For the EditorEngine, there may be one WorldContext for the EditorWorld and one for the PIE World.
 *
 *	FWorldContext provides both a way to manage 'the current PIE UWorld*' as well as state that goes along with connecting/travelling to 
 *  new worlds.
 *
 *	FWorldContext should remain internal to the UEngine classes. Outside code should not keep pointers or try to manage FWorldContexts directly.
 *	Outside code can still deal with UWorld*, and pass UWorld*s into Engine level functions. The Engine code can look up the relevant context 
 *	for a given UWorld*.
 *
 *  For convenience, FWorldContext can maintain outside pointers to UWorld*s. For example, PIE can tie UWorld* UEditorEngine::PlayWorld to the PIE
 *	world context. If the PIE UWorld changes, the UEditorEngine::PlayWorld pointer will be automatically updated. This is done with AddRef() and
 *  SetCurrentWorld().
 *
 */
/*
*  FWorldContext 是用于在引擎层面处理 UWorld 的一个上下文环境。当引擎启动和销毁世界时,我们需要一种方法来明确区分哪些世界属于哪个世界。
*  WorldContext 可以被视为一条轨道。默认情况下,我们有一个轨道用于加载和卸载关卡。添加第二个上下文就是添加第二条轨道;为世界提供另一个存在的进展轨道。
*  对于游戏引擎,直到我们决定支持多个同时存在的世界之前,将只有一个 WorldContext。对于编辑器引擎,可能会有一个用于编辑器世界和一个用于 PIE 世界的 WorldContext。
*  FWorldContext 提供了管理“当前的 PIE UWorld*”的方法,以及与连接/前往新世界相关的状态。
*  FWorldContext 应该保持在 UEngine 类内部。外部代码不应保留指针或直接管理 FWorldContext。外部代码仍然可以处理 UWorld*,并将 UWorld* 传递给引擎级别的函数。引擎代码可以根据给定的 UWorld* 查找相关的上下文。
*  为了方便起见,FWorldContext 可以维护外部对 UWorld* 的指针。例如,PIE 可以将 UWorld* UEditorEngine::PlayWorld 与 PIE 世界上下文关联起来。如果 PIE UWorld 发生变化,UEditorEngine::PlayWorld 指针将自动更新。这是通过调用 AddRef() 和 SetCurrentWorld() 来实现的。
*/

参考文献

虚幻中的日志记录

本文由作者按照 CC BY 4.0 进行授权