fix8  version 1.4.0
Open Source C++ FIX Framework
logger.cpp
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------------------
2 /*
3 
4 Fix8 is released under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
5 
6 Fix8 Open Source FIX Engine.
7 Copyright (C) 2010-16 David L. Dight <fix@fix8.org>
8 
9 Fix8 is free software: you can redistribute it and / or modify it under the terms of the
10 GNU Lesser General Public License as published by the Free Software Foundation, either
11 version 3 of the License, or (at your option) any later version.
12 
13 Fix8 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
14 even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 You should have received a copy of the GNU Lesser General Public License along with Fix8.
16 If not, see <http://www.gnu.org/licenses/>.
17 
18 BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO
19 THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
20 COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
21 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
23 THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
24 YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
25 
26 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT
27 HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
28 ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
29 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
30 NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
31 THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
32 HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
33 
34 */
35 //-----------------------------------------------------------------------------------------
36 #include "precomp.hpp"
37 #include <fix8/f8includes.hpp>
38 
39 //-------------------------------------------------------------------------------------------------
40 using namespace FIX8;
41 using namespace std;
42 
43 //-------------------------------------------------------------------------------------------------
44 namespace FIX8
45 {
46  char glob_log0[max_global_filename_length] { "global_filename_not_set.log" };
47 
48  const vector<string> Logger::_bit_names
49  {
50  "mstart", "sstart", "sequence", "thread", "timestamp", "minitimestamp", "direction", "level", "location",
51  "append", "buffer", "compress", "pipe", "broadcast", "nolf", "inbound", "outbound", "xml"
52  };
53 
54  const vector<string> Logger::_level_names { "Debug", "Info ", "Warn ", "Error", "Fatal" };
55 
56  const Tickval Logger::_started { true };
57 }
58 
59 //-------------------------------------------------------------------------------------------------
61 {
62  unsigned received(0);
63 
64  while (!_stopping)
65  {
66  LogElement *msg_ptr(0);
67 
68 #if (FIX8_MPMC_SYSTEM == FIX8_MPMC_FF)
69  if (!_msg_queue.try_pop(msg_ptr))
70  {
72  continue;
73  }
74 #else
75  LogElement msg;
76  if (_stopping) // make sure we dequeue any pending msgs before exiting
77  {
78  if (!_msg_queue.try_pop(msg))
79  break;
80  }
81  else
82  _msg_queue.pop (msg); // will block
83  msg_ptr = &msg;
84 #endif
85 
86  ++received;
87 
88  if (msg_ptr)
89  {
90  if (msg_ptr->_str.empty()) // means exit
91  {
92 #if (FIX8_MPMC_SYSTEM == FIX8_MPMC_FF)
93  break;
94 #else
95  continue;
96 #endif
97  }
98 
99  process_logline(msg_ptr);
100  }
101 #if (FIX8_MPMC_SYSTEM == FIX8_MPMC_FF)
102  _msg_queue.release(msg_ptr);
103 #endif
104  }
105 
106  return 0;
107 }
108 
109 //-------------------------------------------------------------------------------------------------
111 {
112  ostringstream ostr;
113  f8_scoped_spin_lock posguard(_log_spl); // protect positions, allow app to change
114  for (auto pp : _positions)
115  {
116  ostringstream fostr;
117  if (pp < start_controls) switch (pp)
118  {
119  case mstart:
120  {
121  const Tickval tvs(msg_ptr->_when - _started);
122  fostr << setw(11) << right << setfill('0') << (tvs.secs() * 1000 + tvs.msecs());
123  }
124  break;
125  case sstart:
126  fostr << setw(8) << right << setfill('0') << (msg_ptr->_when - _started).secs();
127  break;
128  case sequence:
129  fostr << setw(7) << right << setfill('0');
130  if (_flags & direction)
131  fostr << (msg_ptr->_val ? ++_sequence : ++_osequence);
132  else
133  fostr << ++_sequence;
134  break;
135  case thread:
136  fostr << get_thread_code(msg_ptr->_tid);
137  break;
138  case location:
139  if (msg_ptr->_fileline)
140  fostr << msg_ptr->_fileline;
141  break;
142  case direction:
143  fostr << (msg_ptr->_val ? " in" : "out");
144  break;
145  case timestamp:
146  fostr << msg_ptr->_when;
147  break;
148  case minitimestamp:
149  fostr << GetTimeAsStringMini(&msg_ptr->_when);
150  break;
151  case level:
152  fostr << _level_names[msg_ptr->_level];
153  break;
154  default:
155  break;
156  }
157 
158  if (!fostr.str().empty())
159  {
160  if (_delim.size() > 1)
161  ostr << _delim[0] << fostr.str() << _delim[1];
162  else
163  ostr << fostr.str() << _delim;
164  }
165  }
166  posguard.release();
167 
168  if (_flags & buffer)
169  {
170  string result(ostr.str());
171  if (_delim.size() > 1)
172  {
173  result += _delim[0];
174  result += msg_ptr->_str;
175  result += _delim[1];
176  }
177  else
178  result += msg_ptr->_str;
179  _buffer.push_back(result);
180  }
181  else
182  {
183  f8_scoped_lock guard(_mutex);
184  if (_delim.size() > 1)
185  get_stream() << ostr.str() << _delim[0] << msg_ptr->_str << _delim[1];
186  else
187  get_stream() << ostr.str() << msg_ptr->_str;
188  if (_flags & nolf)
189  get_stream().flush();
190  else
191  get_stream() << endl;
192  }
193 }
194 
195 //-------------------------------------------------------------------------------------------------
197 {
198  f8_scoped_lock guard(_mutex);
199  for (const auto& pp : _buffer)
200  get_stream() << pp << endl;
201  _buffer.clear();
202  _lines = 0;
203 }
204 
205 //-------------------------------------------------------------------------------------------------
207 {
208 #if (FIX8_THREAD_SYSTEM == FIX8_THREAD_PTHREAD)
209  f8_scoped_lock guard(_mutex);
210 
211  for (ThreadCodes::iterator itr(_thread_codes.begin()); itr != _thread_codes.end();)
212  {
213 #if defined _MSC_VER || defined __APPLE__
214  // If ESRCH then thread is definitely dead. Otherwise, we're unsure.
215  if (pthread_kill(itr->first, 0) == ESRCH)
216 #else
217  // a little trick to see if a thread is still alive
218  clockid_t clock_id;
219  if (pthread_getcpuclockid(itr->first, &clock_id) == ESRCH)
220 #endif
221  {
222  _rev_thread_codes.erase(itr->second);
223  _thread_codes.erase(itr++); // post inc, takes copy before incr;
224  }
225  else
226  ++itr;
227  }
228 #endif
229 }
230 
231 //-------------------------------------------------------------------------------------------------
233 {
234  f8_scoped_lock guard(_mutex);
235 
236  ThreadCodes::const_iterator itr(_thread_codes.find(tid));
237  if (itr != _thread_codes.end())
238  return itr->second;
239 
240  for (char acode('A'); acode < 127; ++acode) // A-~ will allow for 86 threads
241  {
242  RevThreadCodes::const_iterator itr(_rev_thread_codes.find(acode));
243  if (itr == _rev_thread_codes.end())
244  {
245  _thread_codes.insert({tid, acode});
246  _rev_thread_codes.insert({acode, tid});
247  return acode;
248  }
249  }
250 
251  return '?';
252 }
253 
254 //-------------------------------------------------------------------------------------------------
255 FileLogger::FileLogger(const string& fname, const LogFlags flags, const Levels levels,
256  const std::string delim, const LogPositions positions, const unsigned rotnum)
257  : Logger(flags, levels, delim, positions), _rotnum(rotnum)
258 {
259  if (!fname.empty())
260  {
261  _pathname = fname;
262  rotate();
263  }
264 }
265 
266 //-------------------------------------------------------------------------------------------------
267 bool FileLogger::rotate(bool force)
268 {
269  f8_scoped_lock guard(_mutex);
270 
271  delete _ofs;
272 
273  string thislFile(_pathname), filepart, dirpart;
274  split_path(thislFile, filepart, dirpart);
275  if (!dirpart.empty() && !exist(dirpart))
276  create_path(dirpart);
277 
278 #ifdef HAVE_COMPRESSION
279  if (_flags & compress)
280  thislFile += ".gz";
281 #endif
282  if (_rotnum > 0 && (!_flags.has(append) || force))
283  {
284  vector<string> rlst;
285  rlst.push_back(thislFile);
286 
287  for (unsigned ii(0); ii < _rotnum && ii < max_rotation; ++ii)
288  {
289  ostringstream ostr;
290  ostr << _pathname << '.' << (ii + 1);
291  if (_flags & compress)
292  ostr << ".gz";
293  rlst.push_back(ostr.str());
294  }
295 
296  for (unsigned ii(_rotnum); ii; --ii)
297  rename (rlst[ii - 1].c_str(), rlst[ii].c_str());
298  }
299 
300  const ios_base::openmode mode (_flags & append ? ios_base::out | ios_base::app : ios_base::out);
301 #ifdef HAVE_COMPRESSION
302  if (_flags & compress)
303  _ofs = new ogzstream(thislFile.c_str(), mode);
304  else
305 #endif
306  _ofs = new ofstream(thislFile.c_str(), mode);
307 
308  if (!_ofs || !*_ofs)
309  throw LogfileException(thislFile);
310 
311  return true;
312 }
313 
314 //-------------------------------------------------------------------------------------------------
315 PipeLogger::PipeLogger(const string& fname, const LogFlags flags, const Levels levels,
316  const std::string delim, const LogPositions positions)
317  : Logger(flags, levels, delim, positions)
318 {
319  const string pathname(fname.substr(1));
320 
321  if (fname[0] != '|')
322  throw f8Exception("pipe command must be prefixed with '|'");
323 
324 #ifdef _MSC_VER
325  FILE *pcmd(_popen(pathname.c_str(), "w"));
326 #else
327  FILE *pcmd(popen(pathname.c_str(), "w"));
328 #endif
329 
330  if (pcmd == 0)
331  glout_info << "PipeLogger: " << pathname << ": failed to execute";
332  else
333  {
334  _ofs = new fptrostream(pcmd);
335  _flags |= pipe;
336  }
337 }
338 
339 //-------------------------------------------------------------------------------------------------
340 // nc -lu 127.0.0.1 -p 51000
341 BCLogger::BCLogger(Poco::Net::DatagramSocket *sock, const LogFlags flags, const Levels levels,
342  const std::string delim, const LogPositions positions)
343  : Logger(flags, levels, delim, positions), _init_ok(true)
344 {
345  _ofs = new bcostream(sock);
346  _flags |= broadcast;
347 }
348 
349 BCLogger::BCLogger(const string& ip, const unsigned port, const LogFlags flags, const Levels levels,
350  const std::string delim, const LogPositions positions)
351  : Logger(flags, levels, delim, positions), _init_ok()
352 {
353  Poco::Net::IPAddress ipaddr;
354  if (Poco::Net::IPAddress::tryParse(ip, ipaddr)
355  && (ipaddr.isGlobalMC() || ipaddr.isMulticast() || ipaddr.isBroadcast()
356  || ipaddr.isLinkLocalMC() || ipaddr.isUnicast() || ipaddr.isWellKnownMC()
357  || ipaddr.isSiteLocalMC() || ipaddr.isOrgLocalMC()))
358  {
359  Poco::Net::SocketAddress saddr(ipaddr, port);
360  Poco::Net::DatagramSocket *dgs(new Poco::Net::DatagramSocket);
361  if (ipaddr.isBroadcast())
362  dgs->setBroadcast(true);
363  dgs->connect(saddr);
364  _ofs = new bcostream(dgs);
365  _flags |= broadcast;
366  _init_ok = true;
367  }
368 }
369 
370 //-------------------------------------------------------------------------------------------------
372 {
373  f8String spacer(3, ' ');
374  ostringstream ostr;
375 
376  ostr << spacer << "<logline ";
377  if (_flags & mstart)
378  {
379  const Tickval tvs(msg_ptr->_when - _started);
380  ostr << "mstart=\'" << (tvs.secs() * 1000 + tvs.msecs()) << "\' ";
381  }
382  if (_flags & sstart)
383  ostr << "sstart=\'" << (msg_ptr->_when - _started).secs() << "\' ";
384  if (_flags & sequence)
385  ostr << "sequence=\'" << (_flags & direction ? (msg_ptr->_val ? ++_sequence : ++_osequence) : ++_sequence) << "\' ";
386  if (_flags & thread)
387  ostr << "thread=\'" << get_thread_code(msg_ptr->_tid) << "\' ";
388  if (_flags & location && msg_ptr->_fileline)
389  ostr << "location=\'" << msg_ptr->_fileline << "\' ";
390  if (_flags & direction)
391  ostr << "direction=\'" << (msg_ptr->_val ? " in" : "out") << "\' ";
392  if (_flags & timestamp)
393  ostr << "timestamp=\'" << msg_ptr->_when << "\' ";
394  if (_flags & minitimestamp)
395  ostr << "minitimestamp=\'" << GetTimeAsStringMini(&msg_ptr->_when) << "\' ";
396  if (_flags & level)
397  ostr << "level=\'" << _level_names[msg_ptr->_level] << "\' ";
398 
399  ostr << "text=\'" << msg_ptr->_str << "\'/>";
400  f8_scoped_lock guard(_mutex);
401  get_stream() << ostr.str() << endl;
402 }
403 
f8_mutex _mutex
Definition: logger.hpp:179
unsigned _osequence
Definition: logger.hpp:220
F8API const std::string & GetTimeAsStringMini(std::string &result, const Tickval *tv)
f8_thread delegated async logging class
Definition: logger.hpp:153
static const Tickval _started
The time the entire logging system was start.
Definition: logger.hpp:229
F8API char get_thread_code(thread_id_t tid)
Definition: logger.cpp:232
const string & filepart(const string &source, string &where)
Definition: f8cutils.cpp:108
const char * _fileline
Definition: logger.hpp:193
std::ostream * _ofs
Definition: logger.hpp:184
F8API void create_path(const std::string &path)
const size_t max_global_filename_length(1024)
static const std::vector< std::string > _bit_names
string representation of logflags
Definition: logger.hpp:326
string spacer
Definition: f8c.cpp:96
std::vector< int > LogPositions
Definition: logger.hpp:176
virtual F8API void process_logline(LogElement *le)
Definition: logger.cpp:371
File pointer stream.
Definition: logger.hpp:77
Base exception class.
Definition: f8exception.hpp:49
static const int max_rotation
Definition: logger.hpp:173
static const std::vector< std::string > _level_names
string representation of levels
Definition: logger.hpp:329
virtual F8API bool rotate(bool force=false)
Definition: logger.cpp:267
F8API void purge_thread_codes()
Remove dead threads from the thread code cache.
Definition: logger.cpp:206
unsigned msecs() const
Definition: tickval.hpp:145
unsigned _sequence
Definition: logger.hpp:220
#define glout_info
Definition: logger.hpp:601
time_t secs() const
Definition: tickval.hpp:141
PipeLogger(const std::string &command, const LogFlags flags, const Levels levels, const std::string delim=" ", LogPositions positions=LogPositions())
Definition: logger.cpp:315
virtual std::ostream & get_stream() const
Definition: logger.hpp:286
int hypersleep< h_microseconds >(unsigned amt)
Definition: hypersleep.hpp:127
virtual F8API void process_logline(LogElement *le)
Definition: logger.cpp:110
void split_path(const std::string &source, std::string &filepart, std::string &dirpart)
Definition: f8utils.hpp:1082
integral_type has(const T sbit) const
Definition: f8utils.hpp:876
F8API char glob_log0[max_global_filename_length]
Definition: logger.cpp:46
std::thread::id thread_id_t
Definition: thread.hpp:58
unsigned _rotnum
Definition: logger.hpp:361
BCLogger(Poco::Net::DatagramSocket *sock, const LogFlags flags, const Levels levels, const std::string delim=" ", LogPositions positions=LogPositions())
Definition: logger.cpp:341
virtual F8API void flush()
Flush the buffer.
Definition: logger.cpp:196
udp stream
Definition: logger.hpp:134
LogFlags _flags
Definition: logger.hpp:181
bool exist(const std::string &fname)
Definition: f8utils.hpp:1068
F8API int operator()()
Definition: logger.cpp:60
std::string _pathname
Definition: logger.hpp:360
std::string f8String
Definition: f8types.hpp:47
F8API FileLogger(const std::string &pathname, const LogFlags flags, const Levels levels, const std::string delim=" ", const LogPositions positions=LogPositions(), const unsigned rotnum=rotation_default)
Definition: logger.cpp:255
Could not open a logfile.