blob: 522468c06638e3253e89da46904c7a70e07124ec [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#if defined(WEBRTC_WIN)
Tommi23edcff2015-05-25 10:45:43 +020012#if !defined(WIN32_LEAN_AND_MEAN)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000013#define WIN32_LEAN_AND_MEAN
Tommi23edcff2015-05-25 10:45:43 +020014#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000015#include <windows.h>
conceptgenesis3f705622016-01-30 14:40:44 -080016#if _MSC_VER < 1900
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017#define snprintf _snprintf
conceptgenesis3f705622016-01-30 14:40:44 -080018#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000019#undef ERROR // wingdi.h
20#endif
21
22#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
23#include <CoreServices/CoreServices.h>
24#elif defined(WEBRTC_ANDROID)
25#include <android/log.h>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000026// Android has a 1024 limit on log inputs. We use 60 chars as an
27// approx for the header/tag portion.
28// See android/system/core/liblog/logd_write.c
29static const int kMaxLogLineSize = 1024 - 60;
30#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) || WEBRTC_ANDROID
31
Philip Eliasson278aa422018-02-26 14:54:45 +000032static const char kLibjingle[] = "libjingle";
33
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000034#include <time.h>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000035#include <limits.h>
andresp@webrtc.orgff689be2015-02-12 11:54:26 +000036
37#include <algorithm>
38#include <iomanip>
39#include <ostream>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000040#include <vector>
41
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020042#include "rtc_base/criticalsection.h"
43#include "rtc_base/logging.h"
Philip Eliasson278aa422018-02-26 14:54:45 +000044#include "rtc_base/platform_thread.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020045#include "rtc_base/stringencode.h"
46#include "rtc_base/stringutils.h"
47#include "rtc_base/timeutils.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000048
49namespace rtc {
andrew88703d72015-09-07 00:34:56 -070050namespace {
Jonas Olsson2b6f1352018-02-15 11:57:03 +010051// By default, release builds don't log, debug builds at info level
52#if !defined(NDEBUG)
53static LoggingSeverity g_min_sev = LS_INFO;
54static LoggingSeverity g_dbg_sev = LS_INFO;
55#else
56static LoggingSeverity g_min_sev = LS_NONE;
57static LoggingSeverity g_dbg_sev = LS_NONE;
58#endif
andrew88703d72015-09-07 00:34:56 -070059
60// Return the filename portion of the string (that following the last slash).
61const char* FilenameFromPath(const char* file) {
62 const char* end1 = ::strrchr(file, '/');
63 const char* end2 = ::strrchr(file, '\\');
64 if (!end1 && !end2)
65 return file;
66 else
67 return (end1 > end2) ? end1 + 1 : end2 + 1;
68}
69
Jonas Olsson2b6f1352018-02-15 11:57:03 +010070// Global lock for log subsystem, only needed to serialize access to streams_.
71CriticalSection g_log_crit;
andrew88703d72015-09-07 00:34:56 -070072} // namespace
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000073
Philip Eliasson278aa422018-02-26 14:54:45 +000074/////////////////////////////////////////////////////////////////////////////
75// Constant Labels
76/////////////////////////////////////////////////////////////////////////////
77
78const char* FindLabel(int value, const ConstantLabel entries[]) {
79 for (int i = 0; entries[i].label; ++i) {
80 if (value == entries[i].value) {
81 return entries[i].label;
82 }
83 }
84 return 0;
85}
86
87std::string ErrorName(int err, const ConstantLabel* err_table) {
88 if (err == 0)
89 return "No error";
90
91 if (err_table != 0) {
92 if (const char* value = FindLabel(err, err_table))
93 return value;
94 }
95
96 char buffer[16];
97 snprintf(buffer, sizeof(buffer), "0x%08x", err);
98 return buffer;
99}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000100
101/////////////////////////////////////////////////////////////////////////////
102// LogMessage
103/////////////////////////////////////////////////////////////////////////////
104
andrew88703d72015-09-07 00:34:56 -0700105bool LogMessage::log_to_stderr_ = true;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000106
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000107// The list of logging streams currently configured.
108// Note: we explicitly do not clean this up, because of the uncertain ordering
109// of destructors at program exit. Let the person who sets the stream trigger
deadbeef37f5ecf2017-02-27 14:06:41 -0800110// cleanup by setting to null, or let it leak (safe at program exit).
danilchap3c6abd22017-09-06 05:46:29 -0700111LogMessage::StreamList LogMessage::streams_ RTC_GUARDED_BY(g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000112
113// Boolean options default to false (0)
114bool LogMessage::thread_, LogMessage::timestamp_;
115
Peter Boström225789d2015-10-23 15:20:56 +0200116LogMessage::LogMessage(const char* file,
117 int line,
118 LoggingSeverity sev,
119 LogErrorContext err_ctx,
Philip Eliasson278aa422018-02-26 14:54:45 +0000120 int err,
121 const char* module)
122 : severity_(sev), tag_(kLibjingle) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000123 if (timestamp_) {
Taylor Brandstetter4f0dfbd2016-06-15 17:15:23 -0700124 // Use SystemTimeMillis so that even if tests use fake clocks, the timestamp
125 // in log messages represents the real system time.
126 int64_t time = TimeDiff(SystemTimeMillis(), LogStartTime());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000127 // Also ensure WallClockStartTime is initialized, so that it matches
128 // LogStartTime.
129 WallClockStartTime();
130 print_stream_ << "[" << std::setfill('0') << std::setw(3) << (time / 1000)
131 << ":" << std::setw(3) << (time % 1000) << std::setfill(' ')
132 << "] ";
133 }
134
135 if (thread_) {
henrikaba35d052015-07-14 17:04:08 +0200136 PlatformThreadId id = CurrentThreadId();
137 print_stream_ << "[" << std::dec << id << "] ";
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000138 }
139
deadbeef37f5ecf2017-02-27 14:06:41 -0800140 if (file != nullptr)
Alex Glaznevebed24d2015-09-15 11:05:24 -0700141 print_stream_ << "(" << FilenameFromPath(file) << ":" << line << "): ";
andrew88703d72015-09-07 00:34:56 -0700142
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000143 if (err_ctx != ERRCTX_NONE) {
144 std::ostringstream tmp;
145 tmp << "[0x" << std::setfill('0') << std::hex << std::setw(8) << err << "]";
146 switch (err_ctx) {
147 case ERRCTX_ERRNO:
148 tmp << " " << strerror(err);
149 break;
kwiberg77eab702016-09-28 17:42:01 -0700150#ifdef WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000151 case ERRCTX_HRESULT: {
152 char msgbuf[256];
Philip Eliasson278aa422018-02-26 14:54:45 +0000153 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM;
154 HMODULE hmod = GetModuleHandleA(module);
155 if (hmod)
156 flags |= FORMAT_MESSAGE_FROM_HMODULE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000157 if (DWORD len = FormatMessageA(
Philip Eliasson278aa422018-02-26 14:54:45 +0000158 flags, hmod, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
deadbeef37f5ecf2017-02-27 14:06:41 -0800159 msgbuf, sizeof(msgbuf) / sizeof(msgbuf[0]), nullptr)) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000160 while ((len > 0) &&
161 isspace(static_cast<unsigned char>(msgbuf[len-1]))) {
162 msgbuf[--len] = 0;
163 }
164 tmp << " " << msgbuf;
165 }
166 break;
167 }
Tommi0eefb4d2015-05-23 09:54:07 +0200168#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000169#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
170 case ERRCTX_OSSTATUS: {
Tommi09ca02e2016-04-24 17:32:48 +0200171 std::string desc(DescriptionFromOSStatus(err));
172 tmp << " " << (desc.empty() ? "Unknown error" : desc.c_str());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000173 break;
174 }
175#endif // WEBRTC_MAC && !defined(WEBRTC_IOS)
176 default:
177 break;
178 }
179 extra_ = tmp.str();
180 }
181}
182
jiayl66f0da22015-09-14 15:06:39 -0700183LogMessage::LogMessage(const char* file,
184 int line,
185 LoggingSeverity sev,
Philip Eliasson278aa422018-02-26 14:54:45 +0000186 const std::string& tag)
deadbeef37f5ecf2017-02-27 14:06:41 -0800187 : LogMessage(file,
188 line,
189 sev,
190 ERRCTX_NONE,
Philip Eliasson278aa422018-02-26 14:54:45 +0000191 0 /* err */,
192 nullptr /* module */) {
jiayl66f0da22015-09-14 15:06:39 -0700193 tag_ = tag;
Jiayang Liue4ba6ce92015-09-21 15:49:24 -0700194 print_stream_ << tag << ": ";
jiayl66f0da22015-09-14 15:06:39 -0700195}
196
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000197LogMessage::~LogMessage() {
198 if (!extra_.empty())
199 print_stream_ << " : " << extra_;
200 print_stream_ << std::endl;
201
202 const std::string& str = print_stream_.str();
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100203 if (severity_ >= g_dbg_sev) {
jiayl66f0da22015-09-14 15:06:39 -0700204 OutputToDebug(str, severity_, tag_);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000205 }
206
Peter Boström225789d2015-10-23 15:20:56 +0200207 CritScope cs(&g_log_crit);
208 for (auto& kv : streams_) {
209 if (severity_ >= kv.second) {
210 kv.first->OnLogMessage(str);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000211 }
212 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000213}
214
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100215bool LogMessage::Loggable(LoggingSeverity sev) {
216 return sev >= g_min_sev;
217}
218
219int LogMessage::GetMinLogSeverity() {
220 return g_min_sev;
221}
222
223LoggingSeverity LogMessage::GetLogToDebug() {
224 return g_dbg_sev;
225}
Honghai Zhang82d78622016-05-06 11:29:15 -0700226int64_t LogMessage::LogStartTime() {
Taylor Brandstetter4f0dfbd2016-06-15 17:15:23 -0700227 static const int64_t g_start = SystemTimeMillis();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000228 return g_start;
229}
230
Peter Boström0c4e06b2015-10-07 12:23:21 +0200231uint32_t LogMessage::WallClockStartTime() {
deadbeef37f5ecf2017-02-27 14:06:41 -0800232 static const uint32_t g_start_wallclock = time(nullptr);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000233 return g_start_wallclock;
234}
235
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000236void LogMessage::LogThreads(bool on) {
237 thread_ = on;
238}
239
240void LogMessage::LogTimestamps(bool on) {
241 timestamp_ = on;
242}
243
Tommi0eefb4d2015-05-23 09:54:07 +0200244void LogMessage::LogToDebug(LoggingSeverity min_sev) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100245 g_dbg_sev = min_sev;
Peter Boström225789d2015-10-23 15:20:56 +0200246 CritScope cs(&g_log_crit);
Tommi00aac5a2015-05-25 11:25:59 +0200247 UpdateMinLogSeverity();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000248}
249
andrew88703d72015-09-07 00:34:56 -0700250void LogMessage::SetLogToStderr(bool log_to_stderr) {
251 log_to_stderr_ = log_to_stderr;
252}
253
Tommi0eefb4d2015-05-23 09:54:07 +0200254int LogMessage::GetLogToStream(LogSink* stream) {
Peter Boström225789d2015-10-23 15:20:56 +0200255 CritScope cs(&g_log_crit);
Tommi0eefb4d2015-05-23 09:54:07 +0200256 LoggingSeverity sev = LS_NONE;
Peter Boström225789d2015-10-23 15:20:56 +0200257 for (auto& kv : streams_) {
258 if (!stream || stream == kv.first) {
259 sev = std::min(sev, kv.second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000260 }
261 }
262 return sev;
263}
264
Tommi0eefb4d2015-05-23 09:54:07 +0200265void LogMessage::AddLogToStream(LogSink* stream, LoggingSeverity min_sev) {
Peter Boström225789d2015-10-23 15:20:56 +0200266 CritScope cs(&g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000267 streams_.push_back(std::make_pair(stream, min_sev));
268 UpdateMinLogSeverity();
269}
270
Tommi0eefb4d2015-05-23 09:54:07 +0200271void LogMessage::RemoveLogToStream(LogSink* stream) {
Peter Boström225789d2015-10-23 15:20:56 +0200272 CritScope cs(&g_log_crit);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000273 for (StreamList::iterator it = streams_.begin(); it != streams_.end(); ++it) {
274 if (stream == it->first) {
275 streams_.erase(it);
276 break;
277 }
278 }
279 UpdateMinLogSeverity();
280}
281
Tommi0eefb4d2015-05-23 09:54:07 +0200282void LogMessage::ConfigureLogging(const char* params) {
283 LoggingSeverity current_level = LS_VERBOSE;
284 LoggingSeverity debug_level = GetLogToDebug();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000285
286 std::vector<std::string> tokens;
287 tokenize(params, ' ', &tokens);
288
Tommi0eefb4d2015-05-23 09:54:07 +0200289 for (const std::string& token : tokens) {
290 if (token.empty())
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000291 continue;
292
293 // Logging features
Tommi0eefb4d2015-05-23 09:54:07 +0200294 if (token == "tstamp") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000295 LogTimestamps();
Tommi0eefb4d2015-05-23 09:54:07 +0200296 } else if (token == "thread") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000297 LogThreads();
298
299 // Logging levels
Tommi0eefb4d2015-05-23 09:54:07 +0200300 } else if (token == "sensitive") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000301 current_level = LS_SENSITIVE;
Tommi0eefb4d2015-05-23 09:54:07 +0200302 } else if (token == "verbose") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000303 current_level = LS_VERBOSE;
Tommi0eefb4d2015-05-23 09:54:07 +0200304 } else if (token == "info") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000305 current_level = LS_INFO;
Tommi0eefb4d2015-05-23 09:54:07 +0200306 } else if (token == "warning") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000307 current_level = LS_WARNING;
Tommi0eefb4d2015-05-23 09:54:07 +0200308 } else if (token == "error") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000309 current_level = LS_ERROR;
Tommi0eefb4d2015-05-23 09:54:07 +0200310 } else if (token == "none") {
311 current_level = LS_NONE;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000312
313 // Logging targets
Tommi0eefb4d2015-05-23 09:54:07 +0200314 } else if (token == "debug") {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000315 debug_level = current_level;
316 }
317 }
318
319#if defined(WEBRTC_WIN)
Tommi0eefb4d2015-05-23 09:54:07 +0200320 if ((LS_NONE != debug_level) && !::IsDebuggerPresent()) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000321 // First, attempt to attach to our parent's console... so if you invoke
322 // from the command line, we'll see the output there. Otherwise, create
323 // our own console window.
324 // Note: These methods fail if a console already exists, which is fine.
Philip Eliasson278aa422018-02-26 14:54:45 +0000325 bool success = false;
326 typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);
327 if (HINSTANCE kernel32 = ::LoadLibrary(L"kernel32.dll")) {
328 // AttachConsole is defined on WinXP+.
329 if (PFN_AttachConsole attach_console = reinterpret_cast<PFN_AttachConsole>
330 (::GetProcAddress(kernel32, "AttachConsole"))) {
331 success = (FALSE != attach_console(ATTACH_PARENT_PROCESS));
332 }
333 ::FreeLibrary(kernel32);
334 }
335 if (!success) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000336 ::AllocConsole();
Philip Eliasson278aa422018-02-26 14:54:45 +0000337 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000338 }
Tommi0eefb4d2015-05-23 09:54:07 +0200339#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000340
341 LogToDebug(debug_level);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000342}
343
danilchap3c6abd22017-09-06 05:46:29 -0700344void LogMessage::UpdateMinLogSeverity()
345 RTC_EXCLUSIVE_LOCKS_REQUIRED(g_log_crit) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100346 LoggingSeverity min_sev = g_dbg_sev;
Peter Boström225789d2015-10-23 15:20:56 +0200347 for (auto& kv : streams_) {
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100348 min_sev = std::min(g_dbg_sev, kv.second);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000349 }
Jonas Olsson2b6f1352018-02-15 11:57:03 +0100350 g_min_sev = min_sev;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000351}
352
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000353void LogMessage::OutputToDebug(const std::string& str,
jiayl66f0da22015-09-14 15:06:39 -0700354 LoggingSeverity severity,
Philip Eliasson278aa422018-02-26 14:54:45 +0000355 const std::string& tag) {
andrew88703d72015-09-07 00:34:56 -0700356 bool log_to_stderr = log_to_stderr_;
tfarinaa41ab932015-10-30 16:08:48 -0700357#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && defined(NDEBUG)
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000358 // On the Mac, all stderr output goes to the Console log and causes clutter.
359 // So in opt builds, don't log to stderr unless the user specifically sets
360 // a preference to do so.
361 CFStringRef key = CFStringCreateWithCString(kCFAllocatorDefault,
362 "logToStdErr",
363 kCFStringEncodingUTF8);
364 CFStringRef domain = CFBundleGetIdentifier(CFBundleGetMainBundle());
deadbeef37f5ecf2017-02-27 14:06:41 -0800365 if (key != nullptr && domain != nullptr) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000366 Boolean exists_and_is_valid;
367 Boolean should_log =
368 CFPreferencesGetAppBooleanValue(key, domain, &exists_and_is_valid);
369 // If the key doesn't exist or is invalid or is false, we will not log to
370 // stderr.
371 log_to_stderr = exists_and_is_valid && should_log;
372 }
deadbeef37f5ecf2017-02-27 14:06:41 -0800373 if (key != nullptr) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000374 CFRelease(key);
375 }
Philip Eliasson278aa422018-02-26 14:54:45 +0000376#endif
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000377#if defined(WEBRTC_WIN)
378 // Always log to the debugger.
379 // Perhaps stderr should be controlled by a preference, as on Mac?
380 OutputDebugStringA(str.c_str());
381 if (log_to_stderr) {
382 // This handles dynamically allocated consoles, too.
383 if (HANDLE error_handle = ::GetStdHandle(STD_ERROR_HANDLE)) {
384 log_to_stderr = false;
385 DWORD written = 0;
386 ::WriteFile(error_handle, str.data(), static_cast<DWORD>(str.size()),
387 &written, 0);
388 }
389 }
Tommi0eefb4d2015-05-23 09:54:07 +0200390#endif // WEBRTC_WIN
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000391#if defined(WEBRTC_ANDROID)
392 // Android's logging facility uses severity to log messages but we
393 // need to map libjingle's severity levels to Android ones first.
394 // Also write to stderr which maybe available to executable started
395 // from the shell.
396 int prio;
397 switch (severity) {
398 case LS_SENSITIVE:
Philip Eliasson278aa422018-02-26 14:54:45 +0000399 __android_log_write(ANDROID_LOG_INFO, tag.c_str(), "SENSITIVE");
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000400 if (log_to_stderr) {
401 fprintf(stderr, "SENSITIVE");
402 fflush(stderr);
403 }
404 return;
405 case LS_VERBOSE:
406 prio = ANDROID_LOG_VERBOSE;
407 break;
408 case LS_INFO:
409 prio = ANDROID_LOG_INFO;
410 break;
411 case LS_WARNING:
412 prio = ANDROID_LOG_WARN;
413 break;
414 case LS_ERROR:
415 prio = ANDROID_LOG_ERROR;
416 break;
417 default:
418 prio = ANDROID_LOG_UNKNOWN;
419 }
420
421 int size = str.size();
422 int line = 0;
423 int idx = 0;
424 const int max_lines = size / kMaxLogLineSize + 1;
425 if (max_lines == 1) {
Philip Eliasson278aa422018-02-26 14:54:45 +0000426 __android_log_print(prio, tag.c_str(), "%.*s", size, str.c_str());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000427 } else {
428 while (size > 0) {
429 const int len = std::min(size, kMaxLogLineSize);
430 // Use the size of the string in the format (str may have \0 in the
431 // middle).
Philip Eliasson278aa422018-02-26 14:54:45 +0000432 __android_log_print(prio, tag.c_str(), "[%d/%d] %.*s",
433 line + 1, max_lines,
434 len, str.c_str() + idx);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000435 idx += len;
436 size -= len;
437 ++line;
438 }
439 }
440#endif // WEBRTC_ANDROID
441 if (log_to_stderr) {
442 fprintf(stderr, "%s", str.c_str());
443 fflush(stderr);
444 }
445}
446
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000447//////////////////////////////////////////////////////////////////////
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000448
449} // namespace rtc