Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/Logging.h"
8 :
9 : #include <algorithm>
10 :
11 : #include "mozilla/ClearOnShutdown.h"
12 : #include "mozilla/FileUtils.h"
13 : #include "mozilla/Mutex.h"
14 : #include "mozilla/StaticPtr.h"
15 : #include "mozilla/Printf.h"
16 : #include "mozilla/Atomics.h"
17 : #include "mozilla/Sprintf.h"
18 : #include "mozilla/UniquePtrExtensions.h"
19 : #include "MainThreadUtils.h"
20 : #include "nsClassHashtable.h"
21 : #include "nsDebug.h"
22 : #include "NSPRLogModulesParser.h"
23 : #include "LogCommandLineHandler.h"
24 :
25 : #include "prenv.h"
26 : #ifdef XP_WIN
27 : #include <process.h>
28 : #else
29 : #include <sys/types.h>
30 : #include <unistd.h>
31 : #endif
32 :
33 : // NB: Initial amount determined by auditing the codebase for the total amount
34 : // of unique module names and padding up to the next power of 2.
35 : const uint32_t kInitialModuleCount = 256;
36 : // When rotate option is added to the modules list, this is the hardcoded
37 : // number of files we create and rotate. When there is rotate:40,
38 : // we will keep four files per process, each limited to 10MB. Sum is 40MB,
39 : // the given limit.
40 : //
41 : // (Note: When this is changed to be >= 10, SandboxBroker::LaunchApp must add
42 : // another rule to allow logfile.?? be written by content processes.)
43 : const uint32_t kRotateFilesNumber = 4;
44 :
45 : namespace mozilla {
46 :
47 0 : LazyLogModule::operator LogModule*()
48 : {
49 : // NB: The use of an atomic makes the reading and assignment of mLog
50 : // thread-safe. There is a small chance that mLog will be set more
51 : // than once, but that's okay as it will be set to the same LogModule
52 : // instance each time. Also note LogModule::Get is thread-safe.
53 0 : LogModule* tmp = mLog;
54 0 : if (MOZ_UNLIKELY(!tmp)) {
55 0 : tmp = LogModule::Get(mLogName);
56 0 : mLog = tmp;
57 : }
58 :
59 0 : mCanary.Check();
60 :
61 198609 : return tmp;
62 : }
63 :
64 : namespace detail {
65 :
66 0 : void log_print(const LogModule* aModule,
67 : LogLevel aLevel,
68 : const char* aFmt, ...)
69 : {
70 : va_list ap;
71 0 : va_start(ap, aFmt);
72 0 : aModule->Printv(aLevel, aFmt, ap);
73 0 : va_end(ap);
74 0 : }
75 :
76 : } // detail
77 :
78 : LogLevel
79 0 : ToLogLevel(int32_t aLevel)
80 : {
81 0 : aLevel = std::min(aLevel, static_cast<int32_t>(LogLevel::Verbose));
82 0 : aLevel = std::max(aLevel, static_cast<int32_t>(LogLevel::Disabled));
83 0 : return static_cast<LogLevel>(aLevel);
84 : }
85 :
86 : const char*
87 0 : ToLogStr(LogLevel aLevel) {
88 0 : switch (aLevel) {
89 : case LogLevel::Error:
90 : return "E";
91 : case LogLevel::Warning:
92 0 : return "W";
93 : case LogLevel::Info:
94 0 : return "I";
95 : case LogLevel::Debug:
96 0 : return "D";
97 : case LogLevel::Verbose:
98 0 : return "V";
99 : case LogLevel::Disabled:
100 : default:
101 0 : MOZ_CRASH("Invalid log level.");
102 : return "";
103 : }
104 : }
105 :
106 : namespace detail {
107 :
108 : /**
109 : * A helper class providing reference counting for FILE*.
110 : * It encapsulates the following:
111 : * - the FILE handle
112 : * - the order number it was created for when rotating (actual path)
113 : * - number of active references
114 : */
115 : class LogFile
116 : {
117 : FILE* mFile;
118 : uint32_t mFileNum;
119 :
120 : public:
121 : LogFile(FILE* aFile, uint32_t aFileNum)
122 0 : : mFile(aFile)
123 : , mFileNum(aFileNum)
124 0 : , mNextToRelease(nullptr)
125 : {
126 : }
127 :
128 0 : ~LogFile()
129 0 : {
130 0 : fclose(mFile);
131 0 : delete mNextToRelease;
132 0 : }
133 :
134 : FILE* File() const { return mFile; }
135 : uint32_t Num() const { return mFileNum; }
136 :
137 : LogFile* mNextToRelease;
138 : };
139 :
140 : const char*
141 0 : ExpandPIDMarker(const char* aFilename, char (&buffer)[2048])
142 : {
143 0 : MOZ_ASSERT(aFilename);
144 : static const char kPIDToken[] = "%PID";
145 0 : const char* pidTokenPtr = strstr(aFilename, kPIDToken);
146 0 : if (pidTokenPtr &&
147 0 : SprintfLiteral(buffer, "%.*s%s%d%s",
148 0 : static_cast<int>(pidTokenPtr - aFilename), aFilename,
149 0 : XRE_IsParentProcess() ? "-main." : "-child.",
150 : base::GetCurrentProcId(),
151 : pidTokenPtr + strlen(kPIDToken)) > 0)
152 : {
153 : return buffer;
154 : }
155 :
156 0 : return aFilename;
157 : }
158 :
159 : } // detail
160 :
161 : namespace {
162 : // Helper method that initializes an empty va_list to be empty.
163 0 : void empty_va(va_list *va, ...)
164 : {
165 0 : va_start(*va, va);
166 0 : va_end(*va);
167 0 : }
168 : }
169 :
170 : class LogModuleManager
171 : {
172 : public:
173 1 : LogModuleManager()
174 1 : : mModulesLock("logmodules")
175 : , mModules(kInitialModuleCount)
176 : , mPrintEntryCount(0)
177 : , mOutFile(nullptr)
178 : , mToReleaseFile(nullptr)
179 : , mOutFileNum(0)
180 : , mOutFilePath(strdup(""))
181 1 : , mMainThread(PR_GetCurrentThread())
182 : , mSetFromEnv(false)
183 : , mAddTimestamp(false)
184 : , mIsRaw(false)
185 : , mIsSync(false)
186 : , mRotate(0)
187 0 : , mInitialized(false)
188 : {
189 1 : }
190 :
191 0 : ~LogModuleManager()
192 0 : {
193 0 : detail::LogFile* logFile = mOutFile.exchange(nullptr);
194 0 : delete logFile;
195 0 : }
196 :
197 : /**
198 : * Loads config from command line args or env vars if present, in
199 : * this specific order of priority.
200 : *
201 : * Notes:
202 : *
203 : * 1) This function is only intended to be called once per session.
204 : * 2) None of the functions used in Init should rely on logging.
205 : */
206 0 : void Init(int argc, char* argv[])
207 : {
208 1 : MOZ_DIAGNOSTIC_ASSERT(!mInitialized);
209 0 : mInitialized = true;
210 :
211 2 : LoggingHandleCommandLineArgs(argc, static_cast<char const* const*>(argv),
212 0 : [](nsACString const& env) {
213 : // We deliberately set/rewrite the environment variables
214 : // so that when child processes are spawned w/o passing
215 : // the arguments they still inherit the logging settings
216 : // as well as sandboxing can be correctly set.
217 : // Scripts can pass -MOZ_LOG=$MOZ_LOG,modules as an argument
218 : // to merge existing settings, if required.
219 :
220 : // PR_SetEnv takes ownership of the string.
221 0 : PR_SetEnv(ToNewCString(env));
222 0 : });
223 :
224 0 : bool shouldAppend = false;
225 0 : bool addTimestamp = false;
226 0 : bool isSync = false;
227 0 : bool isRaw = false;
228 0 : int32_t rotate = 0;
229 0 : const char* modules = PR_GetEnv("MOZ_LOG");
230 0 : if (!modules || !modules[0]) {
231 1 : modules = PR_GetEnv("MOZ_LOG_MODULES");
232 1 : if (modules) {
233 : NS_WARNING("MOZ_LOG_MODULES is deprecated."
234 0 : "\nPlease use MOZ_LOG instead.");
235 : }
236 : }
237 0 : if (!modules || !modules[0]) {
238 1 : modules = PR_GetEnv("NSPR_LOG_MODULES");
239 1 : if (modules) {
240 : NS_WARNING("NSPR_LOG_MODULES is deprecated."
241 0 : "\nPlease use MOZ_LOG instead.");
242 : }
243 : }
244 :
245 : // Need to capture `this` since `sLogModuleManager` is not set until after
246 : // initialization is complete.
247 0 : NSPRLogModulesParser(modules,
248 : [this, &shouldAppend, &addTimestamp, &isSync, &isRaw, &rotate]
249 0 : (const char* aName, LogLevel aLevel, int32_t aValue) mutable {
250 0 : if (strcmp(aName, "append") == 0) {
251 0 : shouldAppend = true;
252 0 : } else if (strcmp(aName, "timestamp") == 0) {
253 0 : addTimestamp = true;
254 0 : } else if (strcmp(aName, "sync") == 0) {
255 0 : isSync = true;
256 0 : } else if (strcmp(aName, "raw") == 0) {
257 0 : isRaw = true;
258 0 : } else if (strcmp(aName, "rotate") == 0) {
259 0 : rotate = (aValue << 20) / kRotateFilesNumber;
260 : } else {
261 0 : this->CreateOrGetModule(aName)->SetLevel(aLevel);
262 : }
263 1 : });
264 :
265 : // Rotate implies timestamp to make the files readable
266 0 : mAddTimestamp = addTimestamp || rotate > 0;
267 0 : mIsSync = isSync;
268 2 : mIsRaw = isRaw;
269 0 : mRotate = rotate;
270 :
271 1 : if (rotate > 0 && shouldAppend) {
272 0 : NS_WARNING("MOZ_LOG: when you rotate the log, you cannot use append!");
273 : }
274 :
275 0 : const char* logFile = PR_GetEnv("MOZ_LOG_FILE");
276 1 : if (!logFile || !logFile[0]) {
277 1 : logFile = PR_GetEnv("NSPR_LOG_FILE");
278 : }
279 :
280 1 : if (logFile && logFile[0]) {
281 : char buf[2048];
282 0 : logFile = detail::ExpandPIDMarker(logFile, buf);
283 0 : mOutFilePath.reset(strdup(logFile));
284 :
285 0 : if (mRotate > 0) {
286 : // Delete all the previously captured files, including non-rotated
287 : // log files, so that users don't complain our logs eat space even
288 : // after the rotate option has been added and don't happen to send
289 : // us old large logs along with the rotated files.
290 0 : remove(mOutFilePath.get());
291 0 : for (uint32_t i = 0; i < kRotateFilesNumber; ++i) {
292 0 : RemoveFile(i);
293 : }
294 : }
295 :
296 0 : mOutFile = OpenFile(shouldAppend, mOutFileNum);
297 0 : mSetFromEnv = true;
298 : }
299 1 : }
300 :
301 0 : void SetLogFile(const char* aFilename)
302 : {
303 : // For now we don't allow you to change the file at runtime.
304 0 : if (mSetFromEnv) {
305 : NS_WARNING("LogModuleManager::SetLogFile - Log file was set from the "
306 0 : "MOZ_LOG_FILE environment variable.");
307 0 : return;
308 : }
309 :
310 0 : const char * filename = aFilename ? aFilename : "";
311 : char buf[2048];
312 0 : filename = detail::ExpandPIDMarker(filename, buf);
313 :
314 : // Can't use rotate at runtime yet.
315 0 : MOZ_ASSERT(mRotate == 0, "We don't allow rotate for runtime logfile changes");
316 0 : mOutFilePath.reset(strdup(filename));
317 :
318 : // Exchange mOutFile and set it to be released once all the writes are done.
319 0 : detail::LogFile* newFile = OpenFile(false, 0);
320 0 : detail::LogFile* oldFile = mOutFile.exchange(newFile);
321 :
322 : // Since we don't allow changing the logfile if MOZ_LOG_FILE is already set,
323 : // and we don't allow log rotation when setting it at runtime, mToReleaseFile
324 : // will be null, so we're not leaking.
325 0 : DebugOnly<detail::LogFile*> prevFile = mToReleaseFile.exchange(oldFile);
326 0 : MOZ_ASSERT(!prevFile, "Should be null because rotation is not allowed");
327 :
328 : // If we just need to release a file, we must force print, in order to
329 : // trigger the closing and release of mToReleaseFile.
330 0 : if (oldFile) {
331 : va_list va;
332 0 : empty_va(&va);
333 0 : Print("Logger", LogLevel::Info, "Flushing old log files\n", va);
334 : }
335 : }
336 :
337 0 : uint32_t GetLogFile(char *aBuffer, size_t aLength)
338 : {
339 0 : uint32_t len = strlen(mOutFilePath.get());
340 0 : if (len + 1 > aLength) {
341 : return 0;
342 : }
343 0 : snprintf(aBuffer, aLength, "%s", mOutFilePath.get());
344 0 : return len;
345 : }
346 :
347 : void SetIsSync(bool aIsSync)
348 : {
349 0 : mIsSync = aIsSync;
350 : }
351 :
352 : void SetAddTimestamp(bool aAddTimestamp)
353 : {
354 0 : mAddTimestamp = aAddTimestamp;
355 : }
356 :
357 0 : detail::LogFile* OpenFile(bool aShouldAppend, uint32_t aFileNum)
358 : {
359 : FILE* file;
360 :
361 0 : if (mRotate > 0) {
362 : char buf[2048];
363 0 : SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
364 :
365 : // rotate doesn't support append.
366 0 : file = fopen(buf, "w");
367 : } else {
368 0 : file = fopen(mOutFilePath.get(), aShouldAppend ? "a" : "w");
369 : }
370 :
371 0 : if (!file) {
372 : return nullptr;
373 : }
374 :
375 0 : return new detail::LogFile(file, aFileNum);
376 : }
377 :
378 0 : void RemoveFile(uint32_t aFileNum)
379 : {
380 : char buf[2048];
381 0 : SprintfLiteral(buf, "%s.%d", mOutFilePath.get(), aFileNum);
382 0 : remove(buf);
383 0 : }
384 :
385 0 : LogModule* CreateOrGetModule(const char* aName)
386 : {
387 0 : OffTheBooksMutexAutoLock guard(mModulesLock);
388 0 : LogModule* module = nullptr;
389 0 : if (!mModules.Get(aName, &module)) {
390 198 : module = new LogModule(aName, LogLevel::Disabled);
391 99 : mModules.Put(aName, module);
392 : }
393 :
394 208 : return module;
395 : }
396 :
397 0 : void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs)
398 : MOZ_FORMAT_PRINTF(4, 0)
399 : {
400 : // We don't do nuwa-style forking anymore, so our pid can't change.
401 0 : static long pid = static_cast<long>(base::GetCurrentProcId());
402 0 : const size_t kBuffSize = 1024;
403 : char buff[kBuffSize];
404 :
405 0 : char* buffToWrite = buff;
406 0 : SmprintfPointer allocatedBuff;
407 :
408 : va_list argsCopy;
409 0 : va_copy(argsCopy, aArgs);
410 0 : int charsWritten = VsprintfLiteral(buff, aFmt, argsCopy);
411 0 : va_end(argsCopy);
412 :
413 0 : if (charsWritten < 0) {
414 : // Print out at least something. We must copy to the local buff,
415 : // can't just assign aFmt to buffToWrite, since when
416 : // buffToWrite != buff, we try to release it.
417 0 : MOZ_ASSERT(false, "Probably incorrect format string in LOG?");
418 : strncpy(buff, aFmt, kBuffSize - 1);
419 : buff[kBuffSize - 1] = '\0';
420 : charsWritten = strlen(buff);
421 0 : } else if (static_cast<size_t>(charsWritten) >= kBuffSize - 1) {
422 : // We may have maxed out, allocate a buffer instead.
423 0 : allocatedBuff = mozilla::Vsmprintf(aFmt, aArgs);
424 0 : buffToWrite = allocatedBuff.get();
425 0 : charsWritten = strlen(buffToWrite);
426 : }
427 :
428 : // Determine if a newline needs to be appended to the message.
429 0 : const char* newline = "";
430 0 : if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') {
431 0 : newline = "\n";
432 : }
433 :
434 0 : FILE* out = stderr;
435 :
436 : // In case we use rotate, this ensures the FILE is kept alive during
437 : // its use. Increased before we load mOutFile.
438 0 : ++mPrintEntryCount;
439 :
440 0 : detail::LogFile* outFile = mOutFile;
441 0 : if (outFile) {
442 0 : out = outFile->File();
443 : }
444 :
445 : // This differs from the NSPR format in that we do not output the
446 : // opaque system specific thread pointer (ie pthread_t) cast
447 : // to a long. The address of the current PR_Thread continues to be
448 : // prefixed.
449 : //
450 : // Additionally we prefix the output with the abbreviated log level
451 : // and the module name.
452 0 : PRThread *currentThread = PR_GetCurrentThread();
453 0 : const char *currentThreadName = (mMainThread == currentThread)
454 0 : ? "Main Thread"
455 0 : : PR_GetThreadName(currentThread);
456 :
457 : char noNameThread[40];
458 0 : if (!currentThreadName) {
459 0 : SprintfLiteral(noNameThread, "Unnamed thread %p", currentThread);
460 0 : currentThreadName = noNameThread;
461 : }
462 :
463 0 : if (!mAddTimestamp) {
464 0 : if (!mIsRaw) {
465 0 : fprintf_stderr(out,
466 : "[%ld:%s]: %s/%s %s%s",
467 : pid, currentThreadName, ToLogStr(aLevel),
468 0 : aName, buffToWrite, newline);
469 : } else {
470 0 : fprintf_stderr(out, "%s%s", buffToWrite, newline);
471 : }
472 : } else {
473 : PRExplodedTime now;
474 0 : PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now);
475 0 : fprintf_stderr(
476 : out,
477 : "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%ld:%s]: %s/%s %s%s",
478 0 : now.tm_year, now.tm_month + 1, now.tm_mday,
479 : now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec,
480 : pid, currentThreadName, ToLogStr(aLevel),
481 0 : aName, buffToWrite, newline);
482 : }
483 :
484 0 : if (mIsSync) {
485 0 : fflush(out);
486 : }
487 :
488 0 : if (mRotate > 0 && outFile) {
489 0 : int32_t fileSize = ftell(out);
490 0 : if (fileSize > mRotate) {
491 0 : uint32_t fileNum = outFile->Num();
492 :
493 0 : uint32_t nextFileNum = fileNum + 1;
494 0 : if (nextFileNum >= kRotateFilesNumber) {
495 0 : nextFileNum = 0;
496 : }
497 :
498 : // And here is the trick. The current out-file remembers its order
499 : // number. When no other thread shifted the global file number yet,
500 : // we are the thread to open the next file.
501 0 : if (mOutFileNum.compareExchange(fileNum, nextFileNum)) {
502 : // We can work with mToReleaseFile because we are sure the
503 : // mPrintEntryCount can't drop to zero now - the condition
504 : // to actually delete what's stored in that member.
505 : // And also, no other thread can enter this piece of code
506 : // because mOutFile is still holding the current file with
507 : // the non-shifted number. The compareExchange() above is
508 : // a no-op for other threads.
509 0 : outFile->mNextToRelease = mToReleaseFile;
510 0 : mToReleaseFile = outFile;
511 :
512 0 : mOutFile = OpenFile(false, nextFileNum);
513 : }
514 : }
515 : }
516 :
517 0 : if (--mPrintEntryCount == 0 && mToReleaseFile) {
518 : // We were the last Print() entered, if there is a file to release
519 : // do it now. exchange() is atomic and makes sure we release the file
520 : // only once on one thread.
521 0 : detail::LogFile* release = mToReleaseFile.exchange(nullptr);
522 0 : delete release;
523 : }
524 0 : }
525 :
526 : private:
527 : OffTheBooksMutex mModulesLock;
528 : nsClassHashtable<nsCharPtrHashKey, LogModule> mModules;
529 :
530 : // Print() entry counter, actually reflects concurrent use of the current
531 : // output file. ReleaseAcquire ensures that manipulation with mOutFile
532 : // and mToReleaseFile is synchronized by manipulation with this value.
533 : Atomic<uint32_t, ReleaseAcquire> mPrintEntryCount;
534 : // File to write to. ReleaseAcquire because we need to sync mToReleaseFile
535 : // with this.
536 : Atomic<detail::LogFile*, ReleaseAcquire> mOutFile;
537 : // File to be released when reference counter drops to zero. This member
538 : // is assigned mOutFile when the current file has reached the limit.
539 : // It can be Relaxed, since it's synchronized with mPrintEntryCount
540 : // manipulation and we do atomic exchange() on it.
541 : Atomic<detail::LogFile*, Relaxed> mToReleaseFile;
542 : // The next file number. This is mostly only for synchronization sake.
543 : // Can have relaxed ordering, since we only do compareExchange on it which
544 : // is atomic regardless ordering.
545 : Atomic<uint32_t, Relaxed> mOutFileNum;
546 : // Just keeps the actual file path for further use.
547 : UniqueFreePtr<char[]> mOutFilePath;
548 :
549 : PRThread *mMainThread;
550 : bool mSetFromEnv;
551 : Atomic<bool, Relaxed> mAddTimestamp;
552 : Atomic<bool, Relaxed> mIsRaw;
553 : Atomic<bool, Relaxed> mIsSync;
554 : int32_t mRotate;
555 : bool mInitialized;
556 : };
557 :
558 1 : StaticAutoPtr<LogModuleManager> sLogModuleManager;
559 :
560 : LogModule*
561 104 : LogModule::Get(const char* aName)
562 : {
563 : // This is just a pass through to the LogModuleManager so
564 : // that the LogModuleManager implementation can be kept internal.
565 208 : MOZ_ASSERT(sLogModuleManager != nullptr);
566 104 : return sLogModuleManager->CreateOrGetModule(aName);
567 : }
568 :
569 : void
570 0 : LogModule::SetLogFile(const char* aFilename)
571 : {
572 0 : MOZ_ASSERT(sLogModuleManager);
573 0 : sLogModuleManager->SetLogFile(aFilename);
574 0 : }
575 :
576 : uint32_t
577 0 : LogModule::GetLogFile(char *aBuffer, size_t aLength)
578 : {
579 0 : MOZ_ASSERT(sLogModuleManager);
580 0 : return sLogModuleManager->GetLogFile(aBuffer, aLength);
581 : }
582 :
583 : void
584 0 : LogModule::SetAddTimestamp(bool aAddTimestamp)
585 : {
586 0 : sLogModuleManager->SetAddTimestamp(aAddTimestamp);
587 0 : }
588 :
589 : void
590 0 : LogModule::SetIsSync(bool aIsSync)
591 : {
592 0 : sLogModuleManager->SetIsSync(aIsSync);
593 0 : }
594 :
595 : void
596 3 : LogModule::Init(int argc, char* argv[])
597 : {
598 : // NB: This method is not threadsafe; it is expected to be called very early
599 : // in startup prior to any other threads being run.
600 0 : MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
601 :
602 3 : if (sLogModuleManager) {
603 : // Already initialized.
604 : return;
605 : }
606 :
607 : // NB: We intentionally do not register for ClearOnShutdown as that happens
608 : // before all logging is complete. And, yes, that means we leak, but
609 : // we're doing that intentionally.
610 :
611 : // Don't assign the pointer until after Init is called. This should help us
612 : // detect if any of the functions called by Init somehow rely on logging.
613 1 : auto mgr = new LogModuleManager();
614 1 : mgr->Init(argc, argv);
615 : sLogModuleManager = mgr;
616 : }
617 :
618 : void
619 0 : LogModule::Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const
620 : {
621 0 : MOZ_ASSERT(sLogModuleManager != nullptr);
622 :
623 : // Forward to LogModule manager w/ level and name
624 : sLogModuleManager->Print(Name(), aLevel, aFmt, aArgs);
625 : }
626 :
627 : } // namespace mozilla
|