Switch to SimpleLogHandler in Bazel.

The new log handler changes java.log to a symlink to the latest log file. Old
log files will be garbage collected to keep the total size under a limit.

As a temporary "escape hatch", in case switching server log handlers causes
problems, the old non-rotating log handler can be restored using the
--norotating_server_log startup option. This option (and the
BazelServerLogModule providing it) will be removed once it's clear that an
"escape hatch" is not needed.

RELNOTES: Use different server log files per Bazel server process; java.log is
now a symlink to the latest log.
PiperOrigin-RevId: 219512757
diff --git a/src/main/cpp/startup_options.cc b/src/main/cpp/startup_options.cc
index 21f7045..1267384 100644
--- a/src/main/cpp/startup_options.cc
+++ b/src/main/cpp/startup_options.cc
@@ -84,6 +84,7 @@
       oom_more_eagerly(false),
       oom_more_eagerly_threshold(100),
       write_command_log(true),
+      rotating_server_log(true),
       watchfs(false),
       fatal_event_bus_exceptions(false),
       command_port(0),
@@ -142,6 +143,7 @@
   RegisterNullaryStartupFlag("unlimit_coredumps");
   RegisterNullaryStartupFlag("watchfs");
   RegisterNullaryStartupFlag("write_command_log");
+  RegisterNullaryStartupFlag("rotating_server_log");
   RegisterUnaryStartupFlag("command_port");
   RegisterUnaryStartupFlag("connect_timeout_secs");
   RegisterUnaryStartupFlag("digest_function");
@@ -322,6 +324,12 @@
   } else if (GetNullaryOption(arg, "--nowrite_command_log")) {
     write_command_log = false;
     option_sources["write_command_log"] = rcfile;
+  } else if (GetNullaryOption(arg, "--rotating_server_log")) {
+    rotating_server_log = true;
+    option_sources["rotating_server_log"] = rcfile;
+  } else if (GetNullaryOption(arg, "--norotating_server_log")) {
+    rotating_server_log = false;
+    option_sources["rotating_server_log"] = rcfile;
   } else if (GetNullaryOption(arg, "--watchfs")) {
     watchfs = true;
     option_sources["watchfs"] = rcfile;
@@ -542,29 +550,58 @@
   return AddJVMMemoryArguments(server_javabase, result, user_options, error);
 }
 
+static std::string GetJavaUtilLoggingFileHandlerProps(
+    const std::string &java_log, const std::string &java_logging_formatter) {
+  return "handlers=java.util.logging.FileHandler\n"
+         ".level=INFO\n"
+         "java.util.logging.FileHandler.level=INFO\n"
+         "java.util.logging.FileHandler.pattern=" +
+         java_log +
+         "\n"
+         "java.util.logging.FileHandler.limit=1024000\n"
+         "java.util.logging.FileHandler.count=1\n"
+         "java.util.logging.FileHandler.formatter=" +
+         java_logging_formatter + "\n";
+}
+
+static std::string GetSimpleLogHandlerProps(
+    const std::string &java_log, const std::string &java_logging_formatter) {
+  return "handlers=com.google.devtools.build.lib.util.SimpleLogHandler\n"
+         ".level=INFO\n"
+         "com.google.devtools.build.lib.util.SimpleLogHandler.level=INFO\n"
+         "com.google.devtools.build.lib.util.SimpleLogHandler.prefix=" +
+         java_log +
+         "\n"
+         "com.google.devtools.build.lib.util.SimpleLogHandler.limit=1024000\n"
+         "com.google.devtools.build.lib.util.SimpleLogHandler.total_limit="
+         "20971520\n"  // 20 MB.
+         "com.google.devtools.build.lib.util.SimpleLogHandler.formatter=" +
+         java_logging_formatter + "\n";
+}
+
 void StartupOptions::AddJVMLoggingArguments(std::vector<string> *result) const {
   // Configure logging
   const string propFile = blaze_util::PathAsJvmFlag(
       blaze_util::JoinPath(output_base, "javalog.properties"));
   const string java_log(
       blaze_util::PathAsJvmFlag(blaze_util::JoinPath(output_base, "java.log")));
-  if (!blaze_util::WriteFile("handlers=java.util.logging.FileHandler\n"
-                             ".level=INFO\n"
-                             "java.util.logging.FileHandler.level=INFO\n"
-                             "java.util.logging.FileHandler.pattern=" +
-                                 java_log +
-                                 "\n"
-                                 "java.util.logging.FileHandler.limit=1024000\n"
-                                 "java.util.logging.FileHandler.count=1\n"
-                                 "java.util.logging.FileHandler.formatter=" +
-                                 java_logging_formatter + "\n",
-                             propFile)) {
+  const std::string loggingProps =
+      rotating_server_log
+          ? GetSimpleLogHandlerProps(java_log, java_logging_formatter)
+          : GetJavaUtilLoggingFileHandlerProps(java_log,
+                                               java_logging_formatter);
+  const std::string loggingQuerierClass =
+      rotating_server_log
+          ? "com.google.devtools.build.lib.util.SimpleLogHandler$HandlerQuerier"
+          : "com.google.devtools.build.lib.util.FileHandlerQuerier";
+
+  if (!blaze_util::WriteFile(loggingProps, propFile)) {
     perror(("Couldn't write logging file " + propFile).c_str());
   } else {
     result->push_back("-Djava.util.logging.config.file=" + propFile);
     result->push_back(
-        "-Dcom.google.devtools.build.lib.util.LogHandlerQuerier.class="
-        "com.google.devtools.build.lib.util.FileHandlerQuerier");
+        "-Dcom.google.devtools.build.lib.util.LogHandlerQuerier.class=" +
+        loggingQuerierClass);
   }
 }
 
