Ogre 异常处理和日志

来源:互联网 发布:王安石变法总结 知乎 编辑:程序博客网 时间:2024/04/30 08:24

Mage小组

Email: norman_chen@163.com 

renwind@163.com

QQ: 18725262

http://www.173d8.com

http://blog.csdn.net/pizi0475

日志管理

用文件来记录Ogre系统初始化、运行、结束以及调试信息。使用日志便于我们调试程序。

Ogre日志系统的组成

Ogre日志系统由两个类组成:Log类与LogManager。下面我们分别来看这两个类。

Log

代表用于记录信息的日志。Log类的一个对象对应于一个日志文件。

Log类提供了向日志文件写信息的函数logMessage,其定义如下:

    void logMessage(const String& message, LogMessageLevel lml = LML_NORMAL);

              参数messageString类型的变量,存储要写入的信息。

              参数lml,指定传入信息的级别,来衡量一条信息的重要程度。        

参数值

信息的重要程度

LML_TRIVIAL

最低

LML_NORMAL

一般

LML_CRITIAL

最高

 

为了判断一条信息的重要性,从而决定是否将该信息写入日志文件。我们不光要衡量信息的重要程度,还应该同时考虑日志文件的重要程度。Log类提供了一个函数setLogDetail来设置日志文件的重要程度,其定义如下:

    void setLogDetail(LoggingLevel ll);

              参数ll,指定该日志文件的级别,来衡量日志文件的重要程度。

 

参数值

代表日志文件的重要程度

LL_LOW

最低

LL_NORMAL

一般

LL_BOREME

最高

LogManager

管理所有Log类的对象,也就是管理所有的日志文件。并负责向日志文件中输出信息。

LogManager类提供了创建Log对象的成员函数createLog

注意,不要用Log类直接创建对象,而要用LogManagercreateLog函数来创建Log对象。因为这保证了:使LogManager维护所有的Log对象,通过LogManager可以方便地进行查找等操作。createLog定义如下:

 Log* createLog( const String& name, bool defaultLog = false, bool debuggerOutput = true );

     参数nameString类型的,它指定所创建日志文件的文件名,如Ogre.log

     参数defaultLog是布尔类型的,如果为true,则把当前创建的日志文件设置为LogManager默认的日志文件。调用LogManager的接口函数都对此文件生效。

     参数debuggerOutput是布尔类型的,如果为true,则不只向日志文件中输出信息,还向调试窗口中输出信息。

成员函数

     成员函数getLog可以通过文件名得到其代表的Log对象。它的定义如下:

     Log* getLog( const String& name);

     参数name指定了要查找的日志文件名。

 

     设置默认Log文件级别的成员函数:

     void setLogDetail(LoggingLevel ll);

     参数ll指定默认日志文件的重要程度,它可以是:

 

参数值

信息的重要程度

LML_TRIVIAL

最不重要

LML_NORMAL

一般

LML_CRITIAL

最重要

 

     成员函数getDefaultLog返回默认的Log对象

     Log* getDefaultLog();

 

     成员函数logMessage向默认的日志文件中写入信息,它有两种形式:

     void logMessage( const String& message, LogMessageLevel lml = LML_NORMAL);

     void logMessage( LogMessageLevel lml, const char* szMessage, ... );

     参数lml指定传入的信息的重要程度。

     第一个logMessage可以向日志文件中写入一条信息。第二个logMessage可以写入接收的多条信息。

Ogre日志系统的使用举例

程序概述

重载ExampleApplication内的createScene函数,在函数内创建一个名为Test.log的日志文件,并向该文件中写入一段信息。

部分代码

    void createScene(void)

    {

         Log *p_Log = LogManager::getSingleton().createLog( "test.log" );

         p_Log->logMessage( "I write a message to test.log" );

}

     createScene函数中,首先调用LogManagercreateLog成员函数来创建一个名为test.log的日志文件,并用p_Log保存此日志文件的指针。然后,我们通过Log的成员函数logMessagetest.log中写入一句话。

     执行一遍程序后,你会发现程序可执行文件所在的目录下多了一个名为test.log的文件,接着打开test.log,你会看见一行字:I write a message to test.log

当然,这个程序所做的并没有什么意义。它的目的只是演示一下Ogre日志系统的使用方法,

 

关于Ogre日志系统的应用,在下一部分异常处理中还有介绍。

 

异常处理

