Improve the code by static code analysis [2/3]: Performance
[amule.git] / src / Logger.cpp
blob3596340e47ca73410257e5b17d712289b3b0399f
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.
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.
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
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>
37 DEFINE_LOCAL_EVENT_TYPE(MULE_EVT_LOGLINE)
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") )
84 const int categoryCount = itemsof(g_debugcats);
88 #ifdef __DEBUG__
89 bool CLogger::IsEnabled( DebugType type ) const
91 int index = (int)type;
93 if ( index >= 0 && index < categoryCount ) {
94 const CDebugCategory& cat = g_debugcats[ index ];
95 wxASSERT( type == cat.GetType() );
97 return ( cat.IsEnabled() && thePrefs::GetVerbose() );
100 wxFAIL;
101 return false;
103 #endif
106 void CLogger::SetEnabled( DebugType type, bool enabled )
108 int index = (int)type;
110 if ( index >= 0 && index < categoryCount ) {
111 CDebugCategory& cat = g_debugcats[ index ];
112 wxASSERT( type == cat.GetType() );
114 cat.SetEnabled( enabled );
115 } else {
116 wxFAIL;
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)
130 wxString msg(str);
131 // handle Debug messages
132 if (type != logStandard) {
133 if (!critical && !IsEnabled(type)) {
134 return;
136 if (!critical && thePrefs::GetVerboseLogfile()) {
137 // print non critical debug messages only to the logfile
138 toGUI = false;
140 int index = (int)type;
142 if ( index >= 0 && index < categoryCount ) {
143 const CDebugCategory& cat = g_debugcats[ index ];
144 wxASSERT(type == cat.GetType());
146 msg = cat.GetName() + wxT(": ") + msg;
147 } else {
148 wxFAIL;
152 #ifdef __DEBUG__
153 if (line) {
154 msg = file.AfterLast(wxFileName::GetPathSeparator()).AfterLast(wxT('/')) << wxT("(") << line << wxT("): ") + msg;
156 #endif
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);
169 void CLogger::AddLogLine(
170 const wxString &file,
171 int line,
172 bool critical,
173 DebugType type,
174 const std::ostringstream &msg)
176 int index = (int)type;
178 if ( index >= 0 && index < categoryCount ) {
179 const CDebugCategory& cat = g_debugcats[ index ];
180 wxASSERT(type == cat.GetType());
182 AddLogLine(file, line, critical, logStandard,
183 cat.GetName() + wxT(": ") + wxString(char2unicode(msg.str().c_str())));
188 const CDebugCategory& CLogger::GetDebugCategory( int index )
190 wxASSERT( index >= 0 && index < categoryCount );
192 return g_debugcats[ index ];
196 unsigned int CLogger::GetDebugCategoryCount()
198 return categoryCount;
202 bool CLogger::OpenLogfile(const wxString & name)
204 applog = new wxFFileOutputStream(name);
205 bool ret = applog->Ok();
206 if (ret) {
207 FlushApplog();
208 m_LogfileName = name;
209 } else {
210 CloseLogfile();
212 return ret;
216 void CLogger::CloseLogfile()
218 delete applog;
219 applog = NULL;
220 m_LogfileName.Clear();
224 void CLogger::OnLoggingEvent(class CLoggingEvent& evt)
226 DoLines(evt.Message(), evt.IsCritical(), evt.ToStdout(), evt.ToGUI());
230 void CLogger::DoLines(const wxString & lines, bool critical, bool toStdout, bool toGUI)
232 // Remove newspace at end
233 wxString bufferline = lines.Strip(wxString::trailing);
235 // Create the timestamp
236 wxString stamp = wxDateTime::Now().FormatISODate() + wxT(" ") + wxDateTime::Now().FormatISOTime()
237 #ifdef CLIENT_GUI
238 + wxT(" (remote-GUI): ");
239 #else
240 + wxT(": ");
241 #endif
243 // critical lines get a ! prepended, ordinary lines a blank
244 // logfile-only lines get a . to prevent transmission on EC
245 wxString prefix = !toGUI ? wxT(".") : (critical ? wxT("!") : wxT(" "));
247 if ( bufferline.IsEmpty() ) {
248 // If it's empty we just write a blank line with no timestamp.
249 DoLine(wxT(" \n"), toStdout, toGUI);
250 } else {
251 // Split multi-line messages into individual lines
252 wxStringTokenizer tokens( bufferline, wxT("\n") );
253 while ( tokens.HasMoreTokens() ) {
254 wxString fullline = prefix + stamp + tokens.GetNextToken() + wxT("\n");
255 DoLine(fullline, toStdout, toGUI);
261 void CLogger::DoLine(const wxString & line, bool toStdout, bool GUI_ONLY(toGUI))
264 wxMutexLocker lock(m_lineLock);
265 ++m_count;
267 // write to logfile
268 m_ApplogBuf += line;
269 FlushApplog();
271 // write to Stdout
272 if (m_StdoutLog || toStdout) {
273 printf("%s", (const char*)unicode2char(line));
276 #ifndef AMULE_DAEMON
277 // write to Listcontrol
278 if (toGUI) {
279 theApp->AddGuiLogLine(line);
281 #endif
285 void CLogger::EmergencyLog(const wxString &message, bool closeLog)
287 fprintf(stderr, "%s", (const char*)unicode2char(message));
288 m_ApplogBuf += message;
289 FlushApplog();
290 if (closeLog && applog) {
291 applog->Close();
292 applog = NULL;
297 void CLogger::FlushApplog()
299 if (applog) { // Wait with output until logfile is actually opened
300 wxStringInputStream stream(m_ApplogBuf);
301 (*applog) << stream;
302 applog->Sync();
303 m_ApplogBuf.Clear();
307 CLogger theLogger;
309 BEGIN_EVENT_TABLE(CLogger, wxEvtHandler)
310 EVT_MULE_LOGGING(CLogger::OnLoggingEvent)
311 END_EVENT_TABLE()
314 CLoggerTarget::CLoggerTarget()
318 #if wxCHECK_VERSION(2, 9, 0)
319 void CLoggerTarget::DoLogText(const wxString &msg)
321 // prevent infinite recursion
322 static bool recursion = false;
323 if (recursion) {
324 return;
326 recursion = true;
328 // This is much simpler than manually handling all wx log-types.
329 if (msg.StartsWith(_("ERROR: ")) || msg.StartsWith(_("WARNING: "))) {
330 AddLogLineC(msg);
331 } else {
332 AddLogLineN(msg);
335 recursion = false;
337 #else
338 void CLoggerTarget::DoLogString(const wxChar* msg, time_t)
340 // prevent infinite recursion
341 static bool recursion = false;
342 if (recursion) {
343 return;
345 recursion = true;
347 wxCHECK_RET(msg, wxT("Log message is NULL in DoLogString!"));
349 wxString str(msg);
351 // This is much simpler than manually handling all wx log-types.
352 if (str.StartsWith(_("ERROR: ")) || str.StartsWith(_("WARNING: "))) {
353 AddLogLineC(str);
354 } else {
355 AddLogLineN(str);
358 recursion = false;
360 #endif
362 CLoggerAccess::CLoggerAccess()
364 m_bufferlen = 4096;
365 m_buffer = new wxCharBuffer(m_bufferlen);
366 m_logfile = NULL;
367 Reset();
371 void CLoggerAccess::Reset()
373 delete m_logfile;
374 m_logfile = new wxFFileInputStream(theLogger.GetLogfileName());
375 m_pos = 0;
376 m_ready = false;
380 CLoggerAccess::~CLoggerAccess()
382 delete m_buffer;
383 delete m_logfile;
388 // read a line of text from the logfile if available
389 // (can't believe there's no library function for this >:( )
391 bool CLoggerAccess::HasString()
393 while (!m_ready) {
394 int c = m_logfile->GetC();
395 if (c == wxEOF) {
396 break;
398 // check for buffer overrun
399 if (m_pos == m_bufferlen) {
400 m_bufferlen += 1024;
401 m_buffer->extend(m_bufferlen);
403 m_buffer->data()[m_pos++] = c;
404 if (c == '\n') {
405 if (m_buffer->data()[0] == '.') {
406 // Log-only line, skip
407 m_pos = 0;
408 } else {
409 m_ready = true;
413 return m_ready;
417 bool CLoggerAccess::GetString(wxString & s)
419 if (!HasString()) {
420 return false;
422 s = wxString(m_buffer->data(), wxConvUTF8, m_pos);
423 m_pos = 0;
424 m_ready = false;
425 return true;
428 // Functions for EC logging
429 bool ECLogIsEnabled()
431 return theLogger.IsEnabled(logEC);
434 void DoECLogLine(const wxString &line)
436 // without file/line
437 theLogger.AddLogLine(wxEmptyString, 0, false, logStandard, line, false, false);
440 // File_checked_for_headers