Html Logs

posted in Beals Software
Published June 28, 2006
Advertisement
Somebody asked me today why I spent so much time working on my debug log. An exact quote is "It's just the debug log!"

Let us do a little comparison:
Text Log
Log w/ CSS
Log w/ HTML & CSS

The first has a few problems - the lines blend together and for a lot of people the letters tend to blend together. It also has no formatting and is squished (plus errors and such do not stick out.)

The second has almost all of the first's problem other than it is formatted and easier to read.

The last obviously is my favorite. It's easy to read, important parts stick out a lot more, and it doesn't blend (as much.)

Edit: It doesn't quite look as nice for you IE users since IE doesn't support 0 height HRs (meaning you have HRs in the section headers and firefox users don't) and it doesn't support the :before and :after tags. I'll be removing the HRs, but I can't fix the tag problem. *leers at readers using IE* you can though *cough*firefox*cough*.

I'm almost finished tweaking the code for the dftCommon module. Once that's done I'll upload the header, source, and lib file.

(Aside to Raymond - AIM won't let me send/recieve messages for some reason. Just wanted to let you know that I am doing work Malathedra work [grin].)
Previous Entry Jumping!
Next Entry Untitled
0 likes 5 comments

Comments

HopeDagger
God, I still love your debug logging system. *drool*




...Your's too, P16. [lol]
June 28, 2006 11:34 PM
Programmer16
[lol] Thanks!

Is there anybody reading this that would actually use it though? Here's a quick example of how to use it:


#include <dftCommon.h>
#pragma comment(lib, "dftCommon")
#include <iostream>

class State
{
	std::string m_Name;
public:
	State(const std::string& Name)
	{
		m_Name = Name;
	}

	void OnEnter()
	{
		dft::HtmlLog::GetCurrentLog()->StartSection(m_Name);
		dft::HtmlLog::GetCurrentLog()->LogDebugOutput("Entering %s", dft::DOT_TEXT, m_Name.c_str());
	}

	void OnExit()
	{
		dft::HtmlLog::GetCurrentLog()->LogDebugOutput("Exiting %s", dft::DOT_TEXT, m_Name.c_str());
	}
};

class Application
{
	dft::HtmlLog	m_Log;

	static bool LogListener(const std::string& Text, int nType)
	{
		// This strips HTML tags and then outputs the info to the console
		std::string StrippedText = "";

		int nStart		= 0;
		int nLastEnd	= 0;
		for(unsigned int nIndex = 0; nIndex < Text.size(); ++nIndex)
		{
			if(Text[nIndex] == '<')
			{
				nStart = nIndex;
				for(unsigned int nSubIndex = nIndex + 1; nSubIndex < Text.size(); ++nSubIndex)
				{
					if(Text[nSubIndex] == '<')
					{
						nIndex = nSubIndex - 1;
						break;
					}

					if(Text[nSubIndex] == '>')
					{
						StrippedText += Text.substr(nLastEnd, nStart - nLastEnd);
						nLastEnd	= nSubIndex + 1;
						nIndex		= nSubIndex + 1;
					}
				}
			}
		}

		if(nLastEnd != Text.size() - 1)
			StrippedText  += Text.substr(nLastEnd, Text.size() - nLastEnd);

		static std::string OutputTypes[] = { "Text", "Warning", "Error" };
		std::cout<<"["<<OutputTypes[nType]<<"]: "<<StrippedText.c_str()<<std::endl;
		return true;
	}
public:
	Application()
	{
		m_Log.Create("logname.html", "Html File Title", "style1.css\nstyle2.css");
		m_Log.SetUserListener(LogListener);
		dft::HtmlLog::SetCurrentLog(&m_Log);

		State Intro("IntroState");
		State MainMenu("MainMenuState");
		State Game("GameState");

		Intro.OnEnter();
		Intro.OnExit();
		MainMenu.OnEnter();
		MainMenu.OnExit();
		Game.OnEnter();
		Game.OnExit();
	}
};

int main()
{
	Application Appy;
	return 0;
}




You may ask "Why not just use a singleton?" Well, I had originally designed it so that I could have multiple logs, but I'm comtemplating switching to a singleton.

Anyway, I'll leave this entry for a few days. If you'd use it, leave a comment and if I get enough people that want it I'll throw it up here early (instead of releasing it with the rest of the library.) I'd appreciate it if those of you that wouldn't use it would tell me why (for example, bad code design, naming, you hate me, etc.)
June 29, 2006 12:16 AM
Telastyn
Eh, I probably won't use it, mostly because I've already got one I like, but partly because it strikes me as overly complex. I need logging to work no matter what, far moreso than I need the logging output to be pretty.
June 29, 2006 12:26 AM
Programmer16
Thanks for the input Telastyn!

The code for it is actually fairly simple. The interface has 10 functions:

HtmlLog();
~HtmlLog();

void Create(const std::string& Filename, const std::string& Title, const std::string& StyleSheets);
void StartSection(const std::string& Name);
void LogDebugOutput(const std::string& Text, int nType, ...);

void SetUserListener(USERLISTENER fpListner);
USERLISTENER GetUserListener() const;

static void	SetCurrentLog(HtmlLog* pLog);
static HtmlLog* GetCurrentLog();



The biggest chunk of code is in the LogDebugOutput() function, which is 13 lines long. Almost all of the HTML formatting is left up to the user and ALL of the 'pretty' is left up to the user (via CSS files.)

Also the output HTML is fairly clean. The example produces this:

<html>
<head>
<title>Html File Title</title>
<link rel=stylesheet type=text/css href=style1.css>
<link rel=stylesheet type=text/css href=style2.css>
</head>

<div class=SectionHeader><hr>
<font class=SectionHeader>IntroState</font>
<hr></div><div class=SectionBody>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Entering IntroState</font><br>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Exiting IntroState</font><br>
</div><br>

<div class=SectionHeader><hr>
<font class=SectionHeader>MainMenuState</font>
<hr></div><div class=SectionBody>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Entering MainMenuState</font><br>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Exiting MainMenuState</font><br>
</div><br>

<div class=SectionHeader><hr>
<font class=SectionHeader>GameState</font>
<hr></div><div class=SectionBody>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Entering GameState</font><br>
dftLog [01:09:51] - <font class=OUTPUTTYPE0>Exiting GameState</font><br>



I agree though, it does seem overly complex and is prone to errors (since you have to supply a file of your own.) I get around this by having making my logs global or putting them in my Application-esque class.

Again, thanks for the input!
June 29, 2006 12:56 AM
Telastyn
Eh, mine might be more complex in that case (currently ~4 target loggers, and 5 decorating loggers). All pretty simple code, but...
June 30, 2006 07:38 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement