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