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::prin t(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(PrintPoli cy::print( str));
}
void dumpLog(ostream& os)
{
copy(store_.begin(), store_.end(), ostream_iterator<string>(o s, "\n"));
}
private:
vector<string> store_;
};
int main()
{
Logger<PlainPrintPolicy> logger;
logger.addEntry("System booted");
logger.addEntry("FaultFaul tFault");
logger.addEntry("Shutdown" );
logger.dumpLog(cout);
Logger<TimestampPrintPolic y> logger2;
logger2.addEntry("System booted");
logger2.addEntry("FaultFau ltFault");
logger2.addEntry("Shutdown ");
logger2.dumpLog(cout);
return 0;
}
Of course, I'm open to more viable solution with source example. Thanks in advance
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::prin
{
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(PrintPoli
}
void dumpLog(ostream& os)
{
copy(store_.begin(), store_.end(), ostream_iterator<string>(o
}
private:
vector<string> store_;
};
int main()
{
Logger<PlainPrintPolicy> logger;
logger.addEntry("System booted");
logger.addEntry("FaultFaul
logger.addEntry("Shutdown"
logger.dumpLog(cout);
Logger<TimestampPrintPolic
logger2.addEntry("System booted");
logger2.addEntry("FaultFau
logger2.addEntry("Shutdown
logger2.dumpLog(cout);
return 0;
}
Of course, I'm open to more viable solution with source example. Thanks in advance
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
>>>> to point 4 object registration
object registration/deregistratio n 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
object registration/deregistratio
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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(n Size);
// 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
// @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
{
// 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(n
// 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
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