Link to home
Start Free TrialLog in
Avatar of forums_mp
forums_mp

asked on

Log / Instrumentation Class

I'm interested in a creating a - we'll call it - Log (or Instrumentation) class.  My initial thoughts on the log class capabilities are:
1.    A member function that'll take std::ostream as an argument and print all accumulated logs.
2.    File stream, cout and stringstream log capabilities.
3.    Time Stamping.
4.    class 'object' registeration.  My intent here is to obtain visibility into a class's member data.   For ex:
          class  X {
          public:
            void Increment() { idx++; }
          private:
            int idx;
         };

//later:
        X xx;
Now xx would get 'registered' with the log class.  This offers visibility into the member data idx.

-------------------------------------------------------------------------------------------------------------------------------------
I'm unsure of an approach to items 2 and 4.  For items 1 and 3, one solution is as follows:

   #include <iostream>
    #include <ctime>
    #include <iomanip>
    #include <vector>
    #include <algorithm>
    #include <functional>
    #include <iterator>
    #include <sstream>
    using namespace std;

    struct PlainPrintPolicy
    {
       static string print(const string& str) { return str; }
    };

    struct TimestampPrintPolicy
    {
       static string print(const string& str);
    };

    string TimestampPrintPolicy::print(const string& str)
    {
       ostringstream ostr;
       time_t now;
       time(&now);
       tm *current = localtime(&now);
       ostr.fill('0');
       ostr << "["
            << current->tm_year+1900 << "-"
          << setw(2) << current->tm_mon+1 << "-"
          << current->tm_mday << " " 
          << setw(2) << current->tm_hour << "."
          << setw(2) << current->tm_min << "."
          << setw(2) << current->tm_sec << "]: "
          << str;
       return ostr.str();
    }

    template <typename PrintPolicy>
    class Logger
    {
       public:
          void addEntry(const string& str)
        {
           store_.push_back(PrintPolicy::print(str));
        }
   
        void dumpLog(ostream& os)
        {
           copy(store_.begin(), store_.end(), ostream_iterator<string>(os, "\n"));
        }

       private:
          vector<string> store_;
    };

    int main()
    {
       Logger<PlainPrintPolicy> logger;
       logger.addEntry("System booted");
       logger.addEntry("FaultFaultFault");
       logger.addEntry("Shutdown");
       logger.dumpLog(cout);

       Logger<TimestampPrintPolicy> logger2;
       logger2.addEntry("System booted");
       logger2.addEntry("FaultFaultFault");
       logger2.addEntry("Shutdown");
       logger2.dumpLog(cout);

       return 0;
}

Of course, I'm open to more viable solution with source example.   Thanks in advance
Avatar of bcladd
bcladd

First thought: You may want to log directly to disk (or have an option to do so). Crashing with a million lines of log in the logger will not help view what is going on.

I am not sure what you mean in 2.

As to 4, lacking a generic mechanism for iterating over an object's properties, I am thinking that you will need to build the "log" code into your objects. You could do this with some code generator/preprocessor since it will be fairly rote.

These are initial thoughts. I will take a look in Game Programming Gems #n and post again; there was an article on this in #1 or #2 (I think...).

-bcl
SOLUTION
Avatar of grg99
grg99

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of forums_mp

ASKER


bcladd.

Yes, I want the option to 'log directly to a disk'.   With regards to your argument about crashing with a million lines of log.    I agree.   I have the ability to monitor the disk usage (VxWorks 'environment') and as a result I suspect I could abort file logging if need be.

For item 2.  An initial approach/thought was to  create an abstract base class which overloads ostream::operator<<.   This way, I can log to a file stream, cout, or a stringstream as I wish (or all three).

grgg.  Excellent points
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>> to point 4 object registration

object registration/deregistration is a task that should performed by constructors and destructors, thus preventing to have invalid objects in the registry. A log utility hardly could handle this.

I'd recommend to hold a static array of class pointer as a member in any private class, where the constructor registers any new object and the destructor makes the deregistration. Then, a monitor utility may have access to these arrays and could give information on request.

Regards, Alex
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

I'm wading through all the suggestions received thus far and I have a few questions:

guntherothk
  - Could you elaborate on the synchronization issues you allude to.   If I'm following you theres a potential problem when 'sharing' objects?  That's the only time I could envision synchronization issues.

Alex,
 - File log capabilities is the primary 'strength' per the source provided?
 - I think I'm following you here but with regard to the static array of class pointers, could you provide an example (source code) to further illustrate the point.

bcl
 - I too could had no success finding that that article
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

guntherothk.  Will do though I'm already opting to surrender on this one.
Here is a sample using macro BAS_ERROR from the post above. You see, by using streams it's quite comfortable.

// @mfunc read whole file
Bool Inifile::readFile()
{
   // check if file exists and set number of filled bytes
   struct stat status;
   if (stat(m_fileName, &status) != 0)
      return False;

   // the size to read should be less because instead of CRLF-pairs we get only CR-Characters
   size_t    nSize = status.st_size;

   // we have to take try catch blocks to handle memory or file exceptions
   try
   {
      FILE*  pFile;
      // open the file in read mode
      if ((pFile = fopen( m_fileName, "r" )) != NULL)
      {
         if ((nSize = fread(m_contents.getBuffer(nSize), 1, nSize, pFile)) <= 0)
         {
            // something goes wrong
            fclose(pFile);
            BAS_ERROR("Error [" << errno << "] reading inifile " << m_fileName);
            m_contents.releaseBuffer(0);
            return False;
         }
      }      
      else
      {
         // something goes wrong
         BAS_ERROR("Error [" << errno << "] opening inifile " << m_fileName);
         return False;
      }

      // ok, clode the file and set buffer size to th eno of bytes actually read
      fclose(pFile);
      m_contents.releaseBuffer(nSize);
      // convert Carriage Return to Line feeds
      m_contents.replace("\r", "\n", True);
      // add one Line feed for easier searching
      m_contents += '\n';
   }  
   catch(...)
   {
      BAS_ERROR("Exception: Cannot read inifile " << m_fileName << " or get enough memory [" << nSize << "]" );
      return False;
   }

   return True;
}

Regards, Alex