[cstdstring] removal of Trim/TrimLeft/TrimRight
[vuplus_xbmc] / xbmc / utils / log.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22 #include "log.h"
23 #include "stdio_utf8.h"
24 #include "stat_utf8.h"
25 #include "threads/CriticalSection.h"
26 #include "threads/SingleLock.h"
27 #include "threads/Thread.h"
28 #include "utils/StdString.h"
29 #include "utils/StringUtils.h"
30 #if defined(TARGET_ANDROID)
31 #include "android/activity/XBMCApp.h"
32 #elif defined(TARGET_WINDOWS)
33 #include "win32/WIN32Util.h"
34 #endif
35
36 #define critSec XBMC_GLOBAL_USE(CLog::CLogGlobals).critSec
37 #define m_file XBMC_GLOBAL_USE(CLog::CLogGlobals).m_file
38 #define m_repeatCount XBMC_GLOBAL_USE(CLog::CLogGlobals).m_repeatCount
39 #define m_repeatLogLevel XBMC_GLOBAL_USE(CLog::CLogGlobals).m_repeatLogLevel
40 #define m_repeatLine XBMC_GLOBAL_USE(CLog::CLogGlobals).m_repeatLine
41 #define m_logLevel XBMC_GLOBAL_USE(CLog::CLogGlobals).m_logLevel
42 #define m_extraLogLevels XBMC_GLOBAL_USE(CLog::CLogGlobals).m_extraLogLevels
43
44 static char levelNames[][8] =
45 {"DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "SEVERE", "FATAL", "NONE"};
46
47 CLog::CLog()
48 {}
49
50 CLog::~CLog()
51 {}
52
53 void CLog::Close()
54 {
55   
56   CSingleLock waitLock(critSec);
57   if (m_file)
58   {
59     fclose(m_file);
60     m_file = NULL;
61   }
62   m_repeatLine.clear();
63 }
64
65 void CLog::Log(int loglevel, const char *format, ... )
66 {
67   static const char* prefixFormat = "%02.2d:%02.2d:%02.2d T:%"PRIu64" %7s: ";
68   CSingleLock waitLock(critSec);
69   int extras = (loglevel >> LOGMASKBIT) << LOGMASKBIT;
70   loglevel = loglevel & LOGMASK;
71 #if !(defined(_DEBUG) || defined(PROFILE))
72   if (m_logLevel > LOG_LEVEL_NORMAL ||
73      (m_logLevel > LOG_LEVEL_NONE && loglevel >= LOGNOTICE))
74 #endif
75   {
76     if (!m_file)
77       return;
78
79     if (extras != 0 && (m_extraLogLevels & extras) == 0)
80       return;
81
82     SYSTEMTIME time;
83     GetLocalTime(&time);
84
85     CStdString strPrefix, strData;
86
87     strData.reserve(16384);
88     va_list va;
89     va_start(va, format);
90     strData = StringUtils::FormatV(format,va);
91     va_end(va);
92
93     if (m_repeatLogLevel == loglevel && m_repeatLine == strData)
94     {
95       m_repeatCount++;
96       return;
97     }
98     else if (m_repeatCount)
99     {
100       strPrefix = StringUtils::Format(prefixFormat,
101                                       time.wHour,
102                                       time.wMinute,
103                                       time.wSecond,
104                                       (uint64_t)CThread::GetCurrentThreadId(),
105                                       levelNames[m_repeatLogLevel]);
106
107       CStdString strData2 = StringUtils::Format("Previous line repeats %d times."
108                                                 LINE_ENDING,
109                                                 m_repeatCount);
110       fputs(strPrefix.c_str(), m_file);
111       fputs(strData2.c_str(), m_file);
112       OutputDebugString(strData2);
113       m_repeatCount = 0;
114     }
115     
116     m_repeatLine      = strData;
117     m_repeatLogLevel  = loglevel;
118
119     StringUtils::TrimRight(strData);
120     if (strData.empty())
121       return;
122     
123     OutputDebugString(strData);
124
125     /* fixup newline alignment, number of spaces should equal prefix length */
126     strData.Replace("\n", LINE_ENDING"                                            ");
127     strData += LINE_ENDING;
128
129     strPrefix = StringUtils::Format(prefixFormat,
130                                     time.wHour,
131                                     time.wMinute,
132                                     time.wSecond,
133                                     (uint64_t)CThread::GetCurrentThreadId(),
134                                     levelNames[loglevel]);
135
136 //print to adb
137 #if defined(TARGET_ANDROID) && defined(_DEBUG)
138   CXBMCApp::android_printf("%s%s",strPrefix.c_str(), strData.c_str());
139 #endif
140
141     fputs(strPrefix.c_str(), m_file);
142     fputs(strData.c_str(), m_file);
143     fflush(m_file);
144   }
145 }
146
147 bool CLog::Init(const char* path)
148 {
149   CSingleLock waitLock(critSec);
150   if (!m_file)
151   {
152     // the log folder location is initialized in the CAdvancedSettings
153     // constructor and changed in CApplication::Create()
154     CStdString strLogFile = StringUtils::Format("%sxbmc.log", path);
155     CStdString strLogFileOld = StringUtils::Format("%sxbmc.old.log", path);
156
157 #if defined(TARGET_WINDOWS)
158     // the appdata folder might be redirected to an unc share
159     // convert smb to unc path that stat and fopen can handle it
160     strLogFile = CWIN32Util::SmbToUnc(strLogFile);
161     strLogFileOld = CWIN32Util::SmbToUnc(strLogFileOld);
162 #endif
163
164     struct stat64 info;
165     if (stat64_utf8(strLogFileOld.c_str(),&info) == 0 &&
166         remove_utf8(strLogFileOld.c_str()) != 0)
167       return false;
168     if (stat64_utf8(strLogFile.c_str(),&info) == 0 &&
169         rename_utf8(strLogFile.c_str(),strLogFileOld.c_str()) != 0)
170       return false;
171
172     m_file = fopen64_utf8(strLogFile.c_str(),"wb");
173   }
174
175   if (m_file)
176   {
177     unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
178     fwrite(BOM, sizeof(BOM), 1, m_file);
179   }
180
181   return m_file != NULL;
182 }
183
184 void CLog::MemDump(char *pData, int length)
185 {
186   Log(LOGDEBUG, "MEM_DUMP: Dumping from %p", pData);
187   for (int i = 0; i < length; i+=16)
188   {
189     CStdString strLine = StringUtils::Format("MEM_DUMP: %04x ", i);
190     char *alpha = pData;
191     for (int k=0; k < 4 && i + 4*k < length; k++)
192     {
193       for (int j=0; j < 4 && i + 4*k + j < length; j++)
194       {
195         CStdString strFormat = StringUtils::Format(" %02x", (unsigned char)*pData++);
196         strLine += strFormat;
197       }
198       strLine += " ";
199     }
200     // pad with spaces
201     while (strLine.size() < 13*4 + 16)
202       strLine += " ";
203     for (int j=0; j < 16 && i + j < length; j++)
204     {
205       if (*alpha > 31)
206         strLine += *alpha;
207       else
208         strLine += '.';
209       alpha++;
210     }
211     Log(LOGDEBUG, "%s", strLine.c_str());
212   }
213 }
214
215 void CLog::SetLogLevel(int level)
216 {
217   CSingleLock waitLock(critSec);
218   m_logLevel = level;
219   CLog::Log(LOGNOTICE, "Log level changed to %d", m_logLevel);
220 }
221
222 int CLog::GetLogLevel()
223 {
224   return m_logLevel;
225 }
226
227 void CLog::SetExtraLogLevels(int level)
228 {
229   CSingleLock waitLock(critSec);
230   m_extraLogLevels = level;
231 }
232
233 void CLog::OutputDebugString(const std::string& line)
234 {
235 #if defined(_DEBUG) || defined(PROFILE)
236 #if defined(TARGET_WINDOWS)
237   // we can't use charsetconverter here as it's initialized later than CLog and deinitialized early
238   int bufSize = MultiByteToWideChar(CP_UTF8, 0, line.c_str(), -1, NULL, 0);
239   CStdStringW wstr (L"", bufSize);
240   if ( MultiByteToWideChar(CP_UTF8, 0, line.c_str(), -1, wstr.GetBuf(bufSize), bufSize) == bufSize )
241   {
242     wstr.RelBuf();
243     ::OutputDebugStringW(wstr.c_str());
244   }
245   else
246 #endif // TARGET_WINDOWS
247     ::OutputDebugString(line.c_str());
248   ::OutputDebugString("\n");
249 #endif
250 }