SPIN Framework
|
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