4
\$\begingroup\$

I'm currently making a game using C++. I eventually needed a logger, but I wasn't satisfied with any of the existing solutions. After a while, I wrote my own header called log.h:


#include <iostream>
#include <iomanip>
#include <string>
#include <ctime>
#include <fstream>
#include <sstream>

class Logger {
    private:
        std::string logFileName = "log.txt";
        bool print = true;
        bool save = true;
        
        void log(std::string s, std::string i) {
            time_t cuT;
            struct tm *loT;
            time(&cuT);
            loT = localtime(&cuT);
            
            std::stringstream ss;
            ss << std::setw(2) << std::setfill('0') << loT->tm_hour << ':';
            ss << std::setw(2) << std::setfill('0') << loT->tm_min << ':';
            ss << std::setw(2) << std::setfill('0') << loT->tm_sec;
            ss << i << s << "\n";
            
            if(save) {
                std::ofstream of;
                of.open(logFileName, std::ios_base::app | std::ios::out);
                of << ss.rdbuf();
            }
            if(print) {
                std::cout << ss.str();
            }
        }
    public:
        void configure(std::string logFileName_, bool print_, bool save_) {
            this->print = print_;
            this->save = save_;
            this->logFileName = logFileName_;
        }
        
        void note(std::string s) {
            log(s, " NOTE: ");
        }
        
        void error(std::string s) {
            log(s, " ERROR: ");
        }
        
        void warn(std::string s) {
            log(s, " WARNING: ");
        }
};

My library can print the current time as well as other information to the console and save this data into a file. But I did a couple of benchmarks and it turns out that my logging program is a lot slower compared to the normal std::cout.

Since speed is crucial for games, I wanted to ask whether there are ways to optimize my program or, if necessary, to improve it.

\$\endgroup\$
8
  • \$\begingroup\$ By the way, sorry if my question isn't perfect. I'm completely new to StackExchange. :) \$\endgroup\$ Commented Oct 20, 2020 at 19:53
  • 2
    \$\begingroup\$ Can you describe what was unsatisfactory with the other solutions, and what requirements you have set for your logger? \$\endgroup\$
    – G. Sliepen
    Commented Oct 20, 2020 at 20:06
  • 2
    \$\begingroup\$ One of the biggest things about a logging system is being able to enable/disable or set logging level (so less import stuff is not logged). I don't see any of that. Also the concept of log sinks does not seem to exist so you can either log to cout or a file. what about syslog? \$\endgroup\$ Commented Oct 20, 2020 at 20:38
  • 2
    \$\begingroup\$ Some minor suggestions - 1) if the strings may be heavy, better pass them by const reference const std::string& to avoid copy 2) add the constructor, which call configure function \$\endgroup\$ Commented Oct 20, 2020 at 21:17
  • 1
    \$\begingroup\$ Sinks are objects that encapsulate some behavior for handling a logged message. The logger itself doesn't have any notion of whether the message is going to stdout, a file, a database, or whatever, it just has a bunch of sinks that it hands the messages off to. The sinks do the work of actually outputting the message somewhere. \$\endgroup\$ Commented Oct 20, 2020 at 22:22

1 Answer 1

4
\$\begingroup\$

Some things to note;

  1. When doing time related things in C++, use std::chrono, not clock().
  2. Your log will not work if it's used in multiple files. Instead of being in a .h file, put the definition of the logger in a cpp file and build both together.
  3. Use include guards for header files
    #ifndef LOG_H
    #define LOG_H

    // Code

    #endif
  1. Define constructors and use them properly and get rid of configure.
    Log(std::string fileName, bool printToConsole, bool saveToFile) :
        logFileName(fileName),
        print(printToConsole),
        save(saveToFile)
    {
    }
  1. Do not use underscores for naming things. General rules for c++ are class names are PascalCase and functions are camelCase.
  2. std::cout is slow, consider using sprintf to make it faster.
  3. Don't write to a file every time you call log. Opening files and writing to them is slow. Consider writing to the file on a timestamp or aggregating a few logs before writing them.
\$\endgroup\$
2
  • 3
    \$\begingroup\$ To build on #7, try storing the logs in some sort of buffer. You could flush the buffer after a certain size, and this is common in loggers, but it's worth noting that if your program crashes, anything in the buffer disappears. Most logging systems also flush the buffer after a certain time interval too, just to minimize the likelihood of that happening to any particular message. Doing that though will probably involve adding another thread to run the pump, but it would be a good exercise. \$\endgroup\$ Commented Oct 20, 2020 at 22:17
  • \$\begingroup\$ An alternative to #7: do write every log to file, but keep the file on a few orders of magnitude faster RAM disk (which can be periodically copied to hard disk). If the program crashes, the RAM disk will not be affected and you can still see all your logs. If the system crashes (a much less likely event) you loose the logs since the last backup. \$\endgroup\$
    – Pietro
    Commented Mar 13 at 13:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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