void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs) { const size_t kBuffSize = 1024; char buff[kBuffSize]; char* buffToWrite = buff; // For backwards compat we need to use the NSPR format string versions // of sprintf and friends and then hand off to printf. va_list argsCopy; va_copy(argsCopy, aArgs); size_t charsWritten = PR_vsnprintf(buff, kBuffSize, aFmt, argsCopy); va_end(argsCopy); if (charsWritten == kBuffSize - 1) { // We may have maxed out, allocate a buffer instead. buffToWrite = PR_vsmprintf(aFmt, aArgs); charsWritten = strlen(buffToWrite); } // Determine if a newline needs to be appended to the message. const char* newline = ""; if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') { newline = "\n"; } FILE* out = mOutFile ? mOutFile : stderr; // This differs from the NSPR format in that we do not output the // opaque system specific thread pointer (ie pthread_t) cast // to a long. The address of the current PR_Thread continues to be // prefixed. // // Additionally we prefix the output with the abbreviated log level // and the module name. if (!mAddTimestamp) { fprintf_stderr(out, "[%p]: %s/%s %s%s", PR_GetCurrentThread(), ToLogStr(aLevel), aName, buffToWrite, newline); } else { PRExplodedTime now; PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now); fprintf_stderr( out, "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%p]: %s/%s %s%s", now.tm_year, now.tm_month + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec, PR_GetCurrentThread(), ToLogStr(aLevel), aName, buffToWrite, newline); } if (mIsSync) { fflush(out); } if (buffToWrite != buff) { PR_smprintf_free(buffToWrite); } }
void Print(const char* aName, LogLevel aLevel, const char* aFmt, va_list aArgs) { const size_t kBuffSize = 1024; char buff[kBuffSize]; char* buffToWrite = buff; // For backwards compat we need to use the NSPR format string versions // of sprintf and friends and then hand off to printf. va_list argsCopy; va_copy(argsCopy, aArgs); size_t charsWritten = PR_vsnprintf(buff, kBuffSize, aFmt, argsCopy); va_end(argsCopy); if (charsWritten == kBuffSize - 1) { // We may have maxed out, allocate a buffer instead. buffToWrite = PR_vsmprintf(aFmt, aArgs); charsWritten = strlen(buffToWrite); } // Determine if a newline needs to be appended to the message. const char* newline = ""; if (charsWritten == 0 || buffToWrite[charsWritten - 1] != '\n') { newline = "\n"; } FILE* out = stderr; // In case we use rotate, this ensures the FILE is kept alive during // its use. Increased before we load mOutFile. ++mPrintEntryCount; detail::LogFile* outFile = mOutFile; if (outFile) { out = outFile->File(); } // This differs from the NSPR format in that we do not output the // opaque system specific thread pointer (ie pthread_t) cast // to a long. The address of the current PR_Thread continues to be // prefixed. // // Additionally we prefix the output with the abbreviated log level // and the module name. PRThread *currentThread = PR_GetCurrentThread(); const char *currentThreadName = (mMainThread == currentThread) ? "Main Thread" : PR_GetThreadName(currentThread); char noNameThread[40]; if (!currentThreadName) { SprintfLiteral(noNameThread, "Unnamed thread %p", currentThread); currentThreadName = noNameThread; } if (!mAddTimestamp) { fprintf_stderr(out, "[%s]: %s/%s %s%s", currentThreadName, ToLogStr(aLevel), aName, buffToWrite, newline); } else { PRExplodedTime now; PR_ExplodeTime(PR_Now(), PR_GMTParameters, &now); fprintf_stderr( out, "%04d-%02d-%02d %02d:%02d:%02d.%06d UTC - [%s]: %s/%s %s%s", now.tm_year, now.tm_month + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec, currentThreadName, ToLogStr(aLevel), aName, buffToWrite, newline); } if (mIsSync) { fflush(out); } if (buffToWrite != buff) { PR_smprintf_free(buffToWrite); } if (mRotate > 0 && outFile) { int32_t fileSize = ftell(out); if (fileSize > mRotate) { uint32_t fileNum = outFile->Num(); uint32_t nextFileNum = fileNum + 1; if (nextFileNum >= kRotateFilesNumber) { nextFileNum = 0; } // And here is the trick. The current out-file remembers its order // number. When no other thread shifted the global file number yet, // we are the thread to open the next file. if (mOutFileNum.compareExchange(fileNum, nextFileNum)) { // We can work with mToReleaseFile because we are sure the // mPrintEntryCount can't drop to zero now - the condition // to actually delete what's stored in that member. // And also, no other thread can enter this piece of code // because mOutFile is still holding the current file with // the non-shifted number. The compareExchange() above is // a no-op for other threads. outFile->mNextToRelease = mToReleaseFile; mToReleaseFile = outFile; mOutFile = OpenFile(false, nextFileNum); } } } if (--mPrintEntryCount == 0 && mToReleaseFile) { // We were the last Print() entered, if there is a file to release // do it now. exchange() is atomic and makes sure we release the file // only once on one thread. detail::LogFile* release = mToReleaseFile.exchange(nullptr); delete release; } }