日志系统对于消息记录和Debug都非常重要, 这里对Ogre的日志系统做简要分析, Ogre的日志系统不是很复杂, 实现了一个基本的日志系统.

大致的类图如下:

 log Log类 : 日志系统的核心类, 用来实现日志的记录, 其具体是以std::ofstream来实现日志的存储, 可以设置是否记录时间, 也可以为Log类添加LogListener来对日志进行用户级的处理. 要实现自己对日志的处理, 需要用户实现LogListener接口, 并实现messageLogged函数. 例如, 获取当前系统的日志内容,并在自定义的控件内显示当前日志内容.

 


LogListener类 : 用户对日志的监听接口, 实现此接口并注册给Log类, 例如在TankWarEditor中需时刻获取日志内容, 每当记录新的信息时在控件内显示.一个Log可注册多个LogListener.

01 /**
02 自定义日志类监听器, 用于重新将日志定位到指定控件显示.
03 需要在Ogre类生成前生成LogManager,并生成默认日志Log,
04 将此监听器提交给Log.
05 */
06 class LogDockWidget;
07 namespace TankWar
08 {
09 // 重新定向日志, 输出到文本编辑器器里
10 class TankWarLog : public Ogre::LogListener
11 {
12 public:
13 TankWarLog();
14
15 // 将日志文件绑定到指定Qt控件
16 void addLogWidget(LogDockWidget* textEdit);
17
18 // 记录日志过滤
19 void messageLogged( const Ogre::String& message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName);
20
21 // 获取日志类记录型
22 LOGINFO getLogType(const Ogre::String &msg);
23 private:
24 LogDockWidget *mLogWidget;
25 std::stringstream mBack;
26 };
27 }
28
29 //cpp--------------------------------------------------------
30
31 namespace TankWar
32 {
33
34 TankWarLog::TankWarLog() : mLogWidget(0)
35 {
36 }
37
38 void TankWarLog::addLogWidget(LogDockWidget* textEdit)
39 {
40 mLogWidget = textEdit;
41 mLogWidget->logMessage(mBack.str(), LOG_NORMAL, true);
42
43 mBack.clear();
44 }
45
46 void TankWarLog::messageLogged( const Ogre::String& message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName)
47 {
48 std::string m = message.c_str();
49 if (mLogWidget)
50 mLogWidget->logMessage(m);
51 else
52 mBack << formatLog(m) << std::endl;
53 }
54 }
55
56 // 具体使用---------OgreApp.cpp------------
57
58 // 预先创建定向Log, 输出到输出台
59 mDefaultLog = new TankWarLog();
60 Ogre::LogManager *logManager = new Ogre::LogManager();
61 Ogre::Log *log = logManager->createLog("TankWarEditor.log", true, true);
62 log->addListener(mDefaultLog);
63 mRoot = new Ogre::Root;


Log::Stream类 : Log的内部类, 重载了<<输出流, 可以方便的对对象进行输出, 内部使用sstringstream对新日志内容进行缓存, 当Log::Stream对象被销毁或者调用<<Log::Stream::Flush时刷新到到文本.

1 Ogre::Log *log = Ogre::LogManager::getSingletonPtr()->getDefaultLog();
2 Ogre::Log::Stream stream = log->stream();
3 stream<< "this is a log demo1" << std::endl; // 记录日志
4 stream<< "this is a log demo2" << std::endl; // 记录日志
5 stream<< "this is a log demo3" << std::endl; // 记录日志
6 stream<<Ogre::Log::Stream::Flush(); // 强制刷新到文件, 超出作用域会自动刷新


LogManager类 : 日志管理器, 用来统管系统内所有日志对象, 在Ogre::Root创建时会检查LogManager, 如果没有创建, 则会新建一个全局LogManager(实现Singleton接口), 并自动生成一个默认的Log对象,所以如果需要自行控制Log,例如重定向日志输出, 我们需要在Ogre::Root对象声称之前自行创建LogManager, 创建一个Log对象,并为其注册LogListener的实现.如上面的实现.

1 // 预先创建定向Log, 输出到输出台
2 mDefaultLog = new TankWarLog();
3 Ogre::LogManager *logManager = new Ogre::LogManager();
4 Ogre::Log *log = logManager->createLog("TankWarEditor.log", true, true);
5 log->addListener(mDefaultLog);
6 mRoot = new Ogre::Root;


Exception 类 : Ogre异常类基类, 我们发现每当有异常发生时其详细内容会记录到Log文件, 其实Exception类的构造函数会调用LogManager获取默认的Log并对错误进行记录.

01 Exception::Exception(int num, const String& desc, const String& src, const char* typ, const char* fil, long lin) :
02 line( lin ),
03 number( num ),
04 typeName(typ),
05 description( desc ),
06 source( src ),
07 file( fil )
08 {
09 // Log this error, mask it from debug though since it may be caught and ignored
10 if(LogManager::getSingletonPtr()){
11 LogManager::getSingleton().logMessage(this->getFullDescription(), LML_CRITICAL, true);}
12
13 }