libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
logging.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2002 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "file.hpp"
20 
21 #include <locale>
22 #include <cstdlib>
23 #include <ctime>
24 
25 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE {
26 
31  log_info
32  };
33  // Note the order - these must match the "log_level_type" enum.
34  const tchar * const log_level_strs[]={
35  _T("none"),
36  _T("errors"),
37  _T("warnings"),
38  _T("info")
39  };
40 
41  inline bool OutputDateTime(tostream &o,const time_t base_time=time(0),const tstring &fmt=_T("[%b %d %X %Z] ")) {
42  // I don't guard this with a "std::ostream::sentry" because I WANT TO ALWAYS LOG, even if something else is logging. So you'll get screwed output. Boo hoo. Better than lost output. Indeed the stupid "ostream" implementation would have better hidden its critical sections from me rather than making me deal with them... Isn't that what object orientation is about? Hmmm?
43  const tchar * const fmt_t=fmt.c_str(); // Hacking due to crap declaration of std::time_put::put(...) that takes a wank type of format string.
44  const tm * const time=localtime(&base_time);
45 // TODO JMG: better sort this out: if (std::use_facet< std::time_put<tchar> >(o.getloc(),0,true).put(o,o,o.fill(),time,fmt_t,fmt_t+fmt.size()).failed()) {
46 // o.setstate(std::ios_base::failbit);
47 // return false;
48 // } else {
49  return true;
50 // }
51  }
52 
53  template<class Except_>
54  class Logger {
55  public:
56  typedef Except_ exception_type;
57 
58  __stdcall Logger(const tstring &,const tchar *);
59  Logger(Logger const &)=delete;
60  virtual inline __stdcall ~Logger(void) {
61  }
62 
63  const log_level_type __fastcall Level(void) const noexcept(true) {
64  return log_level;
65  }
66  void __fastcall Level(const log_level_type l) noexcept(true) {
67  log_level=l;
68  }
69 
70  const tstring & __fastcall Path(void) const noexcept(true) {
71  return path_to_log_file;
72  }
73 
74  void __fastcall Log(const log_level_type at_level,const tostringstream &ss) const {
75  Log(at_level,ss.str());
76  }
77  virtual void __fastcall Log(const log_level_type,const tstring &) const;
78 
79  private:
80  const tstring path_to_log_file;
81  const tchar *app_name;
82  log_level_type log_level;
83  };
84 
85  template<class Except_> inline
86  Logger<Except_>::Logger(const tstring &path,const tchar *an)
87  : path_to_log_file(path),app_name(an),
88  log_level(log_none) {
89  // If this assertion fails the number of strings in the "log_level_strs" array does not match the number of enumeration values in the "log_level_type" enum.
90  // The "+1" is because of the zero-based indexing of arrays. There's n of them, but the last one is at [n-1].
91  assert(sizeof(log_level_strs)/sizeof(tchar *)==static_cast<size_t>(log_info+1));
92  }
93 
94  template<class Except_> inline void
95  Logger<Except_>::Log(const log_level_type at_level,const tstring &msg) const noexcept(false) {
96  if (at_level<=log_level) {
97  try {
98  tofstream log_file(path_to_log_file.c_str(),std::ios_base::out|std::ios_base::app);
99  if (log_file.is_open()) {
100  OutputDateTime(log_file); // Don't care if I fail to log the time. It's more important to get *anything* at all out, even if incomplete.
101  log_file
102  <<(getenv(_T("LOGNAME")) ? getenv(_T("LOGNAME")) : _T("LOGNAME"))
103  <<_T("@")
104  <<(getenv(_T("HOSTNAME")) ? getenv(_T("HOSTNAME")) : _T("HOSTNAME"))
105  <<_T("(")
106  <<(getenv(_T("HOSTTYPE")) ? getenv(_T("HOSTTYPE")) : _T("HOSTTYPE"))
107  <<_T(",")
108  <<(getenv(_T("OSTYPE")) ? getenv(_T("OSTYPE")) : _T("OSTYPE"))
109  <<_T(") ")
110  <<app_name
111  <<_T("[")<<getpid()<<_T("] ")
112  <<log_level_strs[at_level]<<_T(": ")
113  <<msg<<std::endl;
114  } else {
115  tostringstream ss;
116  ss<<_T("Failed to open '")<<path_to_log_file<<_T("' for writing log information.");
117  info::function fun(__LINE__,__PRETTY_FUNCTION__,typeid(*this),info::function::argument(_T("Log level."),log_level_strs[at_level]));
118  fun.add_args(
119  info::function::argument(_T("Log message."),msg),
120  info::function::argument(_T("Logging at level."),log_level_strs[log_level])
121  );
123  }
124  } catch (const exception &) {
125  throw;
126  } catch (const std::exception &e) {
127  tostringstream ss;
128  ss<<_T("STL exception caught whilst attempting to log. Details: ")<<e.what();
129  info::function fun(__LINE__,__PRETTY_FUNCTION__,typeid(*this),info::function::argument(_T("Log level."),log_level_strs[at_level]));
130  fun.add_args(
131  info::function::argument(_T("Log message."),msg),
132  info::function::argument(_T("Logging at level."), log_level_strs[log_level]),
133  info::function::argument(_T("Logging to file."), path_to_log_file)
134  );
136  }
137  }
138  }
139 
140 #define JMMCG_LOG(logger_,at_level_,msg_)
141  if ((at_level_)>(logger_)->Level()) {
142  ;
143  } else {
144  (logger_)->Log((at_level_),(msg_));
145  }
146 
147 } }