OgreC++内建的异常处理机制来处理错误。

使用异常处理的好处

首先,使用异常处理,可以使C++程序的两个部分相互通信(这两部分通常是分别开发的)。检测到异常的部分可以抛出异常,而另一部分可以捕获异常并做进行处理。

其次,使用异常处理,可以避免用返回值来定义错误。当有错误发生时,一个异常就会被抛出,这个异常里面封装了发生错误的详细信息。

 

Ogre中使用异常的例子:

    try {

        app.go();

} catch( Ogre::Exception& e ) {

   // 处理异常

    }

首先,这段代码分为两部分,分别由trycatch包围起来,如果app.go()有错误产生,则Ogre::Exception类型的异常会被抛出,catch部分捕获并处理这个异常。

 

其次,如果没有异常,这段代码可能会是这样的:

        if( !app.go() )

     {

        // 处理错误

     }

       我们不得不用函数的返回值来定义错误。这样,不但错误信息不准确,而且导致程序的错误处理代码与应用代码混在一起,不易于维护。

 

Ogre对异常处理的支持    

Ogre::Exception

Ogre定义了自己的异常类型Ogre::Exception,它记录了错误的详细信息(错误编号、详细描述、错误发生的文件名、行数等)。当有错误发生时,Ogre会抛出这个类型的异常,并把这个异常记录的错误信息写入到LogManager的默认日志文件中。

Ogre::Exception提供了一个成员函数getFullDescription,它的返回值是String类型的,保存了对错误的详细描述。

Ogre::Exception维护了一个函数名称堆栈。提供了一个入栈函数_pushFunction,它接收一个String类型的参数。和一个出栈函数_popFunction。我们需要在每个函数的起始位置调用_pushFunction,并把该函数的函数名当作参数传入。在结束位置调用_popFunction

 

几个辅助宏

Except(num, desc, src ) 相当于throw( Exception( num, desc, src, __FILE__, __LINE__ ) )a, b, c分别代表错误编号,错误描述与错误发生所在的函数。__FILE____LINE__是系统变量,它们的意思是错误发生时的代码文件和行数。

OgreGuard( a ) 相当于Exception::_pushFunction( ( a ) )

OgreUnguard( a ) 相当于Exception::_popFunction( ( a ) )

 

Ogre异常处理的例子

部分代码:

// Log.cpp

    MaterialApplication app;

 

try {

    app.go();

} catch( Exception& e ) {

#if OGRE_PLATFORM == PLATFORM_WIN32

// 如果是Win32平台,则弹出一个错误对话框,显示错误的详细信息。

MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

       // 如果不是Win32平台,则直接输出错误的详细信息。

    fprintf(stderr, "An exception has occured: %s/n",

            e.getFullDescription().c_str());

 

// Log.h

// 定义一个函数Wudi1,在内部武断地抛出一个异常

void Wudi1()

{

       OgreGuard( "Wudi1" );                // 将函数名称压入堆栈

       Except( 888, "wudi", "Wudi1" );    // 抛出一个异常

       OgreUnguard();                           // 弹出栈顶元素

}

 

// 定义一个函数Wudi2,在内部调用Wudi1

void Wudi2()

{

       OgreGuard( "Wudi2" );                // 将函数名称压入堆栈

       Wudi1();                                     // 调用Wudi1

       OgreUnguard();                           // 弹出栈顶元素

}

 

class MaterialApplication : public ExampleApplication

{

public:

    MaterialApplication() {}

 

protected:

// 重载createScene函数

void createScene(void)

{

       Wudi2();                // 调用Wudi2函数

    }

};

 

执行结果

运行程序,会弹出一个错误对话框:

 

在默认的日志文件Ogre.log中也记录了同样的错误信息:

5:49:48: An exception has been thrown!

 

-----------------------------------

Details:

-----------------------------------

Error #: 888

Function: Wudi1

Description: wudi.

File: d:/programming exercises/ogre例子/ogre99f/renwind_log/log.h

Line: 32

Stack unwinding: Wudi1()<-Wudi2()<-<<beginning of stack>>

最后一行,显示了函数名称队列的展开。我们可以看出,栈底的函数是Wudi2(),异常发生在栈顶Wudi1()函数内。

 

通过Ogre的日志系统与异常处理,我们可以迅速的查看系统运行的信息,快速定位错误发生的位置,并动态地处理错误。