All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mq5.1-source.src.share.cclient.util.Logger.cpp Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * @(#)Logger.cpp	1.19 10/17/07
 */ 

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include 
#include 
#if defined(WIN32)
#include 
#else
#include 
#endif

#ifdef __cplusplus
}
#endif /* __cplusplus */

#include "Logger.hpp"
#include "LogUtils.hpp"
#include "../util/UtilityMacros.h"
#include "../cshim/iMQCallbackUtils.hpp"

static const int MAX_PRINT_LOG_LEVEL_STR_LEN = 7; // WARNING
static const int MAX_PRINT_LOG_CODE_LEN = 7; 
static const int MAX_PRINT_CONN_ID_LEN = 21;
static const int MAX_PRINT_FILE_NAME_LEN = 23;
static const int MAX_PRINT_LINE_NUMBER_LEN = 4;

Logger::Logger()
{
  this->init();
}



Logger::~Logger() 
{
  this->reset();
}

void
Logger::init()
{
  m_logFileLogLevel  = CONFIG_LOG_LEVEL;
  m_callbackLogLevel = CONFIG_LOG_LEVEL;
  m_stdErrLogLevel   = CONFIG_LOG_LEVEL;
  
  char * env = NULL;
  if ((env = PR_GetEnv("MQ_LOG_LEVEL")) != NULL && env[0]) {
    int cnt = 0, enval = NONE_LOG_LEVEL-1;
    cnt = sscanf(env, "%d", &enval);
    if (cnt == 0 || cnt == EOF ||
        enval < NONE_LOG_LEVEL ||
        enval > FINEST_LOG_LEVEL) { 
      fprintf(stderr, "WARNING: Invalid MQ_LOG_LEVEL %s\n", env);
    }
    else {
      m_logFileLogLevel  = enval;
      m_callbackLogLevel = enval;
      m_stdErrLogLevel   = enval;
    }
  }
  if (m_logFileLogLevel > m_callbackLogLevel) {
    g_logLevel = m_logFileLogLevel;
  } else if (m_callbackLogLevel > m_stdErrLogLevel) { 
    g_logLevel = m_callbackLogLevel;
  } else {
    g_logLevel = m_stdErrLogLevel;
  }

  STRCPY( m_logFileName, "" );
  m_logFileName_gptr = NULL;
  m_logFile          = NULL;
  int pid = 0;
  if ((env = PR_GetEnv("MQ_LOG_FILE_APPEND_PID")) != NULL && env[0]) {
    pid = (int)GETPID();
  }
  if ((env = PR_GetEnv("MQ_LOG_FILE")) != NULL && env[0]) {
    if (pid == 0) {
      STRNCPY(m_logFileName, env,  sizeof(m_logFileName));
    } else {
      SNPRINTF(m_logFileName, sizeof(m_logFileName), "%s_%d", env, pid);
    }
    m_logFileName[sizeof(m_logFileName)-1] = '\0';
    this->parseLogFileName(m_logFileName, &m_logFileName_gptr);
    this->openLogFile(m_logFileName, m_logFileName_gptr);
  }
  
  m_loggingCallback  = NULL;
  m_callbackData     = NULL;
  m_maximumLogSize   = LOGGER_DEFAULT_MAXIMUM_LOG_SIZE;

  // init the masks to FFFFFFFF for all levels
  for (LogLevel l = MIN_LOG_LEVEL; l <= MAX_LOG_LEVEL; l++) {
    m_mask[l] = (PRUint32)~0;
  }

  if ((env = PR_GetEnv("MQ_LOG_MASK")) != NULL && env[0]) {
    int cnt = 0, enval = 0;
    cnt = sscanf(env, "%d", &enval);
    if (cnt == 0 || cnt == EOF) {
        fprintf(stderr, "WARNING: Invalid MQ_LOG_MASK %s\n", env);
    } else {
      for (LogLevel l = MIN_LOG_LEVEL; l <= MAX_LOG_LEVEL; l++) {
        m_mask[l] = (PRUint32)enval;
      }
    }
  }

}

void
Logger::reset()
{
  // just close the file... nothing was dynamically allocated
  if (m_logFile != NULL) {
    fclose(m_logFile);
    m_logFile = NULL;
  }
  this->init();
}

void
Logger::setLogFileName(const char * const logfilename)
{
  if (logfilename == NULL) {
    STRCPY(m_logFileName, "");
    this->openLogFile(NULL, NULL);
  } else {
    STRNCPY(m_logFileName, logfilename, sizeof(m_logFileName));
    m_logFileName[sizeof(m_logFileName)-1] = '\0';
    this->parseLogFileName(m_logFileName, &m_logFileName_gptr);
    this->openLogFile(m_logFileName, m_logFileName_gptr);
  }
}

