1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2005-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 //
24
25 #include "Logger.h"
26 #include "amule.h"
27 #include "Preferences.h"
28 #include <common/Macros.h>
29 #include <common/MacrosProgramSpecific.h>
30 #include <sstream>
31 #include <wx/tokenzr.h>
32 #include <wx/wfstream.h>
33 #include <wx/sstream.h>
34 #include <wx/filename.h>
35
36
37 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_LOGLINE)
38
39
40 CDebugCategory g_debugcats[] = {
41 CDebugCategory( logGeneral, wxT("General") ),
42 CDebugCategory( logHasher, wxT("Hasher") ),
43 CDebugCategory( logClient, wxT("ED2k Client") ),
44 CDebugCategory( logLocalClient, wxT("Local Client Protocol") ),
45 CDebugCategory( logRemoteClient, wxT("Remote Client Protocol") ),
46 CDebugCategory( logPacketErrors, wxT("Packet Parsing Errors") ),
47 CDebugCategory( logCFile, wxT("CFile") ),
48 CDebugCategory( logFileIO, wxT("FileIO") ),
49 CDebugCategory( logZLib, wxT("ZLib") ),
50 CDebugCategory( logAICHThread, wxT("AICH-Hasher") ),
51 CDebugCategory( logAICHTransfer, wxT("AICH-Transfer") ),
52 CDebugCategory( logAICHRecovery, wxT("AICH-Recovery") ),
53 CDebugCategory( logListenSocket, wxT("ListenSocket") ),
54 CDebugCategory( logCredits, wxT("Credits") ),
55 CDebugCategory( logClientUDP, wxT("ClientUDPSocket") ),
56 CDebugCategory( logDownloadQueue, wxT("DownloadQueue") ),
57 CDebugCategory( logIPFilter, wxT("IPFilter") ),
58 CDebugCategory( logKnownFiles, wxT("KnownFileList") ),
59 CDebugCategory( logPartFile, wxT("PartFiles") ),
60 CDebugCategory( logSHAHashSet, wxT("SHAHashSet") ),
61 CDebugCategory( logServer, wxT("Servers") ),
62 CDebugCategory( logProxy, wxT("Proxy") ),
63 CDebugCategory( logSearch, wxT("Searching") ),
64 CDebugCategory( logServerUDP, wxT("ServerUDP") ),
65 CDebugCategory( logClientKadUDP, wxT("Client Kademlia UDP") ),
66 CDebugCategory( logKadSearch, wxT("Kademlia Search") ),
67 CDebugCategory( logKadRouting, wxT("Kademlia Routing") ),
68 CDebugCategory( logKadIndex, wxT("Kademlia Indexing") ),
69 CDebugCategory( logKadMain, wxT("Kademlia Main Thread") ),
70 CDebugCategory( logKadPrefs, wxT("Kademlia Preferences") ),
71 CDebugCategory( logPfConvert, wxT("PartFileConvert") ),
72 CDebugCategory( logMuleUDP, wxT("MuleUDPSocket" ) ),
73 CDebugCategory( logThreads, wxT("ThreadScheduler" ) ),
74 CDebugCategory( logUPnP, wxT("Universal Plug and Play" ) ),
75 CDebugCategory( logKadUdpFwTester, wxT("Kademlia UDP Firewall Tester") ),
76 CDebugCategory( logKadPacketTracking, wxT("Kademlia Packet Tracking") ),
77 CDebugCategory( logKadEntryTracking, wxT("Kademlia Entry Tracking") ),
78 CDebugCategory( logEC, wxT("External Connect") ),
79 CDebugCategory( logHTTP, wxT("HTTP") ),
80 CDebugCategory( logAsio, wxT("Asio Sockets") )
81 };
82
83
84 const int categoryCount = itemsof(g_debugcats);
85
86
87
88 #ifdef __DEBUG__
IsEnabled(DebugType type) const89 bool CLogger::IsEnabled( DebugType type ) const
90 {
91 int index = (int)type;
92
93 if ( index >= 0 && index < categoryCount ) {
94 const CDebugCategory& cat = g_debugcats[ index ];
95 wxASSERT( type == cat.GetType() );
96
97 return ( cat.IsEnabled() && thePrefs::GetVerbose() );
98 }
99
100 wxFAIL;
101 return false;
102 }
103 #endif
104
105
SetEnabled(DebugType type,bool enabled)106 void CLogger::SetEnabled( DebugType type, bool enabled )
107 {
108 int index = (int)type;
109
110 if ( index >= 0 && index < categoryCount ) {
111 CDebugCategory& cat = g_debugcats[ index ];
112 wxASSERT( type == cat.GetType() );
113
114 cat.SetEnabled( enabled );
115 } else {
116 wxFAIL;
117 }
118 }
119
120
AddLogLine(const wxString & DEBUG_ONLY (file),int DEBUG_ONLY (line),bool critical,DebugType type,const wxString & str,bool toStdout,bool toGUI)121 void CLogger::AddLogLine(
122 const wxString& DEBUG_ONLY(file),
123 int DEBUG_ONLY(line),
124 bool critical,
125 DebugType type,
126 const wxString &str,
127 bool toStdout,
128 bool toGUI)
129 {
130 wxString msg(str);
131 // handle Debug messages
132 if (type != logStandard) {
133 if (!critical && !IsEnabled(type)) {
134 return;
135 }
136 if (!critical && thePrefs::GetVerboseLogfile()) {
137 // print non critical debug messages only to the logfile
138 toGUI = false;
139 }
140 int index = (int)type;
141
142 if ( index >= 0 && index < categoryCount ) {
143 const CDebugCategory& cat = g_debugcats[ index ];
144 wxASSERT(type == cat.GetType());
145
146 msg = cat.GetName() + wxT(": ") + msg;
147 } else {
148 wxFAIL;
149 }
150 }
151
152 #ifdef __DEBUG__
153 if (line) {
154 msg = file.AfterLast(wxFileName::GetPathSeparator()).AfterLast(wxT('/')) << wxT("(") << line << wxT("): ") + msg;
155 }
156 #endif
157
158 if (toGUI && !wxThread::IsMain()) {
159 // put to background
160 CLoggingEvent Event(critical, toStdout, toGUI, msg);
161 AddPendingEvent(Event);
162 } else {
163 // Try to handle events immediatly when possible (to save to file).
164 DoLines(msg, critical, toStdout, toGUI);
165 }
166 }
167
168
AddLogLine(const wxString & file,int line,bool critical,DebugType type,const std::ostringstream & msg)169 void CLogger::AddLogLine(
170 const wxString &file,
171 int line,
172 bool critical,
173 DebugType type,
174 const std::ostringstream &msg)
175 {
176 AddLogLine(file, line, critical, type, wxString(char2unicode(msg.str().c_str())));
177 }
178
179
GetDebugCategory(int index)180 const CDebugCategory& CLogger::GetDebugCategory( int index )
181 {
182 wxASSERT( index >= 0 && index < categoryCount );
183
184 return g_debugcats[ index ];
185 }
186
187
GetDebugCategoryCount()188 unsigned int CLogger::GetDebugCategoryCount()
189 {
190 return categoryCount;
191 }
192
193
OpenLogfile(const wxString & name)194 bool CLogger::OpenLogfile(const wxString & name)
195 {
196 applog = new wxFFileOutputStream(name);
197 bool ret = applog->Ok();
198 if (ret) {
199 FlushApplog();
200 m_LogfileName = name;
201 } else {
202 CloseLogfile();
203 }
204 return ret;
205 }
206
207
CloseLogfile()208 void CLogger::CloseLogfile()
209 {
210 delete applog;
211 applog = NULL;
212 m_LogfileName.Clear();
213 }
214
215
OnLoggingEvent(class CLoggingEvent & evt)216 void CLogger::OnLoggingEvent(class CLoggingEvent& evt)
217 {
218 DoLines(evt.Message(), evt.IsCritical(), evt.ToStdout(), evt.ToGUI());
219 }
220
221
DoLines(const wxString & lines,bool critical,bool toStdout,bool toGUI)222 void CLogger::DoLines(const wxString & lines, bool critical, bool toStdout, bool toGUI)
223 {
224 // Remove newspace at end
225 wxString bufferline = lines.Strip(wxString::trailing);
226
227 // Create the timestamp
228 wxString stamp = wxDateTime::Now().FormatISODate() + wxT(" ") + wxDateTime::Now().FormatISOTime()
229 #ifdef CLIENT_GUI
230 + wxT(" (remote-GUI): ");
231 #else
232 + wxT(": ");
233 #endif
234
235 // critical lines get a ! prepended, ordinary lines a blank
236 // logfile-only lines get a . to prevent transmission on EC
237 wxString prefix = !toGUI ? wxT(".") : (critical ? wxT("!") : wxT(" "));
238
239 if ( bufferline.IsEmpty() ) {
240 // If it's empty we just write a blank line with no timestamp.
241 DoLine(wxT(" \n"), toStdout, toGUI);
242 } else {
243 // Split multi-line messages into individual lines
244 wxStringTokenizer tokens( bufferline, wxT("\n") );
245 while ( tokens.HasMoreTokens() ) {
246 wxString fullline = prefix + stamp + tokens.GetNextToken() + wxT("\n");
247 DoLine(fullline, toStdout, toGUI);
248 }
249 }
250 }
251
252
DoLine(const wxString & line,bool toStdout,bool GUI_ONLY (toGUI))253 void CLogger::DoLine(const wxString & line, bool toStdout, bool GUI_ONLY(toGUI))
254 {
255 {
256 wxMutexLocker lock(m_lineLock);
257 ++m_count;
258
259 // write to logfile
260 m_ApplogBuf += line;
261 FlushApplog();
262
263 // write to Stdout
264 if (m_StdoutLog || toStdout) {
265 printf("%s", (const char*)unicode2char(line));
266 }
267 }
268 #ifndef AMULE_DAEMON
269 // write to Listcontrol
270 if (toGUI) {
271 theApp->AddGuiLogLine(line);
272 }
273 #endif
274 }
275
276
EmergencyLog(const wxString & message,bool closeLog)277 void CLogger::EmergencyLog(const wxString &message, bool closeLog)
278 {
279 fprintf(stderr, "%s", (const char*)unicode2char(message));
280 m_ApplogBuf += message;
281 FlushApplog();
282 if (closeLog && applog) {
283 applog->Close();
284 applog = NULL;
285 }
286 }
287
288
FlushApplog()289 void CLogger::FlushApplog()
290 {
291 if (applog) { // Wait with output until logfile is actually opened
292 wxStringInputStream stream(m_ApplogBuf);
293 (*applog) << stream;
294 applog->Sync();
295 m_ApplogBuf.Clear();
296 }
297 }
298
299 CLogger theLogger;
300
BEGIN_EVENT_TABLE(CLogger,wxEvtHandler)301 BEGIN_EVENT_TABLE(CLogger, wxEvtHandler)
302 EVT_MULE_LOGGING(CLogger::OnLoggingEvent)
303 END_EVENT_TABLE()
304
305
306 CLoggerTarget::CLoggerTarget()
307 {
308 }
309
310 #if wxCHECK_VERSION(2, 9, 0)
DoLogText(const wxString & msg)311 void CLoggerTarget::DoLogText(const wxString &msg)
312 {
313 // prevent infinite recursion
314 static bool recursion = false;
315 if (recursion) {
316 return;
317 }
318 recursion = true;
319
320 // This is much simpler than manually handling all wx log-types.
321 if (msg.StartsWith(_("ERROR: ")) || msg.StartsWith(_("WARNING: "))) {
322 AddLogLineC(msg);
323 } else {
324 AddLogLineN(msg);
325 }
326
327 recursion = false;
328 }
329 #else
DoLogString(const wxChar * msg,time_t)330 void CLoggerTarget::DoLogString(const wxChar* msg, time_t)
331 {
332 // prevent infinite recursion
333 static bool recursion = false;
334 if (recursion) {
335 return;
336 }
337 recursion = true;
338
339 wxCHECK_RET(msg, wxT("Log message is NULL in DoLogString!"));
340
341 wxString str(msg);
342
343 // This is much simpler than manually handling all wx log-types.
344 // cppcheck-suppress duplicateBranch
345 if (str.StartsWith(_("ERROR: ")) || str.StartsWith(_("WARNING: "))) {
346 AddLogLineC(str);
347 } else {
348 AddLogLineN(str);
349 }
350
351 recursion = false;
352 }
353 #endif
354
CLoggerAccess()355 CLoggerAccess::CLoggerAccess()
356 {
357 m_bufferlen = 4096;
358 m_buffer = new wxCharBuffer(m_bufferlen);
359 m_logfile = NULL;
360 Reset();
361 }
362
363
Reset()364 void CLoggerAccess::Reset()
365 {
366 delete m_logfile;
367 m_logfile = new wxFFileInputStream(theLogger.GetLogfileName());
368 m_pos = 0;
369 m_ready = false;
370 }
371
372
~CLoggerAccess()373 CLoggerAccess::~CLoggerAccess()
374 {
375 delete m_buffer;
376 delete m_logfile;
377 }
378
379
380 //
381 // read a line of text from the logfile if available
382 // (can't believe there's no library function for this >:( )
383 //
HasString()384 bool CLoggerAccess::HasString()
385 {
386 while (!m_ready) {
387 int c = m_logfile->GetC();
388 if (c == wxEOF) {
389 break;
390 }
391 // check for buffer overrun
392 if (m_pos == m_bufferlen) {
393 m_bufferlen += 1024;
394 m_buffer->extend(m_bufferlen);
395 }
396 m_buffer->data()[m_pos++] = c;
397 if (c == '\n') {
398 if (m_buffer->data()[0] == '.') {
399 // Log-only line, skip
400 m_pos = 0;
401 } else {
402 m_ready = true;
403 }
404 }
405 }
406 return m_ready;
407 }
408
409
GetString(wxString & s)410 bool CLoggerAccess::GetString(wxString & s)
411 {
412 if (!HasString()) {
413 return false;
414 }
415 s = wxString(m_buffer->data(), wxConvUTF8, m_pos);
416 m_pos = 0;
417 m_ready = false;
418 return true;
419 }
420
421 // Functions for EC logging
422
423 #ifdef __DEBUG__
424 #include "ec/cpp/ECLog.h"
425
ECLogIsEnabled()426 bool ECLogIsEnabled()
427 {
428 return theLogger.IsEnabled(logEC);
429 }
430
DoECLogLine(const wxString & line)431 void DoECLogLine(const wxString &line)
432 {
433 // without file/line
434 theLogger.AddLogLine(wxEmptyString, 0, false, logStandard, line, false, false);
435 }
436
437 #endif /* __DEBUG__ */
438 // File_checked_for_headers
439