C plus plus:Simple XML Logging System
From GPWiki
The wiki is now hosted by GameDev.NET at wiki.gamedev.net. All gpwiki.org content has been moved to the new server. However, the GPWiki forums are still active! Come say hello.
[edit] XML?XML has been a really big buzz-word for a while, but it seems that the XML bird has some trouble taking off. A lot of people have yet to find out about XML. XML uses a very simple HTML-like syntax for storing data. It's basically just a left angled bracket, the tag name, a right angled bracket, the data for the tag, a left angled bracket and a slash, the tag name and finishing off with another right angled bracket. Like so: < TAG > DATA < / TAG > Then there are all sorts of things you can do to complicate things when you get more experienced, like adding setting-tags inside the tag-definition, including tags inside other tags and much more stuff like that. The beautiful aspect of XML is in its simplicity. Because it has a certain set of definitions of how the data is stored, it's possible for everyone to read XML straight and in most cases get a very good idea of what's being stored, and most importantly it allows for data to be seamlessly shared between different computer, platforms, companies etc.. If you couple XML with scripting capabilities such as XSL stylesheets you can present the data stored in the XML in any way you like, and changing how the data is displayed doesn't change or modify the original data in any way. If you haven't experimented with XML I suggest that you go ahead and give it a twirl.
[edit] XML used for loggingBeing able to dump infomation from your game to a file is great for testing and debugging purposes. However, many logging systems just use brute force to do this; dumping game data into a file in raw readable text. For simple applications this can work, but if you want your logging system to be readable and simple and flexible, then XML comes to the rescue!
[edit] LogHandler.hppFile: LogHandler.hpp #ifndef LOGHANDLER_HPP #define LOGHANDLER_HPP #if (_MSC_VER >= 1300) #pragma once #endif // (_MSC_VER >= 1300) #include <fstream> #include <string> #include <vector> class CFile { std::string m_filename; public: CFile(std::string const & file_name) : m_filename(file_name) {} virtual ~CFile() {} void clear() { std::ofstream file_stream(m_filename.c_str(), std::ios_base::trunc); } template <typename T> bool write(T const & data) { std::ofstream file_stream(m_filename.c_str(), std::ios_base::app); // Set the append flag if (!file_stream) // Use operator! to check file-bit-state (fail & bad-bit) return false; file_stream << data; return file_stream; // writing data successful? operator void* or (return !!file_stream) operator! } }; class CLogHandler { private: std::vector<std::string> m_tags; std::size_t m_layer; protected: CFile* m_ptrFile; public: CLogHandler(std::string const & filename) : m_layer(0) { m_ptrFile = new CFile(filename); m_ptrFile->clear(); m_ptrFile->write("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>\n"); } virtual ~CLogHandler() { while (m_tags.empty() == false) { CloseTagTree(); } delete m_ptrFile; } void add_tag_tree(std::string const & tag_name) { m_ptrFile->write(std::string(m_layer, '\t') + '<' + tag_name + ">\n"); m_tags.push_back(tag_name); ++m_layer; } template <typename T> void add_tag(std::string const& name, T const & data) { m_ptrFile->write(std::string(m_layer, '\t') + '<' + name + '>'); m_ptrFile->write<T>(data); m_ptrFile->write(std::string("</") + name + ">\n"); } void add_comment(std::string const& comment) { m_ptrFile->write(std::string(m_layer, '\t') + "<!-- " + comment + " -->"); } void close_tag_tree() { if (m_tags.empty() == true || m_layer == 0) return; // any tag_tree exist? --m_layer; m_ptrFile->write(std::string(m_layer, '\t') + "</" + m_tags[m_tags.size() - 1] + ">\n" ); m_tags.pop_back(); } }; #endif // LOGHANDLER_HPP [edit] MyLogHandler.hppA wrapper class for the logging system might look something like this. File: MyLogHandler.hpp #ifndef MYLOGHANDLER_HPP #define MYLOGHANDLER_HPP #if (_MSC_VER >= 1300) #pragma once #endif // (_MSC_VER >= 1300) #include <string> #include <ctime> #include "LogHandler.h" class CMyLogHandler : public CLogHandler { public: CMyLogHandler(std::string const & file_name) : CLogHandler(file_name) { m_ptrFile->write("<?xml-stylesheet type=\"text/xsl\" href=\"stylesheet.xsl\" ?>\n\n"); add_comment("Created by XMLLogger"); } virtual ~CMyLogHandler() {} template <typename T> void write_log(std::string const & class_name, std::string const & function_name, std::string const & debug_type, T const& info, std::string const& file, const long line) { std::time_t time(std::time(NULL)); add_tag_tree("LogEntry"); add_tag("Class", class_name); add_tag("Function", function_name); add_tag("DebugType", debug_type); add_tag("Timestamp", std::ctime(&time)); add_tag<T>("Info", info); add_tag("Line", line); add_tag("File", file); close_tag_tree(); // LogEntry } }; #endif // MYLOGHANDLER_HPP [edit] Example UsageFile: main.cpp #include "MyLogHandler.h" #define COMPILE_TIMESTAMP __DATE__ " " __TIME__ #define FILE_INFO __FILE__, __LINE__ int main(int argc, char* argv[]) { CMyLogHandler log("DebugLog.xml"); log.add_tag_tree("Project"); log.add_tag("Name", "XMLLogger"); log.add_tag("Type", "Project"); log.add_tag("FileName", __FILE__); log.add_tag("Compiled", COMPILE_TIMESTAMP); log.add_tag_tree("CmdArguments"); for (int i(0); i < argc; ++i) log.AddTag("CmdArgument", argv[i]); log.close_tag_tree(); // CmdArguments log.add_tag_tree("CommentLines"); log.add_tag("CommentLine", "This is a test"); log.add_tag("CommentLine", "of multiple lines of"); log.add_tag("CommentLine", "comments."); log.close_tag_tree(); // CommentLines log.add_tag_tree("LogEntries"); log.write_log("Main", "main", "Info", "Starting up", FILE_INFO); log.write_log("Main", "main", "Info", "Second test", FILE_INFO); log.write_log("Main", "main", "Warning", "A fake warning", FILE_INFO); log.write_log("CLogHandler", "Write", "Info", "Writing down some stuff...", FILE_INFO); log.write_log("Main", "main", "Error", "A fake error!", FILE_INFO); log.write_log("Main", "main", "Critical", "Terminating", FILE_INFO); log.write_log("Main", "main", "Info", "(I'm fine, don't worry!)", FILE_INFO); log.write_log("Main", "main", "Info", "Look below it's a integer!", FILE_INFO); log.write_log("Main", "main", "Info", 666, FILE_INFO); log.close_tag_tree(); // LogEntries }
add_tag_tree("Project"); add_tag_tree("Files"); add_tag_tree("File"); add_tag_tree("CodeModule"); That would result in a XML-file that would like like this: <Project> <Files> <File> <CodeModule></CodeModule> </File> </Files> </Project> If you need to data, you have to use the add_tag()-function. Here's an example: add_tag_tree("Project"); add_tag_tree("Files"); add_tag_tree("File"); add_tag("FileName", "Test file"); add_tag_tree("CodeModule"); add_tag("SomeCode", "For each some object do something"); add_tag("Global", false); Would result in: <Project> <Files> <File> <FileName>Test file</FileName> <CodeModule> <SomeCode>For each some object do something</SomeCode> <Global>false<Global> </CodeModule> </File> </Files> </Project> The CLogHandler class automaticly closes all the open tag trees when the class is shut down, but if you want to add another <File> tag tree, for instance, you need to call close_tag_tree(). That will close the inner-most tag tree - which is the lastest created tag tree. To add another <File> tag tree, you should use the code below: add_tag_tree("Project"); add_tag_tree("Files"); add_tag_tree("File"); add_tag("FileName", "Test file"); add_tag_tree("CodeModule"); add_tag("SomeCode", "For each some object do something"); add_tag("Global", false); close_tag_tree(); // CodeModule close_tag_tree(); // File add_tag_tree("File"); add_tag_tree("CodeModule"); add_tag("SomeCode", "Do until true == false"); Would result in an XML-file with the following text: <Project> <Files> <File> <FileName>Test file</FileName> <CodeModule> <SomeCode>For each some object do something</SomeCode> <Global>false<Global> </CodeModule> </File> <File> <CodeModule> <SomeCode>Do until true == false</SomeCode> </CodeModule> </File> </Files> </Project> [edit] Displaying the XML fileIf you run the application, you should get a file called "DebugLog.xml" that you can open with any text editor or internet browser. It shouldn't be too hard to read, but what we want is to give the XML-file an elegant website-like appearence. For that purpose we need a XML stylesheet template, which is like a CSS-script, just for XML-files instead of for HTML-files. The stylesheet is a bit lengthy, so I won't post the code here, but you'll find a working stylesheet in the download below.
Added 24. March 2005 Updated 24. March 2005
[edit] Alternatives[edit] C/C++
[edit] Java
[edit] VB .NET
Categories: C plus plus | XML | Logging | Debugging | File I/O |


