summaryrefslogtreecommitdiff
path: root/src/sysync/debuglogger.h
blob: e938c774a4372fa356b29645ee523725936e29ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
/*
 *  File:         debuglogger.h
 *
 *  Author:			  Lukas Zeller (luz@synthesis.ch)
 *
 *  Global debug mechanisms
 *
 *  Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
 *
 *  2005-08-04 : luz : created
 *
 */

#ifndef DEBUGLOGGER_H
#define DEBUGLOGGER_H

#ifdef SYDEBUG

#include "generic_types.h"
#include "sysync.h"

namespace sysync {


/// @brief Debug output formats
typedef enum {
  dbgfmt_text,        ///< plain text format (but can be indented)
  dbgfmt_xml,         ///< XML format
  dbgfmt_html,        ///< HTML format
  numDbgOutFormats
} TDbgOutFormats;

/// @brief HTML dynamic folding
typedef enum {
  dbgfold_none,       ///< do not include dynamic folding into HTML logs
  dbgfold_collapsed,  ///< include folding - all collapsed by default
  dbgfold_expanded,   ///< include folding - all expanded by default
  dbgfold_auto,       ///< include folding - collapse/expand state predefined on a block-by-block basis
  numDbgFoldingModes
} TDbgFoldingModes;

/// @brief Debug flush modes
typedef enum {
  dbgflush_none,      ///< no flush, keep open as long as possible
  dbgflush_flush,		  ///< flush every debug message
  dbgflush_openclose,	///< open and close debug channel separately for every message (as in 2.x engine)
  numDbgFlushModes
} TDbgFlushModes;

/// @brief Debug subthread logging modes
typedef enum {
  dbgsubthread_none,          ///< do not handle output from subthread specially
  dbgsubthread_suppress,			///< suppress output from subthreads
  dbgsubthread_separate,			///< create separate output stream (=file) for each subthread
  dbgsubthread_linemix,       ///< mix output on a line by line basis (forcing output to slow openclose mode)
  dbgsubthread_bufferandmix,  ///< buffer thread's output and mix it into main stream when appropriate
  numDbgSubthreadModes
} TDbgSubthreadModes;


#ifndef HARDCODED_CONFIG
extern cAppCharP const DbgOutFormatNames[numDbgOutFormats];
extern cAppCharP const DbgFoldingModeNames[numDbgFoldingModes];
extern cAppCharP const DbgFlushModeNames[numDbgFlushModes];
extern cAppCharP const DbgSubthreadModeNames[numDbgSubthreadModes];
#endif
extern cAppCharP const DbgOutFormatExtensions[numDbgOutFormats];

/// @brief Debug options container
class TDbgOptions {
public:
  // constructor
  TDbgOptions();
  // methods
  void clear(void);
  // properties
  TDbgOutFormats fOutputFormat; ///< format
  string fIndentString; ///< indent string
  string fCustomPrefix; ///< custom prefix (different xml header or html with different styles for example)
  string fCustomSuffix; ///< custom suffix (should match prefix)
  bool fSeparateMsgs; ///< separate message lines (needed especially in XML to avoid unformatted PCDATA block)
  bool fTimestampStructure; ///< include timestamp for structure elements (blocks)
  bool fTimestampForAll; ///< include timestamp information for every message
  bool fThreadIDForAll; ///< include thread ID information for every message
  TDbgFlushModes fFlushMode; ///< how and when to flush
  TDbgFoldingModes fFoldingMode; ///< if and how to fold HTML output
  bool fAppend; ///< if set, existing debug files will not be overwritten, but appended to
  TDbgSubthreadModes fSubThreadMode; ///< how to handle debug messages from subthreads
  uInt32 fSubThreadBufferMax; ///< how much to buffer for subthread maximally
}; // TDbgOptions


/// @brief Debug output channel
class TDbgOut {
  // construction/destruction
private:
  bool fDestructed; // flag which will be set once destruct() has been called - by the outermost derivate's destructor
public:
  TDbgOut();
  virtual ~TDbgOut();
  virtual void doDestruct(void); // will be called by destruct, derived must call inherited if they implement it
  void destruct(void); // to be called by ALL destructors of derivates.
  // methods
  /// @brief duplicate output channel
  virtual TDbgOut *clone(void) { return new TDbgOut; };
  /// @brief open debug output channel
  /// Notes:
  /// - Autocloses current channel if already open
  /// - May not actually open the channel, but should test if channel is writable
  /// @return                         false if debug channel cannot be opened and written to
  /// @param aDbgOutputName[in]       name (usually file name) of debug output channel
  /// @param aSuggestedExtension[in]  file extension suggested (may not be used depending on channel type)
  /// @param aFlushMode[in]           flush mode to be used on the channel
  /// @param aOverWrite[in]           if true, debug output channel (=file) will be overwritten (otherwise: appended to)
  /// @param aRawMode[in]             if true, debug output channel (=file) is opened in binary raw mode (for message dumps etc.)
  virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false) { return true; };
  /// @brief get current size of output file
  /// @return number of bytes, 0 if file is empty or for non-files (like console)
  virtual uInt32 dbgFileSize(void) { return 0; };
  /// @brief close and flush all log output
  virtual void closeDbg(void) { /* nop */ };
  /// @brief write single line to debug output channel
  /// @param aLine[in]                text for line to be written out (must not contain line ends)
  /// @param aForceFlush[in]          if true, debug output will be flushed to permanent storage regardless of current flush mode
  virtual void putLine(cAppCharP aLine, bool aForceFlush) { /* nop */};
  /// @brief write raw data to debug output channel (usually makes sense only when channel is opened in raw mode)
  /// @param aData[in]                pointer to data to be written
  /// @param aSize[in]                size in bytes of data block at aData to be written
  virtual void putRawData(cAppPointer aData, memSize aSize) { /* nop */};
protected:
  bool fIsOpen;
}; // TDbgOut



