3

Recently my team has programmed a custom developer console in a video game which can easily be hidden or displayed, because it's more comfortable and less of a hassle.

The Console class contains a log(string) function which should be easily accessible from anywhere outside of it, without having to reference it everywhere.

How could we design a method that can be easily accessed from the outside of the class? For example printing a message to the console in C++ is as simple as:

cout << "Message";

Well we want something similar at best, maybe Console::log(), but we are clueless how to achieve a proper way of doing it.

Cheers.

6

The proper way to do this is to use a logging framework. The Apache (or ones modeled on) 'log4xyz' (where xyz is the language choice - log4j for java, log4net for .net, log4cpp for C++) is one of the more common styles.

Since this is specifically about C++, I'll be referencing log4cpp (though I'm most familiar with log4j).

log4cpp has a number of log levels - emerg, fatal, alert, crit, error, warn, notice, info, and debug (docs).

You set up the appenders (an appender is a way to log something - out to a database is one appender, to a file is another, to a file rotating every 24h is another one, to a file rotating every 1 Megabyte of data is another one, etc...) and the categories (a category is a group of things that log the same way). This can be done either in raw code, or in a property file.

The raw code would look something like this:

int main(int argc, char** argv) {
    log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout);
    appender1->setLayout(new log4cpp::BasicLayout());

    log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", "program.log");
    appender2->setLayout(new log4cpp::BasicLayout());

    log4cpp::Category& root = log4cpp::Category::getRoot();
    root.setPriority(log4cpp::Priority::WARN);
    root.addAppender(appender1);
    root.addAppender(appender2);

    ...
}

A property file example can be seen on the webpage for the project.

In this code, you've got two appenders that are attached to the root category - one hooked up to an output stream, the other to a file. The root category is set to log things at the warn level or higher.

To invoke the loggers you would do things such as:

root.warn("%d + %d == %s ?", 1, 1, "two");
root << log4cpp::Priority::ERROR << "Streamed root error";
root << log4cpp::Priority::INFO << "Streamed root info";

And here you have three messages sent to the root logger, one with warn, one with error priority, one with info. As the info is lower than the warn error, it wouldn't get logged. Note the two different styles of logging.

Under this model, you would create an appender that logs to your Console object and hook it up to some category. Adjusting the log level on that category would then allow you to determine what shows up on the console.

Code for getting an arbitrary category looks something like:

log4cpp::Category& root = log4cpp::Category::getRoot();

log4cpp::Category& sub1 = 
    log4cpp::Category::getInstance(std::string("sub1"));

This could be invoked from anywhere. This allows you to abstract away the actual connection of the code to the console (or other logger). You could do things such as set up a remote logger (it logs to something listening on the network) so that you could have an entire screen of logging going on on a different computer (and to a file) as you test the game - without having to open up a local console to capture that information.

  • 2
    Thanks a lot, it's quite a bit of work but IMHO it's better than the default console, especially because of how slow that works. The game starts almost 2x faster since we made this console. Thanks and sorry for the late upvote. – Bugster Sep 14 '13 at 9:00
  • Sorry for the late question but how do I make it "catch" what I want to log? For example I have my log() method and an appender, how do I make it so when I call something like root << "Some text" it calls the log() method? – Bugster Oct 23 '13 at 19:34
  • 1
    Normally, one would do it the other way around. You call log(message) which then calls root << message or the like. The other approach would be to make a class that extends log4cpp::Appender and add it to the logger (you can have multiple appenders attached to a single logger). – user40980 Oct 23 '13 at 19:48
  • Yeah but the root text goes in the actual OS console, I made the console from 0, I even calculate the position of the text displayed in the console, so basically all I need is a convenient way of getting the text that got logged, and when it got logged. A listener for example. – Bugster Oct 23 '13 at 19:50
  • @Bugster thats exactly what an appender is. It listens to the log messages and then handles them some way. Consider (granted, the log4j java one) - code.google.com/p/twitter-log4j which logs to twitter. It listens for messages sent, and then goes and posts them to twitter. You could write one that just keeps a count in some static variable, logs to a database, logs to the network, or whatnot. – user40980 Oct 23 '13 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.