2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
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"
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
44 static char levelNames[][8] =
45 {"DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "SEVERE", "FATAL", "NONE"};
56 CSingleLock waitLock(critSec);
65 void CLog::Log(int loglevel, const char *format, ... )
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))
79 if (extras != 0 && (m_extraLogLevels & extras) == 0)
85 CStdString strPrefix, strData;
87 strData.reserve(16384);
90 strData = StringUtils::FormatV(format,va);
93 if (m_repeatLogLevel == loglevel && m_repeatLine == strData)
98 else if (m_repeatCount)
100 strPrefix = StringUtils::Format(prefixFormat,
104 (uint64_t)CThread::GetCurrentThreadId(),
105 levelNames[m_repeatLogLevel]);
107 CStdString strData2 = StringUtils::Format("Previous line repeats %d times."
110 fputs(strPrefix.c_str(), m_file);
111 fputs(strData2.c_str(), m_file);
112 OutputDebugString(strData2);
116 m_repeatLine = strData;
117 m_repeatLogLevel = loglevel;
119 StringUtils::TrimRight(strData);
123 OutputDebugString(strData);
125 /* fixup newline alignment, number of spaces should equal prefix length */
126 strData.Replace("\n", LINE_ENDING" ");
127 strData += LINE_ENDING;
129 strPrefix = StringUtils::Format(prefixFormat,
133 (uint64_t)CThread::GetCurrentThreadId(),
134 levelNames[loglevel]);
137 #if defined(TARGET_ANDROID) && defined(_DEBUG)
138 CXBMCApp::android_printf("%s%s",strPrefix.c_str(), strData.c_str());
141 fputs(strPrefix.c_str(), m_file);
142 fputs(strData.c_str(), m_file);
147 bool CLog::Init(const char* path)
149 CSingleLock waitLock(critSec);
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);
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);
165 if (stat64_utf8(strLogFileOld.c_str(),&info) == 0 &&
166 remove_utf8(strLogFileOld.c_str()) != 0)
168 if (stat64_utf8(strLogFile.c_str(),&info) == 0 &&
169 rename_utf8(strLogFile.c_str(),strLogFileOld.c_str()) != 0)
172 m_file = fopen64_utf8(strLogFile.c_str(),"wb");
177 unsigned char BOM[3] = {0xEF, 0xBB, 0xBF};
178 fwrite(BOM, sizeof(BOM), 1, m_file);
181 return m_file != NULL;
184 void CLog::MemDump(char *pData, int length)
186 Log(LOGDEBUG, "MEM_DUMP: Dumping from %p", pData);
187 for (int i = 0; i < length; i+=16)
189 CStdString strLine = StringUtils::Format("MEM_DUMP: %04x ", i);
191 for (int k=0; k < 4 && i + 4*k < length; k++)
193 for (int j=0; j < 4 && i + 4*k + j < length; j++)
195 CStdString strFormat = StringUtils::Format(" %02x", (unsigned char)*pData++);
196 strLine += strFormat;
201 while (strLine.size() < 13*4 + 16)
203 for (int j=0; j < 16 && i + j < length; j++)
211 Log(LOGDEBUG, "%s", strLine.c_str());
215 void CLog::SetLogLevel(int level)
217 CSingleLock waitLock(critSec);
219 CLog::Log(LOGNOTICE, "Log level changed to %d", m_logLevel);
222 int CLog::GetLogLevel()
227 void CLog::SetExtraLogLevels(int level)
229 CSingleLock waitLock(critSec);
230 m_extraLogLevels = level;
233 void CLog::OutputDebugString(const std::string& line)
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 )
243 ::OutputDebugStringW(wstr.c_str());
246 #endif // TARGET_WINDOWS
247 ::OutputDebugString(line.c_str());
248 ::OutputDebugString("\n");