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