OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Log.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Log file handling */
19 
20 #include "C4Include.h"
21 #include "lib/C4Log.h"
22 
23 #include "script/C4AulDebug.h"
24 #include "editor/C4Console.h"
25 #include "gui/C4GameLobby.h"
26 #include "game/C4Game.h"
27 #include "lib/C4LogBuf.h"
28 #include "c4group/C4Language.h"
29 #include "network/C4Network2.h"
30 #include "game/C4GraphicsSystem.h"
31 #include "config/C4Config.h"
32 #include "c4group/C4Components.h"
33 #include "platform/C4Window.h"
34 #include "graphics/C4Shader.h"
35 
36 #ifdef HAVE_SYS_FILE_H
37 #include <sys/file.h>
38 #endif
39 
40 #if defined(HAVE_SHARE_H) || defined(_WIN32)
41 #include <share.h>
42 #endif
43 
44 FILE *C4LogFile=nullptr;
45 FILE *C4ShaderLogFile = nullptr;
48 
50 
51 bool OpenLog()
52 {
53  // open
54  sLogFileName = C4CFN_Log; int iLog = 2;
55 #ifdef _WIN32
56  while (!(C4LogFile = _fsopen(Config.AtUserDataPath(sLogFileName.getData()), "wt", _SH_DENYWR)))
57 #elif defined(HAVE_SYS_FILE_H)
58  while (!(C4LogFile = fopen(Config.AtUserDataPath(sLogFileName.getData()), "wb")) || flock(fileno(C4LogFile),LOCK_EX|LOCK_NB))
59 #else
60  while (!(C4LogFile = fopen(Config.AtUserDataPath(sLogFileName.getData()), "wb")))
61 #endif
62  {
63  if(C4LogFile) fclose(C4LogFile); // Already locked by another instance?
64  // If the file does not yet exist, the directory is r/o
65  // don't go on then, or we have an infinite loop
66  if (access(Config.AtUserDataPath(sLogFileName.getData()), 0))
67  return false;
68  // try different name
69  sLogFileName.Format(C4CFN_LogEx, iLog++);
70  }
71  // save start time
72  time(&C4LogStartTime);
73  return true;
74 }
75 
77 {
78  // shader log in editor mode (only one file)
79  bool success = true;
80  if (C4Shader::IsLogging())
81  {
82 #ifdef _WIN32
83  C4ShaderLogFile = _fsopen(Config.AtUserDataPath(C4CFN_LogShader), "wt", _SH_DENYWR);
84 #elif defined(HAVE_SYS_FILE_H)
86  if (C4ShaderLogFile && flock(fileno(C4ShaderLogFile), LOCK_EX | LOCK_NB) != 0)
87  {
88  DebugLog("Couldn't lock shader log file, closing.");
89  fclose(C4ShaderLogFile);
90  C4ShaderLogFile = nullptr;
91  }
92 #else
93  C4ShaderLogFile = fopen(Config.AtUserDataPath(C4CFN_LogShader), "wb");
94 #endif
95  if (!C4ShaderLogFile) success = false;
96  }
97  return success;
98 }
99 
100 bool CloseLog()
101 {
102  // close
103  if (C4ShaderLogFile) fclose(C4ShaderLogFile); C4ShaderLogFile = nullptr;
104  if (C4LogFile) fclose(C4LogFile); C4LogFile = nullptr;
105  // ok
106  return true;
107 }
108 
109 int GetLogFD()
110 {
111  if (C4LogFile)
112  return fileno(C4LogFile);
113  else
114  return -1;
115 }
116 
117 bool LogSilent(const char *szMessage, bool fConsole)
118 {
119  if (!Application.AssertMainThread()) return false;
120  // security
121  if (!szMessage) return false;
122 
123  // add timestamp
124  time_t timenow; time(&timenow);
125  StdStrBuf TimeMessage;
126  TimeMessage.SetLength(11 + SLen(szMessage) + 1);
127  strftime(TimeMessage.getMData(), 11 + 1, "[%H:%M:%S] ", localtime(&timenow));
128 
129  // output until all data is written
130  const char *pSrc = szMessage;
131  do
132  {
133  // timestamp will always be that length
134  char *pDest = TimeMessage.getMData() + 11;
135 
136  // copy rest of message, skip tags
137  C4Markup Markup(false);
138  while (*pSrc)
139  {
140  Markup.SkipTags(&pSrc);
141  // break on crlf
142  while (*pSrc == '\r') pSrc++;
143  if (*pSrc == '\n') { pSrc++; break; }
144  // copy otherwise
145  if (*pSrc) *pDest++ = *pSrc++;
146  }
147  *pDest++='\n'; *pDest = '\0';
148 
149  // Save into log file
150  if (C4LogFile)
151  {
152  fputs(TimeMessage.getData(),C4LogFile);
153  fflush(C4LogFile);
154  }
155 
156  // Save into record log file, if available
157  if(Control.GetRecord())
158  {
159  Control.GetRecord()->GetLogFile()->Write(TimeMessage.getData(), TimeMessage.getLength());
160  #ifdef IMMEDIATEREC
162  #endif
163  }
164 
165 
166  // Write to console
167  if (fConsole)
168  {
169 #if defined(_WIN32)
170  // debug: output to VC console when running with debugger
171  // Otherwise, print to stdout to allow capturing the log.
172  if (IsDebuggerPresent())
173  OutputDebugString(TimeMessage.GetWideChar());
174  else
175 #endif
176  {
177  fputs(TimeMessage.getData(),stdout);
178  fflush(stdout);
179  }
180  }
181 
182  }
183  while (*pSrc);
184 
185  return true;
186 }
187 
188 bool LogSilent(const char *szMessage)
189 {
190  return LogSilent(szMessage, false);
191 }
192 
193 int iDisableLog = 0;
194 
195 bool Log(const char *szMessage)
196 {
197  if (!Application.AssertMainThread()) return false;
198  if (iDisableLog) return true;
199  // security
200  if (!szMessage) return false;
201 
202 #ifndef NOAULDEBUG
203  // Pass on to debugger
204  if (C4AulDebug *pDebug = C4AulDebug::GetDebugger())
205  pDebug->OnLog(szMessage);
206 #endif
207  // Pass on to console
208  Console.Out(szMessage);
209  // pass on to lobby
211  if (pLobby) pLobby->OnLog(szMessage);
212 
213  // Add message to log buffer
214  bool fNotifyMsgBoard = false;
216  {
217  ::GraphicsSystem.MessageBoard->AddLog(szMessage);
218  fNotifyMsgBoard = true;
219  }
220 
221  // log
222  LogSilent(szMessage, true);
223 
224  // Notify message board
225  if (fNotifyMsgBoard) ::GraphicsSystem.MessageBoard->LogNotify();
226 
227  return true;
228 }
229 
230 bool LogFatal(const char *szMessage)
231 {
232  if (!szMessage) szMessage = "(null)";
233  // add to fatal error message stack - if not already in there (avoid duplication)
234  if (!SSearch(sFatalError.getData(), szMessage))
235  {
236  if (!sFatalError.isNull()) sFatalError.AppendChar('|');
237  sFatalError.Append(szMessage);
238  }
239  // write to log - note that Log might overwrite a static buffer also used in szMessage
240  return !!Log(FormatString(LoadResStr("IDS_ERR_FATAL"), szMessage).getData());
241 }
242 
244 {
245  sFatalError.Clear();
246 }
247 
248 const char *GetFatalError()
249 {
250  return sFatalError.getData();
251 }
252 
253 bool LogF(const char *strMessage, ...)
254 {
255  va_list args; va_start(args, strMessage);
256  // Compose formatted message
257  StdStrBuf Buf;
258  Buf.FormatV(strMessage, args);
259  // Log
260  return Log(Buf.getData());
261 }
262 
263 bool LogSilentF(const char *strMessage, ...)
264 {
265  va_list args; va_start(args, strMessage);
266  // Compose formatted message
267  StdStrBuf Buf;
268  Buf.FormatV(strMessage, args);
269  // Log
270  return LogSilent(Buf.getData());
271 }
272 
273 bool DebugLog(const char *strMessage)
274 {
275  if (Game.DebugMode)
276  return Log(strMessage);
277  else
278  return LogSilent(strMessage);
279 }
280 
281 bool DebugLogF(const char *strMessage ...)
282 {
283  va_list args; va_start(args, strMessage);
284  StdStrBuf Buf;
285  Buf.FormatV(strMessage, args);
286  return DebugLog(Buf.getData());
287 }
288 
289 size_t GetLogPos()
290 {
291  // get current log position
292  return FileSize(sLogFileName.getData());
293 }
294 
295 bool GetLogSection(size_t iStart, size_t iLength, StdStrBuf &rsOut)
296 {
297  if (!iLength) { rsOut.Clear(); return true; }
298  // read section from log file
299  StdStrBuf BufOrig;
300  if (!BufOrig.LoadFromFile(sLogFileName.getData())) return false;
301  char *szBuf = BufOrig.getMData();
302  size_t iSize = BufOrig.getSize(); // size excluding terminator
303  // reduce to desired buffer section
304  if (iStart > iSize) iStart = iSize;
305  if (iStart + iLength > iSize) iLength = iSize - iStart;
306  szBuf += iStart; szBuf[iLength] = '\0';
307  // strip timestamps; convert linebreaks to Clonk-linebreaks '|'
308  char *szPosWrite=szBuf; const char *szPosRead=szBuf;
309  while (*szPosRead)
310  {
311  // skip timestamp
312  if (*szPosRead == '[')
313  while (*szPosRead && *szPosRead != ']') { --iSize; ++szPosRead; }
314  // skip whitespace behind timestamp
315  if (!*szPosRead) break;
316  szPosRead++;
317  // copy data until linebreak
318  size_t iLen=0;
319  while (*szPosRead && *szPosRead != 0x0d && *szPosRead != 0x0a)
320  { ++szPosRead; ++iLen; }
321  if (iLen && szPosRead-iLen != szPosWrite) memmove(szPosWrite, szPosRead-iLen, iLen);
322  szPosWrite += iLen;
323  // skip additional linebreaks
324  while (*szPosRead == 0x0d || *szPosRead == 0x0a) ++szPosRead;
325  // write a Clonk-linebreak
326  if (*szPosRead) *szPosWrite++ = '|';
327  }
328  // done; create string buffer from data
329  rsOut.Copy(szBuf, szPosWrite - szBuf);
330  // done, success
331  return true;
332 }
333 
334 bool ShaderLog(const char *szMessage)
335 {
336  // security
337  if (!C4ShaderLogFile) return false;
338  if (!Application.AssertMainThread()) return false;
339  if (!szMessage) return false;
340  // output into shader log file
341  fputs(szMessage, C4ShaderLogFile);
342  fputs("\n", C4ShaderLogFile);
343  fflush(C4ShaderLogFile);
344  return true;
345 }
346 
347 bool ShaderLogF(const char *strMessage ...)
348 {
349  va_list args; va_start(args, strMessage);
350  StdStrBuf Buf;
351  Buf.FormatV(strMessage, args);
352  return ShaderLog(Buf.getData());
353 }
const char * getData() const
Definition: StdBuf.h:450
bool AssertMainThread()
Definition: C4App.h:123
FILE * C4ShaderLogFile
Definition: C4Log.cpp:45
bool ShaderLog(const char *szMessage)
Definition: C4Log.cpp:334
class C4GameLobby::MainDlg * GetLobby() const
Definition: C4Network2.h:216
C4Config Config
Definition: C4Config.cpp:837
time_t C4LogStartTime
Definition: C4Log.cpp:46
C4Console Console
Definition: C4Globals.cpp:45
bool LogSilent(const char *szMessage, bool fConsole)
Definition: C4Log.cpp:117
CStdFile * GetLogFile()
Definition: C4Record.h:278
C4Game Game
Definition: C4Globals.cpp:52
StdStrBuf sLogFileName
Definition: C4Log.cpp:47
StdStrBuf sFatalError
Definition: C4Log.cpp:49
void Clear()
Definition: StdBuf.h:474
bool DebugLog(const char *strMessage)
Definition: C4Log.cpp:273
const char * SSearch(const char *szString, const char *szIndex)
Definition: Standard.cpp:333
bool OpenExtraLogs()
Definition: C4Log.cpp:76
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:181
C4Record * GetRecord()
size_t SLen(const char *sptr)
Definition: Standard.h:78
void OnLog(const char *szLogMsg, DWORD dwClr=C4GUI_LogFontClr)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
char * getMData()
Definition: StdBuf.h:451
void AppendChar(char cChar)
Definition: StdBuf.h:596
bool LoadFromFile(const char *szFile)
Definition: StdBuf.cpp:80
int GetLogFD()
Definition: C4Log.cpp:109
bool Write(const void *pBuffer, int iSize)
Definition: CStdFile.cpp:244
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:263
bool SkipTags(const char **ppText)
Definition: C4Markup.cpp:105
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:281
static bool IsLogging()
Definition: C4Shader.cpp:667
C4Network2 Network
Definition: C4Globals.cpp:53
static C4AulDebug * GetDebugger()
Definition: C4AulDebug.h:31
C4GameControl Control
C4GraphicsSystem GraphicsSystem
Definition: C4Globals.cpp:51
#define C4CFN_Log
Definition: C4Components.h:141
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
void Out(const char *message)
Definition: C4Console.cpp:689
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:230
size_t GetLogPos()
Definition: C4Log.cpp:289
bool GetLogSection(size_t iStart, size_t iLength, StdStrBuf &rsOut)
Definition: C4Log.cpp:295
size_t FileSize(const char *szFilename)
Definition: StdFile.cpp:448
bool OpenLog()
Definition: C4Log.cpp:51
size_t getSize() const
Definition: StdBuf.h:452
const char * GetFatalError()
Definition: C4Log.cpp:248
bool isNull() const
Definition: StdBuf.h:449
std::unique_ptr< C4MessageBoard > MessageBoard
const char * AtUserDataPath(const char *szFilename)
Definition: C4Config.cpp:530
bool ShaderLogF(const char *strMessage...)
Definition: C4Log.cpp:347
FILE * C4LogFile
Definition: C4Log.cpp:44
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
int iDisableLog
Definition: C4Log.cpp:193
void FormatV(const char *szFmt, va_list args)
Definition: StdBuf.cpp:189
#define C4CFN_LogEx
Definition: C4Components.h:142
size_t getLength() const
Definition: StdBuf.h:453
void SetLength(size_t iLength)
Definition: StdBuf.h:517
void ResetFatalError()
Definition: C4Log.cpp:243
bool CloseLog()
Definition: C4Log.cpp:100
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
void Copy()
Definition: StdBuf.h:475
bool Flush()
Definition: CStdFile.h:71
#define C4CFN_LogShader
Definition: C4Components.h:143
C4Application Application
Definition: C4Globals.cpp:44
bool DebugMode
Definition: C4Game.h:146
int iSize
Definition: TstC4NetIO.cpp:35
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277