static {
    ClassLoader prevLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(TCLoggingLog4J.class.getClassLoader());

    Log4jSafeInit.init();

    Logger customerLogger = Logger.getLogger(CUSTOMER_LOGGER_NAMESPACE);
    Logger consoleLogger = Logger.getLogger(CONSOLE_LOGGER_NAME);

    console = new TCLoggerImpl(CONSOLE_LOGGER_NAME);
    consoleAppender =
        new TCConsoleAppender(new PatternLayout(CONSOLE_PATTERN), ConsoleAppender.SYSTEM_OUT);
    operatorEventLogger = new TCLoggerImpl(OPERATOR_EVENT_LOGGER_NAME);

    List<Logger> internalLoggers = new ArrayList<Logger>();
    for (String nameSpace : INTERNAL_LOGGER_NAMESPACES) {
      internalLoggers.add(Logger.getLogger(nameSpace));
    }

    /**
     * Don't add consoleLogger to allLoggers because it's a child of customerLogger, so it shouldn't
     * get any appenders. If you DO add consoleLogger here, you'll see duplicate messages in the log
     * file.
     */
    allLoggers = createAllLoggerList(internalLoggers, customerLogger);

    try {
      boolean customLogging = customConfiguration();
      boolean isDev = customLogging ? false : developmentConfiguration();

      if (!customLogging) {
        for (Logger internalLogger : internalLoggers) {
          internalLogger.setLevel(Level.INFO);
        }
        customerLogger.setLevel(Level.INFO);
        consoleLogger.setLevel(Level.INFO);

        if (!isDev) {
          // Only the console logger goes to the console (by default)
          consoleLogger.addAppender(consoleAppender);
        } else {
          consoleAppender.setLayout(new PatternLayout(CONSOLE_PATTERN_DEVELOPMENT));
          // For non-customer environments, send all logging to the console...
          Logger.getRootLogger().addAppender(consoleAppender);
        }
      }

      delegateFileAppender = new DelegatingAppender(new NullAppender());
      addToAllLoggers(delegateFileAppender);

      BufferingAppender realBufferingAppender;
      realBufferingAppender = new BufferingAppender(MAX_BUFFERED_LOG_MESSAGES);
      realBufferingAppender.setName("buffering appender");
      delegateBufferingAppender = new DelegatingAppender(realBufferingAppender);
      addToAllLoggers(delegateBufferingAppender);
      buffering = true;

      if (!isDev) {
        console.info("New logging session started.");
      }

      writeVersion();
      writePID();
      writeLoggingConfigurations();
    } finally {
      Thread.currentThread().setContextClassLoader(prevLoader);
    }
  }
  @SuppressWarnings("resource")
  public void setLogDirectory(File theDirectory, int processType) {
    Assert.assertNotNull(theDirectory);

    if (theDirectory.getName().trim().equalsIgnoreCase("stdout:")
        || theDirectory.getName().trim().equalsIgnoreCase("stderr:")) {
      if (currentLoggingDirectory != null
          && currentLoggingDirectory.getName().trim().equalsIgnoreCase(theDirectory.getName())) {
        // Nothing to do; great!
        return;
      }

      delegateFileAppender.setDelegate(new NullAppender());
      consoleAppender.setLayout(new PatternLayout(CONSOLE_LOGGING_ONLY_PATTERN));

      // Logger.addAppender() doesn't double-add, so this is safe
      Logger.getRootLogger().addAppender(consoleAppender);

      if (buffering) {
        BufferingAppender realBufferingAppender =
            (BufferingAppender) delegateBufferingAppender.setDelegate(new NullAppender());
        realBufferingAppender.stopAndSendContentsTo(consoleAppender);
        realBufferingAppender.close();
        buffering = false;
      }

      boolean stdout = theDirectory.getName().trim().equalsIgnoreCase("stdout:");
      getConsoleLogger()
          .info(
              "All logging information now output to standard "
                  + (stdout ? "output" : "error")
                  + ".");

      return;
    }

    synchronized (TCLoggingLog4J.class) {
      if (currentLoggingDirectory != null) {
        try {
          if (theDirectory.getCanonicalPath().equals(currentLoggingDirectory.getCanonicalPath())) {
            return;
          }
        } catch (IOException ioe) {
          // oh, well -- what can we do? we'll continue on.
        }
      }
    }

    try {
      FileUtils.forceMkdir(theDirectory);
    } catch (IOException ioe) {
      reportLoggingError(
          "We can't create the directory '"
              + theDirectory.getAbsolutePath()
              + "' that you specified for your logs.",
          ioe);
      return;
    }

    if (!theDirectory.canWrite()) {
      // formatting
      reportLoggingError(
          "The log directory, '" + theDirectory.getAbsolutePath() + "', can't be written to.",
          null);
      return;
    }

    FileLock thisDirectoryLock = null;

    if (!lockingDisabled) {
      File lockFile = new File(theDirectory, LOCK_FILE_NAME);

      try {
        lockFile.createNewFile();
        Assert.eval(lockFile.exists());
        FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel();
        thisDirectoryLock = channel.tryLock();

        if (thisDirectoryLock == null) {
          reportLoggingError(
              "The log directory, '"
                  + theDirectory.getAbsolutePath()
                  + "', is already in use by another "
                  + "Terracotta process. Logging will proceed to the console only.",
              null);
          return;
        }

      } catch (OverlappingFileLockException ofle) {
        // This VM already holds the lock; no problem
      } catch (IOException ioe) {
        reportLoggingError(
            "We can't lock the file '"
                + lockFile.getAbsolutePath()
                + "', to make sure that only one "
                + "Terracotta process is using this directory for logging. This may be a permission "
                + "issue, or some unexpected error. Logging will proceed to the console only.",
            ioe);
        return;
      }
    }

    RollingFileAppender newFileAppender;

    String logFileName;

    switch (processType) {
      case PROCESS_TYPE_L1:
        logFileName = TERRACOTTA_L1_LOG_FILE_NAME;
        break;

      case PROCESS_TYPE_L2:
        logFileName = TERRACOTTA_L2_LOG_FILE_NAME;
        break;

      case PROCESS_TYPE_GENERIC:
        logFileName = TERRACOTTA_GENERIC_LOG_FILE_NAME;
        break;

      default:
        throw Assert.failure("Unknown process type: " + processType);
    }

    String logFilePath = new File(theDirectory, logFileName).getAbsolutePath();

    synchronized (TCLoggingLog4J.class) {
      try {
        TCProperties props =
            TCPropertiesImpl.getProperties().getPropertiesFor(TCPropertiesConsts.LOGGING_CATEGORY);
        newFileAppender =
            new TCRollingFileAppender(new PatternLayout(FILE_AND_JMX_PATTERN), logFilePath, true);
        newFileAppender.setName("file appender");
        int maxLogFileSize = props.getInt(MAX_LOG_FILE_SIZE_PROPERTY, DEFAULT_MAX_LOG_FILE_SIZE);
        newFileAppender.setMaxFileSize(maxLogFileSize + "MB");
        newFileAppender.setMaxBackupIndex(props.getInt(MAX_BACKUPS_PROPERTY, DEFAULT_MAX_BACKUPS));

        // This makes us start with a new file each time.
        newFileAppender.rollOver();

        // Note: order of operations is very important here. We start the new appender before we
        // close and remove the
        // old one so that you don't drop any log records.
        Appender oldFileAppender = delegateFileAppender.setDelegate(newFileAppender);

        if (oldFileAppender != null) {
          oldFileAppender.close();
        }

        if (buffering) {
          BufferingAppender realBufferingAppender =
              (BufferingAppender) delegateBufferingAppender.setDelegate(new NullAppender());
          realBufferingAppender.stopAndSendContentsTo(delegateFileAppender);
          realBufferingAppender.close();
          buffering = false;
        }

        currentLoggingDirectory = theDirectory;

        if (currentLoggingDirectoryFileLock != null) currentLoggingDirectoryFileLock.release();
        currentLoggingDirectoryFileLock = thisDirectoryLock;
      } catch (IOException ioe) {
        reportLoggingError(
            "We were unable to switch the logging system to log to '" + logFilePath + "'.", ioe);
      }
    }

    getConsoleLogger().info("Log file: '" + logFilePath + "'.");
    writeSystemProperties();
  }