views:

50

answers:

2

I have a custom logging system which allows me to send information to a log file and the console depending on the verbosity currently selected. Right now, the trouble I am having is with the output to the file, with output to the console working fine.

Here is an example:

     ilra_talk << "Local IP: " << systemIP() << " |  Hostname: " << systemhostname() << endl;
     // the systemIP() and systemhostname() functions have already been defined

This should result in the current local IP and hostname of the system being printed to a file. However, it is only resulting in the information being printed to the console, despite how the function is overloaded to result in it printing to both.

I've outlined the code below. Any assistance is appreciated (as always).

A definition currently exists for ilra_talk which results in a new class object being created:

#define ilra_talk ilra(__FUNCTION__,0)

The class definition the following:

class ilra
{
    static int ilralevel_set; // properly initialized in my main .cpp
    static int ilralevel_passed; // properly initialized in my main .cpp
    static bool relay_enabled; // properly initialized in my main .cpp
    static bool log_enabled; // properly initialized in my main .cpp
    static ofstream logfile; // properly initialized in my main .cpp
public:
    // constructor / destructor
    ilra(const std::string &funcName, int toset)
    {
        ilralevel_passed = toset;
    }
    ~ilra(){};

    // enable / disable irla functions
    static void ilra_verbose_level(int toset){
        ilralevel_set = toset;
    }
    static void ilra_log_enabled(bool toset){
        log_enabled = toset;

        if (log_enabled == true){
            // get current time
            time_t rawtime;
            time ( &rawtime );

            // name of log file (based on time of application start)
            stringstream logname_s;
            string logname = "rclient-";
            logname_s << rawtime;
            logname.append(logname_s.str());

            // open a log file
            logfile.open(logname.c_str());
        }
    }

    // output
    template <class T>
    ilra &operator<<(const T &v)
    {
        if(log_enabled == true){ // log_enabled is set to true
            logfile << v; 
            logfile << "Test" << endl;  // test will show up, but intended information will not appear
            }
        if(ilralevel_passed <= ilralevel_set)
            std::cout << v;
        return *this;
    }

    ilra &operator<<(std::ostream&(*f)(std::ostream&))
    {
        if(log_enabled == true) // log_enabled is set to true
            logfile << *f;
        if(ilralevel_passed <= ilralevel_set)
            std::cout << *f;
        return *this;
    }
};  // end of the class
A: 

It looks like "Test" is most likely being printed from somewhere else, and that in fact log_enabled isn't set at the point you're inserting data into the stream. Have you tried unconditionally inserting the data into the logfile stream, or printing out log_enabled every time operator<< is called?

Alternately, cout has type ostream and logfile has type ofstream. Is it possible that your function to forward manipulators isn't doing its job for ofstream?

Mark B
+1  A: 

I see nothing totally wrong with the code, though I personally would have made two changes:

  1. Put the logfile.flush() into the ilra::~ilra(). Logging and buffering are no friends.

  2. Change static ofstream logfile to static ofstream *logfile: allocated/delete it in ilra_log_enabled() and add NULL check in the << operators. I prefer objects with explicit life cycle.

Overall, since logging is a performance hog, I never use the iostreams for it and stick with the printf()-like macros: logging check is made without a function call, right in the macro. That has important side-effect: if no logging is needed then the parameter list isn't evaluated at all. In your case it is impossible to avoid the functions being called, e.g. systemIP() and systemhostname(), since the check for log level/etc is done after they are already have been called. With the macros I can also for example remove completely debug-level logging from release builds (or corollary: in a debug build I can have as much logging as I want).

Dummy00001
You were right -- there was nothing wrong with the code. There was simply a large amount of whitespace being created at the top of the text file which resulted in the actual contents being moved to the bottom.Do you have any examples of the printf macros?
BSchlinker
@BSchlinker: i have no code available on net. neither what I have at hand would fit here in the comment section. Simplest example: `int log_level = 0;` `#define TRACE( level, fmt, ... ) do { if (level >= log_level) { fprintf( stderr, "%s:%d:" fmt "\n", __FILE__, __LINE__, __VA_ARGS__ ); }; } while (0)` - It's not using the GCC extension for __VA_ARGS__, thus to trace a plain string one has to use `TRACE(DEBUG, "%s", "hi")` instead of more intuitive `TRACE(DEBUG, "hi")`. Check your compiler/preprocessor documentation about variadic macros support.
Dummy00001
@BSchlinker: ... uhm... comment formatting sucks. Anyway, here is the link to GCC variadic macro documentation - http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html - to know what to look for in other compilers
Dummy00001