diff --git a/src/main/cpp/startup_options.h b/src/main/cpp/startup_options.h
index 10492b7..fe90a9e 100644
--- a/src/main/cpp/startup_options.h
+++ b/src/main/cpp/startup_options.h
@@ -257,6 +257,15 @@
 
   bool write_command_log;
 
+  // If true, Bazel will use SimpleLogHandler for the server log, meaning that
+  // each time a Bazel server process starts, it will create a new log file and
+  // update java.log to be a symbolic link to the new log file.
+  // TODO(b/118755753): The --norotating_server_log option is intended as a
+  // temporary "escape hatch" in case switching to the rotating ServerLogHandler
+  // breaks things. Remove the option and associated logic once we are confident
+  // that the "escape hatch" is not needed.
+  bool rotating_server_log;
+
   // If true, Blaze will listen to OS-level file change notifications.
   bool watchfs;
 
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 2321003..3f6c331 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -1505,6 +1505,7 @@
         ":bazel-main",
         ":server",
         ":single-line-formatter",  # See startup_options.cc
+        "//src/main/java/com/google/devtools/build/lib:simple-log-handler",  # See startup_options.cc
     ],
 )
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
index 96fa9a8..029c1ac 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/Bazel.java
@@ -61,6 +61,7 @@
           com.google.devtools.build.lib.profiler.callcounts.CallcountsModule.class,
           com.google.devtools.build.lib.profiler.memory.AllocationTrackerModule.class,
           com.google.devtools.build.lib.metrics.MetricsModule.class,
+          com.google.devtools.build.lib.bazel.BazelServerLogModule.class,
           BazelBuiltinCommandModule.class);
 
   public static void main(String[] args) {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelServerLogModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelServerLogModule.java
new file mode 100644
index 0000000..80ff7d5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelServerLogModule.java
@@ -0,0 +1,47 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.bazel;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.runtime.BlazeModule;
+import com.google.devtools.common.options.Option;
+import com.google.devtools.common.options.OptionDocumentationCategory;
+import com.google.devtools.common.options.OptionEffectTag;
+import com.google.devtools.common.options.OptionsBase;
+
+/** Provides startup options for the Bazel server log handler. */
+public class BazelServerLogModule extends BlazeModule {
+  /** Logging flags. */
+  public static final class Options extends OptionsBase {
+    // TODO(b/118755753): The --norotating_server_log option is intended as a temporary "escape
+    // hatch" in case switching to the rotating ServerLogHandler breaks things. Remove the option
+    // and associated logic once we are confident that the "escape hatch" is not needed.
+    @Option(
+        name = "rotating_server_log",
+        defaultValue = "true", // NOTE: purely decorative, rc files are read by the client.
+        documentationCategory = OptionDocumentationCategory.BAZEL_CLIENT_OPTIONS,
+        effectTags = {OptionEffectTag.BAZEL_MONITORING},
+        help =
+            "Create a new log file when the %{product} server process (re)starts, and update the "
+                + "java.log symbolic link to point to the new file. Otherwise, java.log would be a "
+                + "regular file which would be overwritten each time the server process starts.")
+    public boolean rotatingServerLog;
+  }
+
+  @Override
+  public Iterable<Class<? extends OptionsBase>> getStartupOptions() {
+    return ImmutableList.of(Options.class);
+  }
+}