//XXX no i18n
void
Logger::parseLogFileName(char * logfilename, char **gptr)
{
  char abbrFilename[LOGGER_MAX_LOG_NAME_SIZE];
  const char * slashptr = NULL, *pctptr = NULL;
  char *ptr = NULL;
  PRUptrdiff termindex = 0;
  PRBool escape = PR_FALSE;

  if (gptr == NULL) return; //should not happen
  *gptr = NULL; 
#if defined(XP_UNIX)
  if ((slashptr = STRRCHR(logfilename, '/')) != NULL) {
#elif defined(WIN32) //XXX
  if ((slashptr = STRRCHR(logfilename, '\\')) != NULL) {
#else //XXX really not currently supported 
  return;
#endif
    STRNCPY(abbrFilename, &(slashptr[1]), sizeof(abbrFilename));
    abbrFilename[sizeof(abbrFilename)-1] = '\0';
  } else {
    STRNCPY(abbrFilename, logfilename, sizeof(abbrFilename));
    abbrFilename[sizeof(abbrFilename)-1] = '\0';
  }
  while (STRLEN(abbrFilename) > 0) {
  if ((pctptr = STRRCHR(abbrFilename, '%')) != NULL) {
    if (*(pctptr+1)  == 'g') {
      if (pctptr > abbrFilename && *(pctptr -1) == '%') { //%%g
        pctptr--;
        escape = PR_TRUE;
      } 
      if (slashptr != NULL) {
        termindex = ((slashptr+1)-logfilename)+(pctptr-abbrFilename);
      } else {
        termindex = pctptr-abbrFilename;
      }
      logfilename[termindex] = '\0';
      if (escape == PR_TRUE) {
        STRCAT(logfilename, logfilename+termindex+1);
      } else {
        *gptr = logfilename + termindex+2;
      }
      return;
    }
    termindex = pctptr - abbrFilename;
    abbrFilename[termindex]= '\0';
    continue;
  } else { 
    break;
  }
  }//while
}

void
Logger::openLogFile(const char * const filename, const char * const gptr)
{
  char logFileName[LOGGER_MAX_LOG_NAME_SIZE];

  // Close the current log file
  if (m_logFile != NULL) {
    fclose(m_logFile);
  }  
  
  if (filename == NULL) {
    return;
  }
  
  // Remove the oldest log
  if (gptr == NULL) {
  SNPRINTF(logFileName, sizeof(logFileName), "%s.%d",
           filename, this->getMaxLogIndex() );
  } else {
  SNPRINTF(logFileName, sizeof(logFileName), "%s%d%s",
           filename, this->getMaxLogIndex(), gptr);
  }
  logFileName[sizeof(logFileName)-1] = '\0';
  remove(logFileName);
  
  // Slide the other logs down.
  // log8.txt -> log9.txt, log7.txt -> log8.txt ...
  for (int i = this->getMaxLogIndex(); i > 0; i--) {
    char logFileNameOld[LOGGER_MAX_LOG_NAME_SIZE];
    char logFileNameNew[LOGGER_MAX_LOG_NAME_SIZE];

    if (gptr == NULL) {
    SNPRINTF(logFileNameOld, sizeof(logFileNameOld), "%s.%d", filename, i - 1);
    SNPRINTF(logFileNameNew, sizeof(logFileNameNew), "%s.%d", filename, i);
    } else {
    SNPRINTF(logFileNameOld, sizeof(logFileNameOld), "%s%d%s", filename, i - 1, gptr);
    SNPRINTF(logFileNameNew, sizeof(logFileNameNew), "%s%d%s", filename, i, gptr);
    }
    logFileNameOld[sizeof(logFileNameOld)-1] = '\0';
    logFileNameNew[sizeof(logFileNameNew)-1] = '\0';
    rename(logFileNameOld, logFileNameNew);
  }

  // Open the current log
  if (gptr == NULL) {
  SNPRINTF(logFileName, sizeof(logFileName), "%s.%d", filename, 0 );
  } else {
  SNPRINTF(logFileName, sizeof(logFileName), "%s%d%s", filename, 0, gptr);
  }
  logFileName[sizeof(logFileName)-1] = '\0';
  m_logFile = fopen(logFileName, "w");
  if (m_logFile != NULL) {
  fprintf(m_logFile, "Log file opened.\n");
  fflush(m_logFile);
  } else {
  fprintf(stderr, "WARNING: Unable to open log file %s\n", logFileName);
  fflush(stderr);
  }
}

void
Logger::setMaxLogSize(const PRInt32 maxLogSize)
{
  m_monitor.enter();
    m_maximumLogSize = maxLogSize;
  m_monitor.exit();
}

PRInt32
Logger::getMaxLogSize() const
{
  return m_maximumLogSize;
}


void 
Logger::setLoggingCallback(MQLoggingFunc const loggingCallback, void * callbackData) 
{
  m_monitor.enter();
  m_loggingCallback = loggingCallback;
  m_callbackData = callbackData;
  m_monitor.exit();
}

MQLoggingFunc
Logger::getLoggingCallback() const 
{
  return m_loggingCallback;
}

LogLevel
Logger::getLogLevel()
{
  return g_logLevel;
}

void 
Logger::setLogFileLogLevel(const LogLevel logFileLogLevel) 
{
  m_monitor.enter();
    if (logFileLogLevel < MIN_LOG_LEVEL) {
      m_logFileLogLevel = MIN_LOG_LEVEL;
    } else if (logFileLogLevel > MAX_LOG_LEVEL) {
      m_logFileLogLevel = MAX_LOG_LEVEL;
    } else {
      m_logFileLogLevel = logFileLogLevel;
    }
    if (m_logFileLogLevel > g_logLevel) {
      g_logLevel = m_logFileLogLevel;
    }
  m_monitor.exit();
}

LogLevel 
Logger::getLogFileLogLevel() const 
{
  return m_logFileLogLevel;
}

void 
Logger::setCallbackLogLevel(const LogLevel callbackLogLevel) 
{
  m_monitor.enter();
    if (callbackLogLevel < MIN_LOG_LEVEL) {
      m_callbackLogLevel = MIN_LOG_LEVEL;
    } else if (callbackLogLevel > MAX_LOG_LEVEL) {
      m_callbackLogLevel = MAX_LOG_LEVEL;
    } else {
      m_callbackLogLevel = callbackLogLevel;
    }
    if (m_callbackLogLevel > g_logLevel) {
      g_logLevel = m_callbackLogLevel;
    }
  m_monitor.exit();
}

LogLevel 
Logger::getCallbackLogLevel() const 
{
  return m_callbackLogLevel;
}

void 
Logger::setStdErrLogLevel(const LogLevel stdErrLogLevel) 
{
  m_monitor.enter();
    if (stdErrLogLevel < MIN_LOG_LEVEL) {
      m_stdErrLogLevel = MIN_LOG_LEVEL;
    } else if (stdErrLogLevel > MAX_LOG_LEVEL) {
      m_stdErrLogLevel = MAX_LOG_LEVEL;
    } else {
      m_stdErrLogLevel = stdErrLogLevel;
    }
    if (m_stdErrLogLevel > g_logLevel) {
      g_logLevel = m_stdErrLogLevel;
    }
  m_monitor.exit();
}

LogLevel 
Logger::getStdErrLogLevel() const 
{
  return m_stdErrLogLevel;
}

void 
Logger::setMask(const LogLevel logLevel, const PRUint32 mask) 
{
  m_monitor.enter();
    if ((logLevel >= MIN_LOG_LEVEL) && (logLevel <= MAX_LOG_LEVEL)) {
      m_mask[logLevel] = mask;
    }
  m_monitor.exit();
}

PRUint32 
Logger::getMask(const LogLevel logLevel) const 
{
  if ((logLevel < MIN_LOG_LEVEL) || (logLevel > MAX_LOG_LEVEL)) {
    return 0;
  }

  return (m_mask[logLevel]);
}

PRInt32
Logger::getMaxLogIndex() const
{
  return LOGGER_DEFAULT_MAXIMUM_LOG_INDEX;
}

void
Logger::logva(const LogLevel       logLevel,
              const char *   const filename,
              const PRInt32        lineNumber,
              const PRUint32       mask,
              const PRInt64        connectionID,
              const LogCode        logCode,
              const char *   const format,
                    va_list        argptr) 
{
  if (logLevel < 0) {
    return;
  }
  //perf: we do not support changing these on-the-fly
  if ((m_loggingCallback == NULL 
       || logLevel > m_callbackLogLevel || m_callbackLogLevel < MIN_LOG_LEVEL)
      && (m_logFile == NULL 
          || logLevel > m_logFileLogLevel || m_logFileLogLevel < MIN_LOG_LEVEL)
      && (logLevel > m_stdErrLogLevel || m_stdErrLogLevel < MIN_LOG_LEVEL)) {
    return; 
  }

  m_monitor.enter();

  PRInt64 time = (PRInt64)PR_Now();

  // execute the callback immediately if necessary
  if ((m_loggingCallback != NULL)          && 
      (logLevel <= m_callbackLogLevel)     && 
      (m_callbackLogLevel >= MIN_LOG_LEVEL)) 
  {
    invokeLoggingCallback(logLevel, logCode, format, time, connectionID,
                       filename, lineNumber, m_loggingCallback, m_callbackData);
  }

  //// check the logLevel for the other outputs
  //if ((logLevel > m_logLevel) && (logLevel > m_stdErrLogLevel)) {
  //  m_monitor.exit();
  //  return;
  //}

  // check the mask
  if ((m_mask[logLevel] & mask) == 0) {
    m_monitor.exit();
    return;
  }

  // get the time
  PRExplodedTime et;
  PR_ExplodeTime(time, PR_LocalTimeParameters, &et);
  // increment the month - it's behind
  et.tm_month++;
  void * threadID = (void *)(PR_GetCurrentThread());

  // write the output string with its expanded args
  char outString[10000];
  PR_vsnprintf(outString, sizeof(outString), format, argptr);
  outString[sizeof(outString)-1] = '\0';

  const char* SEVERITY[] = {"SEVERE", "WARNING", "INFO", "CONFIG",
                            "FINE", "FINER", "FINEST"};

  // Eliminate the directory path of the file
  const char * abbrFilename = NULL;
  if (((abbrFilename = STRRCHR(filename, '/')) != NULL) ||
      ((abbrFilename = STRRCHR(filename, '\\')) != NULL))
  {
    abbrFilename = &(abbrFilename[1]); // skip past the last "/"
  } else {
    abbrFilename = filename;
  }

  // write the outstring to file or stderr
  if ((m_logFileLogLevel >= MIN_LOG_LEVEL) && 
      (logLevel  <= m_logFileLogLevel)    && 
      (m_logFile != NULL)) 
  {
    Long connectionIDLong(connectionID);
    this->openNewLogIfNecessary();


    fprintf(m_logFile, 
            "%-*s "
            "logCode=%-*d "
            "conn=%*s "
#if defined(AIX)
            "td=0x%p "
#else
            "td=0x%08p "
#endif
            "(%d/%02d/%02d %02d:%02d:%02d.%03d) "
            "%*s "
            "%*d "
            "        \"%s\"\n",
            MAX_PRINT_LOG_LEVEL_STR_LEN, SEVERITY[logLevel],
            MAX_PRINT_LOG_CODE_LEN, logCode,
            MAX_PRINT_CONN_ID_LEN, connectionIDLong.toString(),
            threadID,
            et.tm_year, et.tm_month, et.tm_mday, et.tm_hour, et.tm_min, et.tm_sec, et.tm_usec/1000,
            
            MAX_PRINT_FILE_NAME_LEN, abbrFilename,
            MAX_PRINT_LINE_NUMBER_LEN, lineNumber, 
            outString );

    /*    fprintf(m_logFile, 
            "%s: msgid=%d conn=%d td=0x%p "
            "(%d/%02d/%02d %02d:%02d:%02d.%03d) "
            "%s:%d \n"
            "    \"%s\"\n", 
            SEVERITY[logLevel], logCode, connectionID, threadID,
            et.tm_year, et.tm_month, et.tm_mday, 
            et.tm_hour, et.tm_min, et.tm_sec, et.tm_usec/1000,
            abbrFilename, lineNumber, 
            outString );*/
#define LOGGER_FFLUSH
#ifdef LOGGER_FFLUSH
    fflush(m_logFile);
#endif
  }
  if ((m_stdErrLogLevel >= MIN_LOG_LEVEL) && 
      (logLevel <= m_stdErrLogLevel) && 
      (m_logFile == NULL)) 
  {
    fprintf(stderr, 
#ifdef LOG_THREAD_INFO
            "%s: msgid=%d conn=%d td=0x%p "
            "(%d/%02d/%02d %02d:%02d:%02d.%06d) "
            "%s:%d \n"
#endif // LOG_THREAD_INFO
            "    \"%s\"\n", 
#ifdef LOG_THREAD_INFO
            SEVERITY[logLevel], logCode, connectionID, threadID,
            et.tm_year, et.tm_month, et.tm_mday, 
            et.tm_hour, et.tm_min, et.tm_sec, et.tm_usec/1000,
            abbrFilename, lineNumber, 
#endif // LOG_THREAD_INFO

            outString );
#ifdef LOGGER_FFLUSH
    fflush(stderr);
#endif
  }
  m_monitor.exit();
}

void
Logger::openNewLogIfNecessary()
{
  if (m_logFile != NULL) {
    long filePosition;
    if ((filePosition=ftell(m_logFile)) >=0 ) {
      if (filePosition > this->getMaxLogSize()) {
        openLogFile(m_logFileName, m_logFileName_gptr);
      }
    }
  }
}

void 
Logger::log(const LogLevel       logLevel,
            const char *   const filename,
            const PRInt32        lineNumber,
            const PRUint32       mask,
            const PRInt64        connectionID,
            const LogCode        logCode,
            const char *   const format,
            ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(logLevel,
        filename,
	lineNumber,
	mask,
	connectionID,
	logCode,
	format,
	argptr);
}

void 
Logger::log_Severe(const char *   const filename,
                   const PRInt32        lineNumber,
                   const PRUint32       mask,
                   const PRInt64        connectionID,
                   const LogCode        logCode,
                   const char *   const format,
                   ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(SEVERE_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

void 
Logger::log_Warning(const char *   const filename,
                    const PRInt32        lineNumber,
                    const PRUint32       mask,
                    const PRInt64        connectionID,
                    const LogCode        logCode,
                    const char *   const format,
                    ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(WARNING_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

void 
Logger::log_Info(const char *   const filename,
                 const PRInt32        lineNumber,
                 const PRUint32       mask,
                 const PRInt64        connectionID,
                 const LogCode        logCode,
                 const char *   const format,
                 ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(INFO_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);

}

void 
Logger::log_Config(const char *   const filename,
                   const PRInt32        lineNumber,
                   const PRUint32       mask,
                   const PRInt64        connectionID,
                   const LogCode        logCode,
                   const char *   const format,
                   ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(CONFIG_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

void 
Logger::log_Fine(const char *   const filename,
                 const PRInt32        lineNumber,
                 const PRUint32       mask,
                 const PRInt64        connectionID,
                 const LogCode        logCode,
                 const char *   const format,
                 ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(FINE_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

void 
Logger::log_Finer(const char *   const filename,
                  const PRInt32        lineNumber,
                  const PRUint32       mask,
                  const PRInt64        connectionID,
                  const LogCode        logCode,
                  const char *   const format,
                  ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(FINER_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

void 
Logger::log_Finest(const char *   const filename,
                   const PRInt32        lineNumber,
                   const PRUint32       mask,
                   const PRInt64        connectionID,
                   const LogCode        logCode,
                   const char *   const format,
                   ...) 
{
  va_list argptr;
  va_start(argptr, format);
  logva(FINEST_LOG_LEVEL,
        filename,
        lineNumber,
        mask,
        connectionID,
        logCode,
        format,
        argptr);
}

//
// Static methods
//

Logger * Logger::instance = NULL;

/*
 *
 */
Logger*
Logger::getInstance()
{
  if (instance == NULL) {
    instance = new Logger();
  }
  return instance;
}

void
Logger::deleteInstance()
{
  DELETE(instance);
}


// TESTING METHODS

extern "C" {
  void logSomething(void * arg);
}


const PRInt32 MAX_NUM_LOG_THREADS = 10000;

void 
Logger::test(PRInt32 numLogThreads, const PRInt32 itemsToLog)
{
  int i;
  int numItemsToLog = itemsToLog;
  PRThread* logThreads[MAX_NUM_LOG_THREADS];

  // Create the logging threads
  numLogThreads = MIN(numLogThreads, MAX_NUM_LOG_THREADS);
  for (i = 0; i < numLogThreads; i++) {
    logThreads[i] = PR_CreateThread(PR_SYSTEM_THREAD, logSomething, (void*)&numItemsToLog,  
                                    PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
  }

  // Wait for the logging threads to finish
  for (i = 0; i < numLogThreads; i++) {
    if (logThreads[i] != NULL) {
      PR_JoinThread(logThreads[i]);
    }
  }
}



#define SOME_LOG_MASK 0x00000001

void 
logSomething(void * arg) 
{
  PRInt32 itemsToLog = *(PRInt32*)arg;
  int j;
  
  for (j = 0; j < itemsToLog; j++) {
    LOG_FINE(( CODELOC, SOME_LOG_MASK, 1, j, "Some error. %d", j ));
    LOG_FINER(( CODELOC, SOME_LOG_MASK, 1, j, "This line should not appear." ));

    PR_Sleep(0);  // Yield
  }
}






© 2015 - 2025 Weber Informatics LLC | Privacy Policy