source: rrlib_logging/tLogDomain.h @ 49:075058b4f654

Last change on this file since 49:075058b4f654 was 49:075058b4f654, checked in by Tobias Föhst <foehst@…>, 10 years ago

Fixed bug that truncated RRLIB_LOG_MESSAGE output

File size: 14.9 KB
Line 
1//
2// You received this file as part of RRLib
3// Robotics Research Library
4//
5// Copyright (C) AG Robotersysteme TU Kaiserslautern
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License
9// as published by the Free Software Foundation; either version 2
10// of the License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20//
21//----------------------------------------------------------------------
22/*!\file    tLogDomain.h
23 *
24 * \author  Tobias Foehst
25 * \author  Max Reichardt
26 *
27 * \date    2010-06-16
28 *
29 * \brief Contains tLogDomain
30 *
31 * \b tLogDomain
32 *
33 * The RRLib logging system is structured into hierarchical domains that
34 * can be created and configured via tLogDomainRegistry. That given,
35 * in the program implementation instances of the class tLogDomain
36 * wrap the stream that can be access either in C++ iostream style via
37 * operator << or in good old-fashioned C style using printf formatting.
38 *
39 */
40//----------------------------------------------------------------------
41#ifndef _rrlib_logging_include_guard_
42#error Invalid include directive. Try #include "rrlib/logging/definitions.h" instead.
43#endif
44
45#ifndef _rrlib_logging_tLogDomain_h_
46#define _rrlib_logging_tLogDomain_h_
47
48//----------------------------------------------------------------------
49// External includes (system with <>, local with "")
50//----------------------------------------------------------------------
51#include <string>
52#include <vector>
53#include <fstream>
54#include <iomanip>
55#include <ctime>
56#include <cstdarg>
57#include <cassert>
58#include <tr1/memory>
59
60//----------------------------------------------------------------------
61// Internal includes with ""
62//----------------------------------------------------------------------
63#include "rrlib/logging/tLogDomainConfiguration.h"
64#include "rrlib/logging/tLogStreamBuffer.h"
65#include "rrlib/logging/tLogStream.h"
66#include "rrlib/logging/tLogStreamContext.h"
67
68//----------------------------------------------------------------------
69// Debugging
70//----------------------------------------------------------------------
71
72//----------------------------------------------------------------------
73// Namespace declaration
74//----------------------------------------------------------------------
75namespace rrlib
76{
77namespace logging
78{
79
80//----------------------------------------------------------------------
81// Forward declarations / typedefs / enums
82//----------------------------------------------------------------------
83//! Shared pointer to instances of tLogDomain for user space
84class tLogDomain;
85typedef std::tr1::shared_ptr<const tLogDomain> tLogDomainSharedPointer;
86
87//----------------------------------------------------------------------
88// Class declaration
89//----------------------------------------------------------------------
90//! This class implements messaging via a specific logging domain
91/*! The RRLib logging system is structured into hierarchical domains that
92 *  can be created and configured via tLogDomainRegistry. That given,
93 *  in the program implementation instances of this class wrap the stream
94 *  that can be access either in C++ iostream style via operator << or
95 *  in the good old-fashioned C style using printf formatting.
96 *
97 */
98class tLogDomain
99{
100  friend class tLogDomainRegistry;
101
102//----------------------------------------------------------------------
103// Public methods and typedefs
104//----------------------------------------------------------------------
105public:
106
107  /*! The dtor of tLogDomain
108   */
109  ~tLogDomain();
110
111  /*! Get the full qualified name of this domain
112   *
113   * Each domain has a full qualified name consisting of its parent's name
114   * and the local part that was given at creation time.
115   *
116   * \returns The full qualified domain name
117   */
118  inline const std::string GetName() const
119  {
120    return this->configuration->name;
121  }
122
123  /*! Get configuration status of this domain's print_time flag
124   *
125   * The current time is prepended to messages of this domain if the
126   * print_time flag is set.
127   *
128   * \returns Whether the print_time flag is set or not.
129   */
130  inline const bool GetPrintTime() const
131  {
132    return this->configuration->print_time;
133  }
134
135  /*! Get configuration status of this domain's print_name flag
136   *
137   * The name of this domain prepended to messages of this domain if its
138   * print_name flag is set.
139   *
140   * \returns Whether the print_name flag is set or not.
141   */
142  inline const bool GetPrintName() const
143  {
144    return this->configuration->print_name;
145  }
146
147  /*! Get configuration status of this domain's print_level flag
148   *
149   * The level of each message is contained in the output of this domain
150   * if the print_level flag is set.
151   *
152   * \returns Whether the print_level flag is set or not.
153   */
154  inline const bool GetPrintLevel() const
155  {
156    return this->configuration->print_level;
157  }
158
159  /*! Get configuration status of this domain's print_location flag
160   *
161   * The location given to each message is contained in the output of this
162   * domain if the print_location flag is set.
163   *
164   * \returns Whether the print_location flag is set or not.
165   */
166  inline const bool GetPrintLocation() const
167  {
168    return this->configuration->print_location;
169  }
170
171  /*! Get the maximal log level a message must have to be processed
172   *
173   * Each message has a log level that must not be above the configured limit to be processed.
174   *
175   * \returns The configured maximal log level
176   */
177  inline const tLogLevel GetMaxMessageLevel() const
178  {
179    return this->configuration->max_message_level;
180  }
181
182  /*! Get a message stream from this domain
183   *
184   * This method is the streaming interface to this logging domain.
185   * It must be used for every output using operator <<.
186   * The method then depending on the domain's configuration chooses
187   * a stream, prints the prefix that should be prepended to every
188   * message and returns the stream to process further input given as
189   * operator << cascade in the user's program.
190   * To properly specify the arguments of this method consider using
191   * the macros defined in rrlib/logging/definitions.h
192   *
193   * \param description   A string that describes the global context of the message
194   * \param function      The name of the function that contains the message (__FUNCTION__)
195   * \param file          The file that contains the message
196   * \param line          The line that contains the message
197   * \param level         The log level of the message
198   *
199   * \returns A reference to the stream that can be used for the remaining message parts
200   */
201  template <typename TDescription>
202  inline tLogStream GetMessageStream(const TDescription &description, const char *function, const char *file, unsigned int line, tLogLevel level) const
203  {
204    tLogStream stream_proxy(std::tr1::shared_ptr<tLogStreamContext>(new tLogStreamContext(this->stream_buffer, mutex.get())));
205    this->stream_buffer.Clear();
206    this->stream_buffer.InitializeMultiLinePadding();
207    if (level > this->GetMaxMessageLevel())
208    {
209      return stream_proxy;
210    }
211    this->SetupOutputStream(this->configuration->sink_mask);
212    if (level == eLL_USER)
213    {
214      return stream_proxy;
215    }
216    if (this->GetPrintTime())
217    {
218      stream_proxy << this->GetTimeString();
219    }
220    this->SetupOutputStreamColor(level);
221#ifndef _RRLIB_LOGGING_LESS_OUTPUT_
222    if (this->GetPrintName())
223    {
224      stream_proxy << this->GetNameString();
225    }
226    if (this->GetPrintLevel())
227    {
228      stream_proxy << this->GetLevelString(level);
229    }
230#endif
231    stream_proxy << description << "::" << function << " ";
232#ifndef _RRLIB_LOGGING_LESS_OUTPUT_
233    if (this->GetPrintLocation())
234    {
235      stream_proxy << this->GetLocationString(file, line);
236    }
237#endif
238    stream_proxy << ">> ";
239    this->stream_buffer.ResetColor();
240
241    switch (level)
242    {
243    case eLL_ERROR:
244      stream_proxy << "ERROR: ";
245      break;
246    case eLL_WARNING:
247    case eLL_DEBUG_WARNING:
248      stream_proxy << "WARNING: ";
249      break;
250    default:
251      ;
252    }
253
254    this->stream_buffer.MarkEndOfPrefixForMultiLinePadding();
255
256    return stream_proxy;
257  }
258
259  /*! A printf like variant of using logging domains for message output
260   *
261   * Instead of using operator << to output messages this method can be
262   * used. It then itself uses printf to format the given message and
263   * streams the result through the result obtained from GetMessageStream.
264   * That way the message prefix is only generated in one place and - more
265   * important - the underlying technique is the more sane one from
266   * iostreams instead of file descriptors.
267   * Apart from that: iostreams and file descriptors can not be mixed. So
268   * a decision had to be made.
269   *
270   * \param description   A string that describes the global context of the message
271   * \param function      The name of the function that contains the message (__FUNCTION__)
272   * \param file          The file that contains the message
273   * \param line          The line that contains the message
274   * \param level         The log level of the message
275   * \param fmt           The format string for printf
276   * \param ...           The remaining arguments for printf
277   */
278  template <typename TDescription>
279  inline void PrintMessage(const TDescription &description, const char *function, const char *file, int line, tLogLevel level, const char *fmt, ...) const
280  {
281    if (level > this->GetMaxMessageLevel())
282    {
283      return;
284    }
285
286    va_list printf_args;
287    va_start(printf_args, fmt);
288    this->VPrintMessage(description, function, file, line, level, fmt, printf_args);
289    va_end(printf_args);
290  }
291
292  template <typename TDescription>
293  inline void PrintMessage(const TDescription &description, const char *function, const char *file, int line, tLogLevel level, tLogDomainSharedPointer(&)(), const char *fmt, ...) const
294  {
295    if (level > this->GetMaxMessageLevel())
296    {
297      return;
298    }
299
300    va_list printf_args;
301    va_start(printf_args, fmt);
302    this->VPrintMessage(description, function, file, line, level, fmt, printf_args);
303    va_end(printf_args);
304  }
305
306//----------------------------------------------------------------------
307// Private fields and methods
308//----------------------------------------------------------------------
309private:
310
311  tLogDomain *parent;
312  std::vector<tLogDomain *> children;
313
314  tLogDomainConfigurationSharedPointer configuration;
315
316  mutable tLogStreamBuffer stream_buffer;
317  mutable std::ofstream file_stream;
318
319  std::tr1::shared_ptr<boost::recursive_mutex> mutex;
320
321  /*! The ctor of a top level domain
322   *
323   * This ctor is to be called by the registry that creates the top level
324   * domain.
325   *
326   * \param configuration   The configuration for the new domain
327   */
328  tLogDomain(tLogDomainConfigurationSharedPointer configuration);
329
330  /*! The ctor for a new sub domain
331   *
332   * This ctor is to be called by the registry to create a new subdomain
333   * with a given configuration
334   *
335   * \param configuration   The configuration for the new domain
336   * \param parent          The parent domain
337   */
338  tLogDomain(tLogDomainConfigurationSharedPointer configuration, tLogDomain &parent);
339
340  /*!
341   * \returns Shared Pointer to output mutex that is shared by all logging domains
342   */
343  static std::tr1::shared_ptr<boost::recursive_mutex> GetMutex()
344  {
345    static std::tr1::shared_ptr<boost::recursive_mutex> mutex(new boost::recursive_mutex());
346    return mutex;
347  }
348
349  /*! Recursively configure the subtree that begins in this domain
350   *
351   * If the domain is configured by its parent, the configuration is
352   * copied and propagated to this domain's children
353   */
354  void ConfigureSubTree();
355
356  /*! Open the file stream for file output
357   *
358   * This method creates a new file which name is build using a prefix
359   * and the full qualified domain name.
360   * If the file already exists, it will be truncated.
361   *
362   * \returns Whether the file stream could be opened or not
363   */
364  const bool OpenFileOutputStream() const;
365
366  /*! Setup the output stream to be used in this domain
367   *
368   * A domain can stream its input to stdout, stderr, an own file and/or its parent's file.
369   *
370   *\param mask   The bitmask that selects the output streams to use
371   */
372  void SetupOutputStream(int mask) const;
373
374  /*! Get the current time as string for internal use in messages
375   *
376   * This method formats the current time as string that can be used in
377   * messages.
378   *
379   * \returns The current time as string
380   */
381  const std::string GetTimeString() const;
382
383  /*! Get the domain's name as string for internal use in messages
384   *
385   * This method formats the name as string that can be used in
386   * messages. This string is padded with spaces to the length of the
387   * longest domain name
388   *
389   * \returns The padded name as string
390   */
391  const std::string GetNameString() const;
392
393  /*! Get the given message level as string for internal use in messages
394   *
395   * This method formats the given level as string that can be used in
396   * messages.
397   *
398   * \param level   The level that should be represented as string
399   *
400   * \returns The given level as padded string
401   */
402  const std::string GetLevelString(tLogLevel level) const;
403
404  /*! Get the given location as string for internal use in messages
405   *
406   * This method formats given location consisting of a file name and a
407   * line number as string that can be used in messages.
408   *
409   * \param file   The file name (e.g. from __FILE__)
410   * \param line   The line number (e.g. from __LINE__)
411   *
412   * \returns The given location as string
413   */
414  const std::string GetLocationString(const char *file, unsigned int line) const;
415
416  /*! Setup the underlying streambuffer to produce colored output
417   *
418   * This method sets up the underlying streambuffer for colored
419   * output according to the given level.
420   *
421   * \param level   The according log level
422   */
423  void SetupOutputStreamColor(tLogLevel level) const;
424
425  template <typename TDescription>
426  inline void VPrintMessage(const TDescription &description, const char *function, const char *file, int line, tLogLevel level, const char *fmt,  va_list printf_args) const
427  {
428    char temp;
429    int needed_buffer_size = vsnprintf(&temp, 1, fmt, printf_args);
430    if (needed_buffer_size > 0)
431    {
432      char formatted_string_buffer[needed_buffer_size + 1];
433      vsnprintf(formatted_string_buffer, sizeof(formatted_string_buffer), fmt, printf_args);
434      this->GetMessageStream(description, function, file, line, level) << formatted_string_buffer;
435    }
436  }
437
438};
439
440//----------------------------------------------------------------------
441// End of namespace declaration
442//----------------------------------------------------------------------
443}
444}
445
446#endif
Note: See TracBrowser for help on using the repository browser.