#ifndef NO_C_FILES

/// @brief Standard file debug output channel
class TStdFileDbgOut : public TDbgOut {
  typedef TDbgOut inherited;
public:
  // constructor/destructor
  TStdFileDbgOut();
  virtual ~TStdFileDbgOut();
  // methods
  virtual TDbgOut *clone(void) { return new TStdFileDbgOut; };
  virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
  virtual uInt32 dbgFileSize(void);
  virtual void closeDbg(void);
  virtual void putLine(cAppCharP aLine, bool aForceFlush);
  virtual void putRawData(cAppPointer aData, memSize aSize);
private:
  TDbgFlushModes fFlushMode;
  string fFileName;
  FILE * fFile;
}; // TStdFileDbgOut

#endif


/// @brief Output to console
class TConsoleDbgOut : public TDbgOut {
  typedef TDbgOut inherited;
public:
  // constructor/destructor
  TConsoleDbgOut();
  // methods
  virtual TDbgOut *clone(void) { return new TConsoleDbgOut; };
  virtual bool openDbg(cAppCharP aDbgOutputName, cAppCharP aSuggestedExtension, TDbgFlushModes aFlushMode, bool aOverWrite, bool aRawMode=false);
  virtual void closeDbg(void);
  virtual void putLine(cAppCharP aLine, bool aForceFlush);
  virtual void putRawData(cAppPointer aData, memSize aSize) { /* not supported on console, just NOP */ };
}; // TConsoleDbgOut


// Debug logger class
// ------------------

/// @brief hierachical block history
typedef struct BlockLevel {
  string fBlockName;
  uInt32 fBlockNo;
  struct BlockLevel *fNext;
} TBlockLevel;

class TDebugLogger;
class GZones;

