SPIN Framework

include/spinLog.h

00001 // -----------------------------------------------------------------------------
00002 // |    ___  ___  _  _ _     ___                                        _      |
00003 // |   / __>| . \| || \ |   | __>_ _  ___ ._ _ _  ___  _ _ _  ___  _ _ | |__   |
00004 // |   \__ \|  _/| ||   |   | _>| '_><_> || ' ' |/ ._>| | | |/ . \| '_>| / /   |
00005 // |   <___/|_|  |_||_\_|   |_| |_|  <___||_|_|_|\___.|__/_/ \___/|_|  |_\_\   |
00006 // |                                                                           |
00007 // |---------------------------------------------------------------------------|
00008 //
00009 // http://spinframework.sourceforge.net
00010 // Copyright (C) 2009 Mike Wozniewski, Zack Settel
00011 //
00012 // Developed/Maintained by:
00013 //    Mike Wozniewski (http://www.mikewoz.com)
00014 //    Zack Settel (http://www.sheefa.net/zack)
00015 // 
00016 // Principle Partners:
00017 //    Shared Reality Lab, McGill University (http://www.cim.mcgill.ca/sre)
00018 //    La Societe des Arts Technologiques (http://www.sat.qc.ca)
00019 //
00020 // Funding by:
00021 //    NSERC/Canada Council for the Arts - New Media Initiative
00022 //    Heritage Canada
00023 //    Ministere du Developpement economique, de l'Innovation et de l'Exportation
00024 //
00025 // -----------------------------------------------------------------------------
00026 //  This file is part of the SPIN Framework.
00027 //
00028 //  SPIN Framework is free software: you can redistribute it and/or modify
00029 //  it under the terms of the GNU Lesser General Public License as published by
00030 //  the Free Software Foundation, either version 3 of the License, or
00031 //  (at your option) any later version.
00032 //
00033 //  SPIN Framework is distributed in the hope that it will be useful,
00034 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00035 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00036 //  GNU Lesser General Public License for more details.
00037 //
00038 //  You should have received a copy of the GNU Lesser General Public License
00039 //  along with SPIN Framework. If not, see <http://www.gnu.org/licenses/>.
00040 // -----------------------------------------------------------------------------
00041 
00042 #ifndef spinLog_H_
00043 #define spinLog_H_
00044 
00045 #include <iostream>
00046 #include <fstream>
00047 //#include <time.h>
00048 #include <sys/time.h>
00049 #include <cstdio>
00050 #include <cstdlib>
00051 #include <cstring>
00052 
00053 namespace spin
00054 {
00055 
00060 static int timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
00061 {
00062     // Perform the carry for the later subtraction by updating y:
00063     if (x->tv_usec < y->tv_usec) {
00064         int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
00065         y->tv_usec -= 1000000 * nsec;
00066         y->tv_sec += nsec;
00067     }
00068     if (x->tv_usec - y->tv_usec > 1000000) {
00069         int nsec = (y->tv_usec - x->tv_usec) / 1000000;
00070         y->tv_usec += 1000000 * nsec;
00071         y->tv_sec -= nsec;
00072     }
00073 
00074     // Compute the time remaining to wait. tv_usec is certainly positive:
00075     result->tv_sec = x->tv_sec - y->tv_sec;
00076     result->tv_usec = x->tv_usec - y->tv_usec;
00077 
00078     // Return 1 if result is negative:
00079     return x->tv_sec < y->tv_sec;
00080 }
00081 
00082 // code from http://www.advogato.org/person/elanthis/diary/363.html
00083 
00084 enum LogPriority
00085 {
00086     INFO, // regular unimportant log messages
00087     DEV, // debugging fluff
00088     ERROR, // it's dead, jim
00089 };
00090 
00094 class logbuf : public std::streambuf
00095 {
00096 public:
00097     
00098     // create a buffer and initialize our logfile
00099     logbuf(const char* logpath) :
00100         bCOUT(false), bFILE(true), 
00101         logfile(),
00102         priority(INFO), buf(0), buflen(1024)
00103     {
00104         // create our buffer
00105         buf = new char_type[buflen];
00106         setp(buf, buf + buflen);
00107 
00108         // open the log file
00109         logfile.open(logpath, std::ios::app);
00110 
00111         if (! logfile.is_open())
00112         {
00113             std::cout << "Error: Could not open log file: " << logpath << std::endl;
00114             exit(1);
00115         }
00116         gettimeofday(&startTime, NULL);
00117     }
00118 
00119     // free our buffer
00120     ~logbuf()
00121     {
00122         sync();
00123         delete[] buf;
00124     }
00125 
00126     // set the priority to be used on the next call to sync()
00127     void set_priority(LogPriority p)
00128     {
00129         priority = p;
00130     }
00131 
00132     // logging modes:
00133     bool bCOUT;
00134     bool bFILE;
00135     
00136 private:
00137     struct timeval startTime;
00138     
00139     // spit out the time, priority, and the log buffer to cerr and logfile
00140     int sync()
00141     {
00142         // nifty time formatting functions from the C standard library
00143         time_t t = time(NULL);
00144         tm* tmp = localtime(&t);
00145         char shortTime[128];
00146         char longTime[128];
00147         strftime(shortTime, sizeof(shortTime), "%H:%M:%S", tmp);
00148         strftime(longTime, sizeof(longTime), "%Y-%m-%d %H:%M:%S", tmp);
00149         
00150         // for more precise time:
00151         struct timeval elapsedTime;
00152         gettimeofday(&elapsedTime, NULL);
00153         struct timeval dt;
00154         timeval_subtract(&dt, &startTime, &elapsedTime);
00155         
00156 
00157         // now we stream the time, then the priority, then the message
00158         if (bCOUT)
00159             std::cout << shortTime << ' ';
00160         if (bFILE)
00161             logfile << longTime << ' ';
00162         
00163         logfile << (int)(-dt.tv_sec) << "." << (int)(dt.tv_usec) << ' ';
00164 
00165         /*
00166         switch (priority)
00167         {
00168         case INFO:
00169             if (bCOUT) cout << "[INFO:] ";
00170             if (bFILE) logfile << "[INFO:] ";
00171             break;
00172         case DEV:
00173             if (bCOUT) cout << "[DEBUG] ";
00174             if (bFILE) logfile << "[DEBUG] ";
00175             break;
00176         case ERROR:
00177             if (bCOUT) cout << "[ERROR] ";
00178             if (bFILE) logfile << "[ERROR] ";
00179             break;
00180         }
00181         */
00182 
00183         if (bCOUT)
00184             std::cout.write(pbase(), pptr() - pbase());
00185         if (bFILE)
00186             logfile.write(pbase(), pptr() - pbase());
00187         
00188         // flush output
00189         if (bCOUT)
00190             std::cout.flush();
00191         if (bFILE)
00192             logfile.flush();
00193 
00194         // reset our priority to INFO
00195         priority = INFO;
00196 
00197         // reset the buffer
00198         setp(pbase(), epptr());
00199         return 0;
00200     }
00201 
00202     // we ran out of space, so grow the buffer
00203     int overflow(int c)
00204     {
00205         // allocate a new buffer and copy our current data into it, then swap
00206         // it with the old buffer
00207         char_type newbuf[buflen + 1024];
00208         memcpy(newbuf, buf, buflen);
00209         delete[] buf;
00210         buf = newbuf;
00211 
00212         // now we need to stuff c into the buffer
00213         sputc(c);
00214         return 0;
00215     }
00216 
00217     // our log file
00218     std::ofstream logfile;
00219 
00220     // current priority
00221     LogPriority priority;
00222 
00223     // our buffer
00224     char_type* buf;
00225     unsigned long buflen;
00226 };
00227 
00231 class spinLog : public std::ostream
00232 {
00233 public:
00234     // we initialize the ostream to use our logbuf
00235     spinLog(const char* logpath) : std::ostream(new logbuf(logpath))
00236     {
00237         buf = (logbuf*) rdbuf();
00238     }
00239 
00243     void set_priority(LogPriority pr)
00244     {
00245         buf->set_priority(pr);
00246     }
00247     
00248     void enable_cout(bool b)
00249     {
00250         buf->bCOUT = b;
00251     }
00252     
00253     void enable_logfile(bool b)
00254     {
00255         buf->bFILE = b;
00256     }
00257 private:
00258     // our logbuf object
00259     logbuf *buf;
00260 };
00261 
00262 // set the priority for a spinLog/logbuf this must be a global function and not
00263 // a member to work around C++'s type resolution of overloaded functions
00264 static spinLog& operator<<(spinLog& vlog, LogPriority pr)
00265 {
00266     vlog.set_priority(pr);
00267     return vlog;
00268 }
00269 
00270 } // end of namespace spin
00271 
00272 #endif // not included
00273