3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "threading/mutex_auto_lock.h"
28 #include "exceptions.h"
29 #include "util/numeric.h"
38 const int BUFFER_LENGTH
= 256;
40 class StringBuffer
: public std::streambuf
{
47 virtual void flush(const std::string
&buf
) = 0;
48 std::streamsize
xsputn(const char *s
, std::streamsize n
);
49 void push_back(char c
);
52 char buffer
[BUFFER_LENGTH
];
57 class LogBuffer
: public StringBuffer
{
59 LogBuffer(Logger
&logger
, LogLevel lev
) :
64 void flush(const std::string
&buffer
);
72 class RawLogBuffer
: public StringBuffer
{
74 void flush(const std::string
&buffer
);
83 StreamLogOutput
stdout_output(std::cout
);
84 StreamLogOutput
stderr_output(std::cerr
);
85 std::ostream
null_stream(NULL
);
89 LogBuffer
none_buf(g_logger
, LL_NONE
);
90 LogBuffer
error_buf(g_logger
, LL_ERROR
);
91 LogBuffer
warning_buf(g_logger
, LL_WARNING
);
92 LogBuffer
action_buf(g_logger
, LL_ACTION
);
93 LogBuffer
info_buf(g_logger
, LL_INFO
);
94 LogBuffer
verbose_buf(g_logger
, LL_VERBOSE
);
97 std::ostream
*dout_con_ptr
= &null_stream
;
98 std::ostream
*derr_con_ptr
= &verbosestream
;
101 std::ostream
*dout_server_ptr
= &infostream
;
102 std::ostream
*derr_server_ptr
= &errorstream
;
106 std::ostream
*dout_client_ptr
= &infostream
;
107 std::ostream
*derr_client_ptr
= &errorstream
;
110 std::ostream
rawstream(&raw_buf
);
111 std::ostream
dstream(&none_buf
);
112 std::ostream
errorstream(&error_buf
);
113 std::ostream
warningstream(&warning_buf
);
114 std::ostream
actionstream(&action_buf
);
115 std::ostream
infostream(&info_buf
);
116 std::ostream
verbosestream(&verbose_buf
);
121 static unsigned int g_level_to_android
[] = {
122 ANDROID_LOG_INFO
, // LL_NONE
124 ANDROID_LOG_ERROR
, // LL_ERROR
125 ANDROID_LOG_WARN
, // LL_WARNING
126 ANDROID_LOG_WARN
, // LL_ACTION
128 ANDROID_LOG_DEBUG
, // LL_INFO
129 ANDROID_LOG_VERBOSE
, // LL_VERBOSE
132 class AndroidSystemLogOutput
: public ICombinedLogOutput
{
134 AndroidSystemLogOutput()
136 g_logger
.addOutput(this);
138 ~AndroidSystemLogOutput()
140 g_logger
.removeOutput(this);
142 void logRaw(LogLevel lev
, const std::string
&line
)
144 STATIC_ASSERT(ARRLEN(g_level_to_android
) == LL_MAX
,
145 mismatch_between_android_and_internal_loglevels
);
146 __android_log_print(g_level_to_android
[lev
],
147 PROJECT_NAME_C
, "%s", line
.c_str());
151 AndroidSystemLogOutput g_android_log_output
;
155 ///////////////////////////////////////////////////////////////////////////////
162 LogLevel
Logger::stringToLevel(const std::string
&name
)
166 else if (name
== "error")
168 else if (name
== "warning")
170 else if (name
== "action")
172 else if (name
== "info")
174 else if (name
== "verbose")
180 void Logger::addOutput(ILogOutput
*out
)
182 addOutputMaxLevel(out
, (LogLevel
)(LL_MAX
- 1));
185 void Logger::addOutput(ILogOutput
*out
, LogLevel lev
)
187 m_outputs
[lev
].push_back(out
);
190 void Logger::addOutputMasked(ILogOutput
*out
, LogLevelMask mask
)
192 for (size_t i
= 0; i
< LL_MAX
; i
++) {
193 if (mask
& LOGLEVEL_TO_MASKLEVEL(i
))
194 m_outputs
[i
].push_back(out
);
198 void Logger::addOutputMaxLevel(ILogOutput
*out
, LogLevel lev
)
200 assert(lev
< LL_MAX
);
201 for (size_t i
= 0; i
<= lev
; i
++)
202 m_outputs
[i
].push_back(out
);
205 LogLevelMask
Logger::removeOutput(ILogOutput
*out
)
207 LogLevelMask ret_mask
= 0;
208 for (size_t i
= 0; i
< LL_MAX
; i
++) {
209 std::vector
<ILogOutput
*>::iterator it
;
211 it
= std::find(m_outputs
[i
].begin(), m_outputs
[i
].end(), out
);
212 if (it
!= m_outputs
[i
].end()) {
213 ret_mask
|= LOGLEVEL_TO_MASKLEVEL(i
);
214 m_outputs
[i
].erase(it
);
220 void Logger::setLevelSilenced(LogLevel lev
, bool silenced
)
222 m_silenced_levels
[lev
] = silenced
;
225 void Logger::registerThread(const std::string
&name
)
227 std::thread::id id
= std::this_thread::get_id();
228 MutexAutoLock
lock(m_mutex
);
229 m_thread_names
[id
] = name
;
232 void Logger::deregisterThread()
234 std::thread::id id
= std::this_thread::get_id();
235 MutexAutoLock
lock(m_mutex
);
236 m_thread_names
.erase(id
);
239 const std::string
Logger::getLevelLabel(LogLevel lev
)
241 static const std::string names
[] = {
249 assert(lev
< LL_MAX
&& lev
>= 0);
250 STATIC_ASSERT(ARRLEN(names
) == LL_MAX
,
251 mismatch_between_loglevel_names_and_enum
);
255 LogColor
Logger::color_mode
= LOG_COLOR_AUTO
;
257 const std::string
Logger::getThreadName()
259 std::map
<std::thread::id
, std::string
>::const_iterator it
;
261 std::thread::id id
= std::this_thread::get_id();
262 it
= m_thread_names
.find(id
);
263 if (it
!= m_thread_names
.end())
266 std::ostringstream os
;
267 os
<< "#0x" << std::hex
<< id
;
271 void Logger::log(LogLevel lev
, const std::string
&text
)
273 if (m_silenced_levels
[lev
])
276 const std::string thread_name
= getThreadName();
277 const std::string label
= getLevelLabel(lev
);
278 const std::string timestamp
= getTimestamp();
279 std::ostringstream
os(std::ios_base::binary
);
280 os
<< timestamp
<< ": " << label
<< "[" << thread_name
<< "]: " << text
;
282 logToOutputs(lev
, os
.str(), timestamp
, thread_name
, text
);
285 void Logger::logRaw(LogLevel lev
, const std::string
&text
)
287 if (m_silenced_levels
[lev
])
290 logToOutputsRaw(lev
, text
);
293 void Logger::logToOutputsRaw(LogLevel lev
, const std::string
&line
)
295 MutexAutoLock
lock(m_mutex
);
296 for (size_t i
= 0; i
!= m_outputs
[lev
].size(); i
++)
297 m_outputs
[lev
][i
]->logRaw(lev
, line
);
300 void Logger::logToOutputs(LogLevel lev
, const std::string
&combined
,
301 const std::string
&time
, const std::string
&thread_name
,
302 const std::string
&payload_text
)
304 MutexAutoLock
lock(m_mutex
);
305 for (size_t i
= 0; i
!= m_outputs
[lev
].size(); i
++)
306 m_outputs
[lev
][i
]->log(lev
, combined
, time
, thread_name
, payload_text
);
311 //// *LogOutput methods
314 void FileLogOutput::setFile(const std::string
&filename
, s64 file_size_max
)
316 // Only move debug.txt if there is a valid maximum file size
317 bool is_too_large
= false;
318 if (file_size_max
> 0) {
319 std::ifstream
ifile(filename
, std::ios::binary
| std::ios::ate
);
320 is_too_large
= ifile
.tellg() > file_size_max
;
325 std::string filename_secondary
= filename
+ ".1";
326 actionstream
<< "The log file grew too big; it is moved to " <<
327 filename_secondary
<< std::endl
;
328 remove(filename_secondary
.c_str());
329 rename(filename
.c_str(), filename_secondary
.c_str());
331 m_stream
.open(filename
, std::ios::app
| std::ios::ate
);
333 if (!m_stream
.good())
334 throw FileNotGoodException("Failed to open log file " +
335 filename
+ ": " + strerror(errno
));
337 "-------------" << std::endl
<<
338 " Separator" << std::endl
<<
339 "-------------\n" << std::endl
;
342 void StreamLogOutput::logRaw(LogLevel lev
, const std::string
&line
)
344 bool colored_message
= (Logger::color_mode
== LOG_COLOR_ALWAYS
) ||
345 (Logger::color_mode
== LOG_COLOR_AUTO
&& is_tty
);
346 if (colored_message
) {
350 m_stream
<< "\033[91m";
354 m_stream
<< "\033[93m";
357 // info is a bit dark
358 m_stream
<< "\033[37m";
361 // verbose is darker than info
362 m_stream
<< "\033[2m";
366 colored_message
= false;
370 m_stream
<< line
<< std::endl
;
372 if (colored_message
) {
373 // reset to white color
374 m_stream
<< "\033[0m";
378 void LogOutputBuffer::updateLogLevel()
380 const std::string
&conf_loglev
= g_settings
->get("chat_log_level");
381 LogLevel log_level
= Logger::stringToLevel(conf_loglev
);
382 if (log_level
== LL_MAX
) {
383 warningstream
<< "Supplied unrecognized chat_log_level; "
384 "showing none." << std::endl
;
388 m_logger
.removeOutput(this);
389 m_logger
.addOutputMaxLevel(this, log_level
);
392 void LogOutputBuffer::logRaw(LogLevel lev
, const std::string
&line
)
396 if (!g_settings
->getBool("disable_escape_sequences")) {
398 case LL_ERROR
: // red
399 color
= "\x1b(c@#F00)";
401 case LL_WARNING
: // yellow
402 color
= "\x1b(c@#EE0)";
404 case LL_INFO
: // grey
405 color
= "\x1b(c@#BBB)";
407 case LL_VERBOSE
: // dark grey
408 color
= "\x1b(c@#888)";
414 m_buffer
.push(color
.append(line
));
421 int StringBuffer::overflow(int c
)
428 std::streamsize
StringBuffer::xsputn(const char *s
, std::streamsize n
)
430 for (int i
= 0; i
< n
; ++i
)
435 void StringBuffer::push_back(char c
)
437 if (c
== '\n' || c
== '\r') {
439 flush(std::string(buffer
, buffer_index
));
442 buffer
[buffer_index
++] = c
;
443 if (buffer_index
>= BUFFER_LENGTH
) {
444 flush(std::string(buffer
, buffer_index
));
451 void LogBuffer::flush(const std::string
&buffer
)
453 logger
.log(level
, buffer
);
456 void RawLogBuffer::flush(const std::string
&buffer
)
458 g_logger
.logRaw(LL_NONE
, buffer
);