/// @brief Debug logger base class (without subthread handling)
class TDebugLoggerBase {
public:
  // constructor/destructor
  TDebugLoggerBase(GZones *aGZonesP);
  virtual ~TDebugLoggerBase();
  // methods
  /// @brief install output channel handler object (and pass it's ownership!)
  /// @param aDbgOutP[in] output channel to be used for this logger (will be owned and finally destroyed by the logger)
  void installOutput(TDbgOut *aDbgOutP);
  /// @brief link this logger to another logger and redirect output to that logger
  /// @param aDebugLoggerP[in] another logger, that must be alive as long as this logger is alive
  void outputVia(TDebugLoggerBase *aDebugLoggerP);
  /// @brief check if an output channel is already established other than with default values
  bool outputEstablished(void) { return fOutStarted; };
  /// @brief set debug options
  void setOptions(const TDbgOptions *aDbgOptionsP) { fDbgOptionsP=aDbgOptionsP; };
  /// @brief get debug options pointer
  const TDbgOptions *getOptions(void) { return fDbgOptionsP; };
  // @brief convenience version for getting time
  lineartime_t getSystemNowAs(timecontext_t aContext);
  /// @brief get current debug mask for this logger.
  /// Note that setEnabled(false) will cause this to return 0 even if the mask itself is non-zero
  uInt32 getMask(void) { return fDebugEnabled ? fDebugMask : 0; };
  uInt32 getRealMask(void) { return fDebugMask; };
  /// @brief set new debug mask for this logger
  void setMask(uInt32 aDbgMask)
    { fDebugMask=aDbgMask; };
  /// @brief enable or disable this logger (but leave dbgMask intact)
  void setEnabled(bool aEnabled)
    { fDebugEnabled=aEnabled; };
  /// @brief set debug output path + filename (no extension, please)
  void setDebugPath(cAppCharP aPath) { fDbgPath = aPath; };
  /// @brief append to debug output path + filename (no extension, please)
  void appendToDebugPath(cAppCharP aPathElement) { fDbgPath += aPathElement; };
  /// @brief get debug output file path (w/o extension)
  cAppCharP getDebugPath(void) { return fOutputLoggerP ? fOutputLoggerP->getDebugPath() : fDbgPath.c_str(); };
  /// @brief get debug output file name (w/o path or extension)
  cAppCharP getDebugFilename(void) { if (fOutputLoggerP) return fOutputLoggerP->getDebugFilename(); size_t n=fDbgPath.find_last_of("\\/:"); return fDbgPath.c_str()+(n!=string::npos ? n+1 : 0); };
  /// @brief get debug output file extension
  cAppCharP getDebugExt(void) { return fOutputLoggerP ? fOutputLoggerP->getDebugExt() : fDbgOptionsP ? DbgOutFormatExtensions[fDbgOptionsP->fOutputFormat] : ""; };
  // - normal output
  /// @brief Write text to debug output channel.
  /// Notes:
  /// - Line will be terminated by linefeed automatically (no need to include a linefeed for single line message)
  /// - \n chars can be used to separate multi-line output. Formatter will take care that
  ///   all lines are equally indented/formatted/prefixed
  /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
  /// @param aText[in] text to be written out
  /// @param aTextSize[in] if>0, this is the maximum number of chars to output from aText
  virtual void DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
  /// @brief Write formatted text to debug output channel.
  /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
  /// @param aFormat[in] format text in vprintf style to be written out
  /// @param aArgs[in] varargs in vprintf style
  virtual void DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
  /// @brief Write formatted text to debug output channel.
  /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
  /// @param aFormat[in] format text in printf style to be written out
  void DebugPrintf(uInt32 aDbgMask, cAppCharP aFormat, ...)
  	#ifdef __GNUC__
    __attribute__((format(printf, 3, 4)))
		#endif
    ;
  /// @brief set debug mask to be used for next DebugPrintfLastMask() call
  /// @param aDbgMask debug mask, bits set here must be set in the debuglogger's own mask in order to display the debug text
  virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
  /// @brief like DebugPrintf(), but using mask previously set by setNextMask()
  /// @param aFormat[in] format text in printf style to be written out
  void DebugPrintfLastMask(cAppCharP aFormat, ...)
		#ifdef __GNUC__
    __attribute__((format(printf, 2, 3)))
		#endif
    ;
  // - Blocks
  /// @brief Open structure Block. Depending on the output format, this will generate indent, XML tags, HTML headers etc.
  /// @param aBlockName[in]  Name of Block. Will be used e.g. for tag name in XML. Intention is to group similar entities with the same BlockName
  /// @param aBlockTitle[in] Title (descriptive text) of Block.
  /// @param aCollapsed[in]  If set, and folding mode is auto, block will be initially collapsed when log is opened in browser.
  /// @param aBlockFmt[in]   Format string for additional Block info. Should contain one or multiple tag=value pairs, separated by the pipe char |.
  ///                          This will be used to generate XML attributes or other identifiers.
  /// @param aArgs[in]         varargs in vprintf style for aBlockFmt
  virtual void DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
  /// @brief Open structure Block, printf style variant
  void DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
    #ifdef __GNUC__
    __attribute__((format(printf, 5, 6)))
		#endif
      ;
  void DebugOpenBlockExpanded(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
    #ifdef __GNUC__
    __attribute__((format(printf, 4, 5)))
		#endif
    ;
  void DebugOpenBlockCollapsed(cAppCharP aBlockName, cAppCharP aBlockTitle, cAppCharP aBlockFmt, ...)
    #ifdef __GNUC__
    __attribute__((format(printf, 4, 5)))
		#endif
    ;
  /// @brief Open structure Block, without any attributes
  virtual void DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle=NULL, bool aCollapsed=false);
  /// @brief Close structure Block. Name is used to close eventually unclosed contained Blocks automatically.
  virtual void DebugCloseBlock(cAppCharP aBlockName);
protected:
  // helper methods
  /// @brief start debugging output if needed and sets fOutStarted
  bool DebugStartOutput(void);
  /// @brief Output single line to debug channel (includes indenting, but no other formatting)
  void DebugPutLine(cAppCharP aText, stringSize aTextSize=0, bool aPre=false);
  /// @brief finalize debugging output
  void DebugFinalizeOutput(void);
  /// @brief get block number
  uInt32 getBlockNo(void) { return fOutputLoggerP ? fOutputLoggerP->getBlockNo() : fBlockNo; };
  /// @brief increment block number
  void nextBlock(void) { if (fOutputLoggerP) fOutputLoggerP->nextBlock(); else fBlockNo++; };
  /// @brief internal helper for closing debug Blocks
  /// @param aBlockName[in]   Name of Block to close. All Blocks including the first with given name will be closed. If NULL, all Blocks will be closed.
  /// @param aCloseComment[in]  Comment about closing Block. If NULL, no comment will be shown (unless implicit closes occur, which auto-creates a comment)
  void internalCloseBlocks(cAppCharP aBlockName, cAppCharP aCloseComment);
  // Variables
  TDbgOut *fDbgOutP; // the debug output
  string fDbgPath; // the output path+filename (w/o extension)
  const TDbgOptions *fDbgOptionsP; // the debug options
  uInt32 fDebugMask; // the debug mask
  bool fDebugEnabled; // on-off-switch for debugging output
  uInt32 fNextDebugMask; // debug mask to be used for next DebugPrintfLastMask()
  uInt16 fIndent; // the current indent
  TBlockLevel *fBlockHistory; // the linked list of Block history entries
  bool fOutStarted; // set if output has started
  uInt32 fBlockNo; // block count for folding
  GZones *fGZonesP; // zones list for time conversions
  TDebugLoggerBase *fOutputLoggerP; // another logger to be used for output
}; // TDebugLoggerBase


