Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages
calllog.h
Go to the documentation of this file.00001 00004 /* 00005 * Copyright © 2003 Michael Geddes 00006 * 00007 * This material is provided "as is", with absolutely no warranty 00008 * expressed or implied. Any use is at your own risk. Permission to 00009 * use or copy this software for any purpose is hereby granted without 00010 * fee, provided the above notices are retained on all copies. 00011 * Permission to modify the code and to distribute modified code is 00012 * granted, provided the above notices are retained, and a notice that 00013 * the code was modified is included with the above copyright notice. 00014 * 00015 * This header is part of comet. 00016 * http://www.lambdasoft.dk/comet 00017 */ 00018 00019 #ifndef COMET_CALLLOG_H 00020 #define COMET_CALLLOG_H 00021 00022 #include <comet/config.h> 00023 #include <comet/tstring.h> 00024 #include <comet/variant.h> 00025 #include <comet/currency.h> 00026 #include <comet/server.h> 00027 #include <iomanip> 00028 #include <fstream> 00029 #include <sstream> 00030 #include <comet/handle_except.h> 00031 00052 #ifdef COMET_DOXYGEN // Only for doxygen 00053 00059 #define COMET_LOGFILE 00060 00068 #define COMET_LOGFILE_DEFAULT "C:\\log\\comet.log" 00069 #endif // COMET_DOXYGEN 00070 00072 00073 namespace comet 00074 { 00089 template<bool OVERRIDE> 00090 struct call_logger_ 00091 { 00095 static inline bool can_log_call() { return false; } 00099 static inline bool can_log_return() { return false; } 00100 00103 static inline void log_call( const tstring &iface, const tstring &funcname, const tstring &log){} 00104 00106 static inline void log_return( const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval){} 00107 00108 00110 00111 00115 static inline bool can_log_exception() { return false; } 00116 00118 static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource ,const source_info_t &callSource ){} 00120 }; 00121 00133 template<typename CREATESTREAM> 00134 struct stream_call_logger_t 00135 #ifdef COMET_DOXYGEN // For documentation 00136 : call_logger_ 00137 #endif 00138 { 00139 static inline bool can_log_call() 00140 { 00141 tostream *ofs = logger(); 00142 return ofs != NULL && ofs->good(); 00143 } 00144 static inline bool can_log_return() { return can_log_call(); } 00145 static inline bool can_log_exception() { return can_log_call(); } 00146 00147 static inline void log_call( const tstring &iface, const tstring &funcname, const tstring &log) 00148 { 00149 log_( false, iface, funcname, log, tstring()); 00150 } 00151 static inline void log_return( const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval) 00152 { 00153 log_( true, iface, funcname, log, retval); 00154 } 00155 static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource ,const source_info_t &callSource ) 00156 { 00157 COMET_NOTUSED(errorSource); 00158 tostream *ofs= logger(); 00159 if (ofs==NULL) return; // Should never be NULL, as can_log_call() should have been obeyed before calling. 00160 *ofs << _T("Err ") << callSource.source().t_str(); 00161 if ( !desc.empty() ) 00162 { 00163 *ofs << type << _T(": ") << desc ; 00164 } 00165 *ofs << std::endl; 00166 } 00167 00168 protected: 00169 // Log a function call/return. 00170 static void log_( bool return_val, const tstring &iface, const tstring &funcname, const tstring &log, const tstring &retval) 00171 { 00172 tostream *ofs= logger(); 00173 if (ofs==NULL) return; // Should never be NULL, as can_log_call() should have been obeyed before calling. 00174 00175 *ofs << (return_val?_T("Out "):_T("In ")) << iface << _T("::") << funcname; 00176 if (!return_val || !log.empty()) 00177 *ofs << _T("(") << log << _T(")"); 00178 00179 if (return_val && !retval.empty()) 00180 *ofs << _T(" returned ") << retval; 00181 *ofs << std::endl; 00182 00183 } 00184 // Access to the static logger instance without needing to initialise a 00185 // member. 00186 static tostream *logger() 00187 { 00188 static tostream *ofs_= NULL; 00189 if (ofs_ ==NULL) 00190 { 00191 ofs_ = CREATESTREAM::create(); 00192 if (ofs_ != NULL) 00193 { 00194 // Safely clean up static pointer on module destruct 00195 module().add_object_to_dispose( create_pointer_deleter( ofs_ ) ); 00196 } 00197 } 00198 return ofs_; 00199 } 00200 }; 00201 00202 namespace impl 00203 { 00207 template<> 00208 struct call_logger_redirect_<true> 00209 { 00210 // This trick allows the user to be able to still override the call logging. 00211 // Without this, log_exception has to call 00212 // call_logger_<true>::log_exception which causes the template to be 00213 // instantiated, and the user is no longer able to specialise 00214 // call_logger_ 00215 template<bool OVERRIDE> 00216 struct exe 00217 { 00218 static inline void log_exception(const tstring &type, const tstring &desc, const source_info_t &errorSource, const source_info_t &callSource ) 00219 { 00220 call_logger_<OVERRIDE>::log_exception(type,desc,errorSource, callSource); 00221 } 00222 static inline bool can_log_exception() 00223 { 00224 return call_logger_<OVERRIDE>::can_log_exception(); 00225 } 00226 }; 00227 }; 00228 } 00229 00230 #ifdef COMET_LOGFILE 00231 #ifndef COMET_LOGFILE_DEFAULT 00232 #define COMET_LOGFILE_DEFAULT NULL 00233 #endif 00234 #include <fstream> 00239 class tofstream_comet : public tofstream 00240 { 00241 static const char *&filename_() 00242 { 00243 static const char *default_filename= COMET_LOGFILE_DEFAULT; 00244 return default_filename; 00245 } 00246 public: 00248 static const char *get_default_filename() { return filename_(); } 00250 void set_default_filename( const char *filename) { filename_() = filename; } 00251 00252 tofstream_comet( const char *fname) : tofstream(fname) {} 00253 00257 static tostream *create() 00258 { 00259 const char *fname = filename_(); 00260 if (fname == NULL) 00261 return NULL; 00262 return new tofstream_comet(fname); 00263 } 00264 private: 00265 tofstream_comet(const tofstream_comet &); 00266 tofstream_comet &operator=(const tofstream_comet &); 00267 00268 }; 00269 00273 template<> 00274 struct call_logger_<true> : public stream_call_logger_t<tofstream_comet> 00275 { 00276 }; 00277 00278 #endif // FILE_LOG 00279 00293 template< typename IFACE> 00294 void comet_log_interface(tostream &os, const com_ptr<IFACE> &iface) 00295 { 00296 os << _T("0x") << std::hex << reinterpret_cast<unsigned long>(iface.get()) << std::dec; 00297 } 00298 00299 // Forward declarations. 00300 template<typename T> void comet_log(tostream &os, const T &value); 00301 00302 namespace impl 00303 { 00304 // Forward declarations. 00305 template<typename T> inline void default_comet_log(tostream &os, const T &value); 00306 static void comet_log_array_raw(tostream &os, SAFEARRAY *value); 00307 00308 // The default variant handler. 00309 template< bool B> 00310 static inline void default_comet_log_variant(tostream &os, const variant_t &value, bool out_type) 00311 { 00312 VARTYPE vt = value.get_vt(); 00313 if ((vt & VT_ARRAY) != 0) 00314 { 00315 comet_log_array_raw(os, value.get().parray); 00316 } 00317 else 00318 { 00319 VARIANT varcopy=value.in(); 00320 if (vt == (VT_BYREF | VT_VARIANT)) // Dereference variant by reference 00321 varcopy = *V_VARIANTREF(&varcopy); 00322 const VARIANT *var=&varcopy; 00323 00324 #define __VARIANT_LOGPOINTER_TYPE_CAST(vartype,cast) \ 00325 case VT_##vartype:\ 00326 if(out_type) os << _T("VT_")_T(#vartype)_T(":");\ 00327 comet_log_interface(os, cast(V_##vartype(var))); \ 00328 break 00329 00330 #define __VARIANT_LOGPOINTER_REFTYPE_CAST(vartype,cast) \ 00331 case VT_BYREF|VT_##vartype:\ 00332 if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":");\ 00333 comet_log_interface(os, cast(*V_##vartype##REF(var)));\ 00334 break 00335 #define __VARIANT_LOG_TYPE_CAST(vartype,cast) \ 00336 case VT_##vartype: \ 00337 if(out_type) os << _T("VT_")_T(#vartype)_T(":"); \ 00338 comet_log(os, cast(V_##vartype(var))); \ 00339 break 00340 00341 #define __VARIANT_LOG_REFTYPE_CAST(vartype,cast)\ 00342 case VT_BYREF|VT_##vartype:\ 00343 if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":");\ 00344 comet_log(os, cast(*V_##vartype##REF(var)));\ 00345 break 00346 00347 #define __VARIANT_LOG_TYPE(vartype) \ 00348 case VT_##vartype: \ 00349 if(out_type) os << _T("VT_")_T(#vartype)_T(":");\ 00350 comet_log(os, V_##vartype(var));\ 00351 break 00352 00353 #define __VARIANT_LOG_REFTYPE(vartype)\ 00354 case VT_BYREF|VT_##vartype: \ 00355 if(out_type) os << _T("BYREF VT_")_T(#vartype)_T(":"); \ 00356 comet_log(os, *V_##vartype##REF(var)); \ 00357 break 00358 00359 switch (vt) 00360 { 00361 __VARIANT_LOG_TYPE(UI1); 00362 __VARIANT_LOG_TYPE(UI2); 00363 __VARIANT_LOG_TYPE(UINT); 00364 __VARIANT_LOG_TYPE(UI4); 00365 __VARIANT_LOG_TYPE(I1); 00366 __VARIANT_LOG_TYPE(I2); 00367 __VARIANT_LOG_TYPE(INT); 00368 __VARIANT_LOG_TYPE(I4); 00369 __VARIANT_LOG_TYPE(R4); 00370 __VARIANT_LOG_TYPE(R8); 00371 __VARIANT_LOG_REFTYPE(UI1); 00372 __VARIANT_LOG_REFTYPE(UI2); 00373 __VARIANT_LOG_REFTYPE(UINT); 00374 __VARIANT_LOG_REFTYPE(UI4); 00375 __VARIANT_LOG_REFTYPE(I1); 00376 __VARIANT_LOG_REFTYPE(I2); 00377 __VARIANT_LOG_REFTYPE(INT); 00378 __VARIANT_LOG_REFTYPE(I4); 00379 __VARIANT_LOG_REFTYPE(R4); 00380 __VARIANT_LOG_REFTYPE(R8); 00381 00382 case VT_BOOL: 00383 if(out_type) os << _T("VT_BOOL:"); 00384 os << (V_BOOL(var)==VARIANT_FALSE)?_T("true"):_T("false"); 00385 break; 00386 case VT_BYREF|VT_BOOL: 00387 if(out_type) os << _T("BYREF VT_BOOL:"); 00388 os << (V_BOOL(var)==VARIANT_FALSE)?_T("true"):_T("false"); 00389 break; 00390 __VARIANT_LOG_TYPE_CAST( CY, currency_t::create_const_reference); 00391 __VARIANT_LOG_REFTYPE_CAST( CY, currency_t::create_const_reference); 00392 __VARIANT_LOG_TYPE_CAST( DATE, datetime_t::create_const_reference); 00393 __VARIANT_LOG_REFTYPE_CAST( DATE, datetime_t::create_const_reference); 00394 __VARIANT_LOG_TYPE_CAST( BSTR, bstr_t::create_const_reference); 00395 __VARIANT_LOG_REFTYPE_CAST( BSTR, bstr_t::create_const_reference); 00396 00397 __VARIANT_LOGPOINTER_TYPE_CAST( UNKNOWN, com_ptr<IUnknown>::create_const_reference); 00398 __VARIANT_LOGPOINTER_TYPE_CAST( DISPATCH, com_ptr<IDispatch>::create_const_reference); 00399 __VARIANT_LOGPOINTER_REFTYPE_CAST( UNKNOWN, com_ptr<IUnknown>::create_const_reference); 00400 __VARIANT_LOGPOINTER_REFTYPE_CAST( DISPATCH, com_ptr<IDispatch>::create_const_reference); 00401 00402 case VT_DECIMAL: 00403 if(out_type) os << _T("BYREF VT_DECIMAL:"); 00404 os << _T("?"); 00405 break; 00406 case VT_ERROR: 00407 if(out_type) os << _T("VT_ERROR:"); 00408 os <<_T("0x") << std::hex << V_ERROR(var) << std::dec; 00409 break; 00410 case VT_BYREF|VT_ERROR: 00411 if(out_type) os << _T("BYREF VT_ERROR:"); 00412 os <<_T("0x") << std::hex << *V_ERRORREF(var) << std::dec; 00413 break; 00414 default: 00415 os << _T("???"); 00416 } 00417 #undef __VARIANT_LOG_TYPE_CAST 00418 #undef __VARIANT_LOG_REFTYPE_CAST 00419 #undef __VARIANT_LOG_TYPE 00420 #undef __VARIANT_LOG_REFTYPE 00421 #undef __VARIANT_LOGPOINTER_TYPE_CAST 00422 #undef __VARIANT_LOGPOINTER_REFTYPE_CAST 00423 } 00424 } 00425 00426 /* Logging for raw safearrays. 00427 * For vector arrays of size <= 16, this fakes a variant and then uses the default variant logging to log the elements 00428 */ 00429 static void comet_log_array_raw(tostream &os, SAFEARRAY *value) 00430 { 00431 UINT dims = SafeArrayGetDim(value); 00432 VARTYPE vt ; 00433 if( FAILED(SafeArrayGetVartype(value, &vt ) ) ) 00434 { 00435 os << "???"; 00436 return; 00437 } 00438 00439 long ubound=-1,lbound=0; // Outside so it can be used for the '1' case below. 00440 for (UINT d = 1; d<= dims ; ++d) 00441 { 00442 if( SUCCEEDED(SafeArrayGetUBound( value, d, &ubound) ) && SUCCEEDED(SafeArrayGetLBound(value, d, &lbound) )) 00443 { 00444 if( lbound == 0) 00445 os << _T("[") << ubound+1 << _T("]"); 00446 else 00447 os << _T("(") << lbound << _T("..") << ubound << _T(")"); 00448 } 00449 00450 } 00451 if (dims == 1 ) 00452 { 00453 long size = 1+ubound-lbound; 00454 if (size ==0) 00455 { 00456 os << _T("{}"); 00457 } 00458 else if( size > 0 && size <= 15) 00459 { 00460 // For small arrays, print out the elements. 00461 os << _T("{"); 00462 VARIANT v; 00463 void *var; 00464 bool first=true, is_variant= (vt==VT_VARIANT); 00465 if ( is_variant) 00466 var = (void *)&v; 00467 else 00468 { 00469 V_VT(&v) = vt; 00470 var = (void *)&V_UI1(&v); 00471 } 00472 00473 for (long i = lbound; i <= ubound; ++i ) 00474 { 00475 if(first) 00476 first = false; 00477 else 00478 os << _T(","); 00479 if( SUCCEEDED( SafeArrayGetElement( value, &i, var ) )) 00480 default_comet_log_variant<true>( os, variant_t(auto_attach(v)), first | is_variant); 00481 } 00482 os << _T("}"); 00483 } 00484 } 00485 } 00486 00487 // Default logging: stream the value. 00488 template<typename T> 00489 inline void default_comet_log(tostream &os, const T &value) 00490 { 00491 // ERRORS: If a compiler error occurs here, you may need to 00492 // specialise comet_log<> to the type T here. 00493 os << value; 00494 } 00495 // Default logging for bstr_t 00496 template<> 00497 inline void default_comet_log<bstr_t>(tostream &os, const bstr_t &value) 00498 { 00499 // Put quotes round the string - simplistic, don't worry about 00500 // non-printables or escaping for the moment. 00501 os << _T("'") << value << _T("'"); 00502 } 00503 00504 // Deafult logging for variants. 00505 template<> 00506 static inline void default_comet_log<variant_t>(tostream &os, const variant_t &value) 00507 { 00508 default_comet_log_variant<true>( os, value, true); 00509 } 00510 } 00511 00518 template<typename T> 00519 void comet_log(tostream &os, const T &value) 00520 { 00521 impl::default_comet_log(os,value); 00522 } 00523 00524 namespace impl 00525 { 00526 // trick to work out whether it is a safearray or com_ptr type (required because of no 00527 // partial specialisation). Needed to handle the special circumstances 00528 template<typename T> 00529 struct check_log_type_ 00530 { 00531 static long check(...); 00532 00533 template<typename S> 00534 static char check(const safearray_t<S> &); 00535 00536 template<typename S> 00537 static short check( const com_ptr<S> &); 00538 00539 static T createT(); 00540 enum { is = sizeof( check( createT() ) ) }; 00541 00542 }; 00543 template < int TYPE> struct choose_logger; 00544 00545 // Default safearray_t logger where we know the type T. 00546 // 00547 template<typename T> 00548 inline void default_comet_log_array(tostream &os, const safearray_t<T> &value) 00549 { 00550 safearray_t<T>::index_type lbound=value.lower_bound(), size = value.size(); 00551 if (lbound == 0) 00552 os << _T("[") << size << _T("]"); 00553 else 00554 os << _T("(") << lbound << _T("..") << (lbound + size -1) << _T(")"); 00555 00556 if( value.size() <= 15) 00557 { 00558 os << _T("{"); 00559 bool first = true; 00560 for (safearray_t<T>::const_iterator it = value.begin(); it != value.end(); ++it) 00561 { 00562 choose_logger<check_log_type_<T>::is >::do_comet_log(os, *it); 00563 if (first) 00564 first=false; 00565 else 00566 os << _T(","); 00567 } 00568 os << _T("}"); 00569 } 00570 } 00571 } 00578 template<typename T> 00579 void comet_log_array(tostream &os, const safearray_t<T> &value) 00580 { 00581 impl::default_comet_log_array(os,value); 00582 } 00583 00584 namespace impl 00585 { 00586 // choose the standard logger 00587 template < int TYPE> 00588 struct choose_logger 00589 { 00590 template<typename T> 00591 static inline void do_comet_log(tostream &os, const T &value) 00592 { 00593 comet_log(os,value); 00594 } 00595 }; 00596 // Choose the array logger 00597 template<> 00598 struct choose_logger<sizeof(char)> 00599 { 00600 template <typename T> 00601 static inline void do_comet_log(tostream &os, const safearray_t<T> &value) 00602 { 00603 comet_log_array(os, value); 00604 } 00605 }; 00606 // Choose the interface logger 00607 template<> 00608 struct choose_logger<sizeof(short)> 00609 { 00610 template <typename T> 00611 static inline void do_comet_log(tostream &os, const com_ptr<T> &value) 00612 { 00613 comet_log_interface(os, value); 00614 } 00615 }; 00616 00617 // Choose which of the comet_loggers to use. 00618 template<typename T> 00619 static inline tostream &do_comet_log(tostream &os, const T &value) 00620 { 00621 choose_logger<check_log_type_<T>::is >::do_comet_log(os, value); 00622 return os; 00623 } 00624 } 00626 } 00627 00628 #endif /* COMET_CALLLOG_H */