#ifdef MULTI_THREAD_SUPPORT
/// @brief Subthread log handling
typedef struct SubThreadLog {
  uInt32 fThreadID;
  struct SubThreadLog *fNext;
  TDebugLoggerBase *fSubThreadLogger;
} TSubThreadLog;
#endif



/// @brief Debug logger class
class TDebugLogger : public TDebugLoggerBase {
  typedef TDebugLoggerBase inherited;
public:
  // constructor/destructor
  TDebugLogger(GZones *aGZonesP);
  virtual ~TDebugLogger();
  // methods
  #ifdef MULTI_THREAD_SUPPORT
  virtual void DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize=0, bool aPreFormatted=false);
  virtual void DebugVPrintf(uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs);
  virtual void DebugVOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, va_list aArgs);
  virtual void DebugCloseBlock(cAppCharP aBlockName);
  virtual TDebugLoggerBase &setNextMask(uInt32 aDbgMask);
  #endif
  // - thread debug output serializing
  /// @brief output all buffered subthread's output in a special subthread Block in the main output
  void DebugShowSubThreadOutput(void);
  /// @brief signals the calling thread that it is done with doing output for now.
  /// @param aRemoveIt[in] if set, do remove thread from the subthread logger list
	/// Notes:
  /// - If the main thread is doing this and we have bufferandmix mode, the next subthread will be allowed
  ///   to write into the output channel until a new main thread gains control via DebugDefineMainThread();
  void DebugThreadOutputDone(bool aRemoveIt=false);
  /// @brief Define the current calling thread as the main debug thread
  /// Note: This is used for example when starting to process the next request of a session which eventually
  //        occurs from another thread).
  void DebugDefineMainThread(void);
private:
  #ifdef MULTI_THREAD_SUPPORT
  // helpers
  /// @brief find (and possibly delete) subthread record
  /// @param aAndRemove[in] if set, the subthread record will be removed in a thread safe way
  ///        IF AND ONLY IF aThreadID is the calling thread (i.e. only own thread may be removed from the list)!
  ///        Note that the caller must take care of deleting the subthread record
  TSubThreadLog *findSubThread(uInt32 aThreadID, bool aAndRemove=false);
  /// @brief find or create logger for subthread
  TDebugLoggerBase *getThreadLogger(bool aCreateNew=true);
  // Variables
  uInt32 fMainThreadID;
  TSubThreadLog *fSubThreadLogs; // the linked list of active subthreads
  TDebugLoggerBase *fSilentLoggerP; // a silent (inactive) logger required for suppressed subthreads
  #endif
}; // TDebugLogger




} // namespace sysync

#endif // SYDEBUG

#endif // DEBUGLOGGER_H


// eof