changeset 9386:f99ad7bb5df5

8046148: JEP 158: Unified JVM Logging Reviewed-by: coleenp, sla
author mlarsson
date Thu, 24 Sep 2015 12:36:04 +0200
parents 1ce8347eea86
children 1f6500dbefcb
files make/windows/makefiles/vm.make src/share/vm/Xusage.txt src/share/vm/logging/log.hpp src/share/vm/logging/logConfiguration.cpp src/share/vm/logging/logConfiguration.hpp src/share/vm/logging/logDecorations.cpp src/share/vm/logging/logDecorations.hpp src/share/vm/logging/logDecorators.cpp src/share/vm/logging/logDecorators.hpp src/share/vm/logging/logDiagnosticCommand.cpp src/share/vm/logging/logDiagnosticCommand.hpp src/share/vm/logging/logFileOutput.cpp src/share/vm/logging/logFileOutput.hpp src/share/vm/logging/logFileStreamOutput.cpp src/share/vm/logging/logFileStreamOutput.hpp src/share/vm/logging/logLevel.cpp src/share/vm/logging/logLevel.hpp src/share/vm/logging/logOutput.cpp src/share/vm/logging/logOutput.hpp src/share/vm/logging/logOutputList.cpp src/share/vm/logging/logOutputList.hpp src/share/vm/logging/logPrefix.hpp src/share/vm/logging/logTag.cpp src/share/vm/logging/logTag.hpp src/share/vm/logging/logTagLevelExpression.cpp src/share/vm/logging/logTagLevelExpression.hpp src/share/vm/logging/logTagSet.cpp src/share/vm/logging/logTagSet.hpp src/share/vm/memory/allocation.hpp src/share/vm/precompiled/precompiled.hpp src/share/vm/runtime/arguments.cpp src/share/vm/runtime/mutexLocker.cpp src/share/vm/runtime/mutexLocker.hpp src/share/vm/runtime/thread.cpp src/share/vm/services/management.hpp src/share/vm/services/nmtCommon.cpp src/share/vm/utilities/ostream.cpp src/share/vm/utilities/ostream.hpp test/serviceability/logging/TestBasicLogOutput.java
diffstat 39 files changed, 2842 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/make/windows/makefiles/vm.make	Wed Sep 23 22:04:23 2015 +0300
+++ b/make/windows/makefiles/vm.make	Thu Sep 24 12:36:04 2015 +0200
@@ -163,6 +163,7 @@
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc/cms
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc/g1
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/asm
+VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/logging
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/memory
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/oops
 VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/prims
@@ -250,6 +251,9 @@
 {$(COMMONSRC)\share\vm\asm}.cpp.obj::
         $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
 
+{$(COMMONSRC)\share\vm\logging}.cpp.obj::
+        $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
+
 {$(COMMONSRC)\share\vm\memory}.cpp.obj::
         $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
 
@@ -330,6 +334,9 @@
 {$(ALTSRC)\share\vm\asm}.cpp.obj::
         $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
 
+{$(ALTSRC)\share\vm\logging}.cpp.obj::
+        $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
+
 {$(ALTSRC)\share\vm\memory}.cpp.obj::
         $(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
 
--- a/src/share/vm/Xusage.txt	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/Xusage.txt	Thu Sep 24 12:36:04 2015 +0200
@@ -7,6 +7,7 @@
     -Xbootclasspath/p:<directories and zip/jar files separated by ;>
                       prepend in front of bootstrap class path
     -Xnoclassgc       disable class garbage collection
+    -Xlog:<opts>      control JVM logging, use -Xlog:help for details
     -Xloggc:<file>    log GC status to a file with time stamps
     -Xbatch           disable background compilation
     -Xms<size>        set initial Java heap size
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/log.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOG_HPP
+#define SHARE_VM_LOGGING_LOG_HPP
+
+#include "logging/logLevel.hpp"
+#include "logging/logPrefix.hpp"
+#include "logging/logTagSet.hpp"
+#include "logging/logTag.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/ostream.hpp"
+
+//
+// Logging macros
+//
+// Usage:
+//   log_<level>(<comma separated log tags>)(<printf-style log arguments>);
+// e.g.
+//   log_debug(logging)("message %d", i);
+//
+// Note that these macros will not evaluate the arguments unless the logging is enabled.
+//
+#define log_error(...)   (!log_is_enabled(Error, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Error>
+#define log_warning(...) (!log_is_enabled(Warning, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Warning>
+#define log_info(...)    (!log_is_enabled(Info, __VA_ARGS__))    ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
+#define log_debug(...)   (!log_is_enabled(Debug, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
+#define log_trace(...)   (!log_is_enabled(Trace, __VA_ARGS__))   ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
+#ifndef PRODUCT
+#define log_develop(...) (!log_is_enabled(Develop, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Develop>
+#else
+#define DUMMY_ARGUMENT_CONSUMER(...)
+#define log_develop(...) DUMMY_ARGUMENT_CONSUMER
+#endif
+
+// Convenience macro to test if the logging is enabled on the specified level for given tags.
+#define log_is_enabled(level, ...) (Log<LOG_TAGS(__VA_ARGS__)>::is_level(LogLevel::level))
+
+//
+// Log class for more advanced logging scenarios.
+// Has printf-style member functions for each log level (trace(), debug(), etc).
+//
+// Also has outputStream compatible API for the different log-levels.
+// The streams are resource allocated when requested and are accessed through
+// calls to <level>_stream() functions (trace_stream(), debug_stream(), etc).
+//
+// Example usage:
+//   LogHandle(logging) log;
+//   if (log.is_debug()) {
+//     ...
+//     log.debug("result = %d", result).trace(" tracing info");
+//     obj->print_on(log.debug_stream());
+//   }
+//
+#define LogHandle(...)  Log<LOG_TAGS(__VA_ARGS__)>
+
+template <LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG, LogTagType T3 = LogTag::__NO_TAG,
+          LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
+class Log VALUE_OBJ_CLASS_SPEC {
+ private:
+  static const size_t LogBufferSize = 512;
+ public:
+  // Make sure no more than the maximum number of tags have been given.
+  // The GuardTag allows this to be detected if/when it happens. If the GuardTag
+  // is not __NO_TAG, the number of tags given exceeds the maximum allowed.
+  STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported!
+
+  static bool is_level(LogLevelType level) {
+    return LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().is_level(level);
+  }
+
+  template <LogLevelType Level>
+  ATTRIBUTE_PRINTF(1, 2)
+  static void write(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    vwrite<Level>(fmt, args);
+    va_end(args);
+  };
+
+  template <LogLevelType Level>
+  ATTRIBUTE_PRINTF(1, 0)
+  static void vwrite(const char* fmt, va_list args) {
+    char buf[LogBufferSize];
+    size_t prefix_len = LogPrefix<T0, T1, T2, T3, T4>::prefix(buf, sizeof(buf));
+    int ret = vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, fmt, args);
+    assert(ret >= 0 && (size_t)ret < sizeof(buf), "Log message too long");
+    puts<Level>(buf);
+  }
+
+  template <LogLevelType Level>
+  static void puts(const char* string) {
+    LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().log(Level, string);
+  }
+
+#define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \
+  Log& v##name(const char* fmt, va_list args) { \
+    vwrite<LogLevel::level>(fmt, args); \
+    return *this; \
+  } \
+  Log& name(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3) { \
+    va_list args; \
+    va_start(args, fmt); \
+    vwrite<LogLevel::level>(fmt, args); \
+    va_end(args); \
+    return *this; \
+  } \
+  static bool is_##name() { \
+    return is_level(LogLevel::level); \
+  } \
+  static outputStream* name##_stream() { \
+    return new logStream(write<LogLevel::level>); \
+  }
+  LOG_LEVEL_LIST
+#undef LOG_LEVEL
+};
+
+#endif // SHARE_VM_LOGGING_LOG_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logConfiguration.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/log.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logDecorations.hpp"
+#include "logging/logDecorators.hpp"
+#include "logging/logDiagnosticCommand.hpp"
+#include "logging/logFileOutput.hpp"
+#include "logging/logOutput.hpp"
+#include "logging/logTagLevelExpression.hpp"
+#include "logging/logTagSet.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/os.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+LogOutput** LogConfiguration::_outputs = NULL;
+size_t      LogConfiguration::_n_outputs = 0;
+
+void LogConfiguration::post_initialize() {
+  assert(LogConfiguration_lock != NULL, "Lock must be initialized before post-initialization");
+  LogDiagnosticCommand::registerCommand();
+  LogHandle(logging) log;
+  log.info("Log configuration fully initialized.");
+  if (log.is_trace()) {
+    ResourceMark rm;
+    MutexLocker ml(LogConfiguration_lock);
+    describe(log.trace_stream());
+  }
+}
+
+void LogConfiguration::initialize(jlong vm_start_time) {
+  LogFileOutput::set_file_name_parameters(vm_start_time);
+  LogDecorations::set_vm_start_time_millis(vm_start_time);
+
+  assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
+  _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
+  _outputs[0] = LogOutput::Stdout;
+  _outputs[1] = LogOutput::Stderr;
+  _n_outputs = 2;
+}
+
+void LogConfiguration::finalize() {
+  for (size_t i = 2; i < _n_outputs; i++) {
+    delete _outputs[i];
+  }
+  FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
+}
+
+size_t LogConfiguration::find_output(const char* name) {
+  for (size_t i = 0; i < _n_outputs; i++) {
+    if (strcmp(_outputs[i]->name(), name) == 0) {
+      return i;
+    }
+  }
+  return SIZE_MAX;
+}
+
+LogOutput* LogConfiguration::new_output(char* name, const char* options) {
+  const char* type;
+  char* equals_pos = strchr(name, '=');
+  if (equals_pos == NULL) {
+    type = "file";
+  } else {
+    *equals_pos = '\0';
+    type = name;
+    name = equals_pos + 1;
+  }
+
+  LogOutput* output;
+  if (strcmp(type, "file") == 0) {
+    output = new LogFileOutput(name);
+  } else {
+    // unsupported log output type
+    return NULL;
+  }
+
+  bool success = output->initialize(options);
+  if (!success) {
+    delete output;
+    return NULL;
+  }
+  return output;
+}
+
+size_t LogConfiguration::add_output(LogOutput* output) {
+  size_t idx = _n_outputs++;
+  _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
+  _outputs[idx] = output;
+  return idx;
+}
+
+void LogConfiguration::delete_output(size_t idx) {
+  assert(idx > 1 && idx < _n_outputs,
+         err_msg("idx must be in range 1 < idx < _n_outputs, but idx = " SIZE_FORMAT
+                 " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs));
+  LogOutput* output = _outputs[idx];
+  // Swap places with the last output and shrink the array
+  _outputs[idx] = _outputs[--_n_outputs];
+  _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
+  delete output;
+}
+
+void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) {
+  assert(idx < _n_outputs, err_msg("Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs));
+  LogOutput* output = _outputs[idx];
+  output->set_decorators(decorators);
+  output->set_config_string(tag_level_expression.to_string());
+  bool enabled = false;
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    LogLevelType level = tag_level_expression.level_for(*ts);
+    if (level != LogLevel::Off) {
+      enabled = true;
+    }
+    ts->update_decorators(decorators);
+    ts->set_output_level(output, level);
+  }
+
+  // If the output is not used by any tagset it should be removed, unless it is stdout/stderr.
+  if (!enabled && idx > 1) {
+    delete_output(idx);
+  }
+}
+
+void LogConfiguration::disable_output(size_t idx) {
+  LogOutput* out = _outputs[idx];
+  LogDecorators empty_decorators;
+  empty_decorators.clear();
+
+  // Remove the output from all tagsets.
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    ts->set_output_level(out, LogLevel::Off);
+    ts->update_decorators(empty_decorators);
+  }
+
+  // Delete the output unless stdout/stderr
+  if (out != LogOutput::Stderr && out != LogOutput::Stdout) {
+    delete_output(find_output(out->name()));
+  } else {
+    out->set_config_string("all=off");
+  }
+}
+
+void LogConfiguration::disable_logging() {
+  assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
+         "LogConfiguration lock must be held when calling this function");
+  for (size_t i = 0; i < _n_outputs; i++) {
+    disable_output(i);
+  }
+}
+
+bool LogConfiguration::parse_command_line_arguments(const char* opts) {
+  char* copy = os::strdup_check_oom(opts, mtLogging);
+
+  // Split the option string to its colon separated components.
+  char* what = NULL;
+  char* output_str = NULL;
+  char* decorators_str = NULL;
+  char* output_options = NULL;
+
+  what = copy;
+  char* colon = strchr(what, ':');
+  if (colon != NULL) {
+    *colon = '\0';
+    output_str = colon + 1;
+    colon = strchr(output_str, ':');
+    if (colon != NULL) {
+      *colon = '\0';
+      decorators_str = colon + 1;
+      colon = strchr(decorators_str, ':');
+      if (colon != NULL) {
+        *colon = '\0';
+        output_options = colon + 1;
+      }
+    }
+  }
+
+  // Parse each argument
+  char errbuf[512];
+  stringStream ss(errbuf, sizeof(errbuf));
+  bool success = parse_log_arguments(output_str, what, decorators_str, output_options, &ss);
+  if (!success) {
+    errbuf[strlen(errbuf) - 1] = '\0'; // Strip trailing newline.
+    log_error(logging)("%s", errbuf);
+  }
+
+  os::free(copy);
+  return success;
+}
+
+bool LogConfiguration::parse_log_arguments(const char* outputstr,
+                                           const char* what,
+                                           const char* decoratorstr,
+                                           const char* output_options,
+                                           outputStream* errstream) {
+  assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
+         "LogConfiguration lock must be held when calling this function");
+  if (outputstr == NULL || strlen(outputstr) == 0) {
+    outputstr = "stdout";
+  }
+
+  size_t idx;
+  if (outputstr[0] == '#') {
+    int ret = sscanf(outputstr+1, SIZE_FORMAT, &idx);
+    if (ret != 1 || idx >= _n_outputs) {
+      errstream->print_cr("Invalid output index '%s'", outputstr);
+      return false;
+    }
+  } else {
+    idx = find_output(outputstr);
+    if (idx == SIZE_MAX) {
+      char* tmp = os::strdup_check_oom(outputstr, mtLogging);
+      LogOutput* output = new_output(tmp, output_options);
+      os::free(tmp);
+      if (output == NULL) {
+        errstream->print("Unable to add output '%s'", outputstr);
+        if (output_options != NULL && strlen(output_options) > 0) {
+          errstream->print(" with options '%s'", output_options);
+        }
+        errstream->cr();
+        return false;
+      }
+      idx = add_output(output);
+    } else if (output_options != NULL && strlen(output_options) > 0) {
+      errstream->print_cr("Output options for existing outputs are ignored.");
+    }
+  }
+
+  LogTagLevelExpression expr;
+  if (!expr.parse(what, errstream)) {
+    return false;
+  }
+
+  LogDecorators decorators;
+  if (!decorators.parse(decoratorstr, errstream)) {
+    return false;
+  }
+
+  configure_output(idx, expr, decorators);
+  return true;
+}
+
+void LogConfiguration::describe(outputStream* out) {
+  assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
+         "LogConfiguration lock must be held when calling this function");
+
+  out->print("Available log levels:");
+  for (size_t i = 0; i < LogLevel::Count; i++) {
+    out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
+  }
+  out->cr();
+
+  out->print("Available log decorators:");
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
+    out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
+  }
+  out->cr();
+
+  out->print("Available log tags:");
+  for (size_t i = 1; i < LogTag::Count; i++) {
+    out->print("%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
+  }
+  out->cr();
+
+  out->print_cr("Log output configuration:");
+  for (size_t i = 0; i < _n_outputs; i++) {
+    out->print("#" SIZE_FORMAT ": %s %s ", i, _outputs[i]->name(), _outputs[i]->config_string());
+    for (size_t d = 0; d < LogDecorators::Count; d++) {
+      LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);
+      if (_outputs[i]->decorators().is_decorator(decorator)) {
+        out->print("%s,", LogDecorators::name(decorator));
+      }
+    }
+    out->cr();
+  }
+}
+
+void LogConfiguration::print_command_line_help(FILE* out) {
+  jio_fprintf(out, "-Xlog Usage: -Xlog[:[what][:[output][:[decorators][:output-options]]]]\n"
+              "\t where 'what' is a combination of tags and levels on the form tag1[+tag2...][*][=level][,...]\n"
+              "\t Unless wildcard (*) is specified, only log messages tagged with exactly the tags specified will be matched.\n\n");
+
+  jio_fprintf(out, "Available log levels:\n");
+  for (size_t i = 0; i < LogLevel::Count; i++) {
+    jio_fprintf(out, "%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
+  }
+
+  jio_fprintf(out, "\n\nAvailable log decorators: \n");
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
+    jio_fprintf(out, "%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
+  }
+  jio_fprintf(out, "\n Decorators can also be specified as 'none' for no decoration.\n\n");
+
+  jio_fprintf(out, "Available log tags:\n");
+  for (size_t i = 1; i < LogTag::Count; i++) {
+    jio_fprintf(out, "%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
+  }
+  jio_fprintf(out, "\n Specifying 'all' instead of a tag combination matches all tag combinations.\n\n");
+
+  jio_fprintf(out, "Available log outputs:\n"
+              " stdout, stderr, file=<filename>\n"
+              " Specifying %%p and/or %%t in the filename will expand to the JVM's PID and startup timestamp, respectively.\n\n"
+
+              "Some examples:\n"
+              " -Xlog\n"
+              "\t Log all messages using 'info' level to stdout with 'uptime', 'levels' and 'tags' decorations.\n"
+              "\t (Equivalent to -Xlog:all=info:stdout:uptime,levels,tags).\n\n"
+
+              " -Xlog:gc\n"
+              "\t Log messages tagged with 'gc' tag using 'info' level to stdout, with default decorations.\n\n"
+
+              " -Xlog:gc=debug:file=gc.txt:none\n"
+              "\t Log messages tagged with 'gc' tag using 'debug' level to file 'gc.txt' with no decorations.\n\n"
+
+              " -Xlog:gc=trace:file=gctrace.txt:uptimemillis,pids:filecount=5,filesize=1024\n"
+              "\t Log messages tagged with 'gc' tag using 'trace' level to a rotating fileset of 5 files of size 1MB,\n"
+              "\t using the base name 'gctrace.txt', with 'uptimemillis' and 'pid' decorations.\n\n"
+
+              " -Xlog:gc::uptime,tid\n"
+              "\t Log messages tagged with 'gc' tag using 'info' level to output 'stdout', using 'uptime' and 'tid' decorations.\n\n"
+
+              " -Xlog:gc*=info,rt*=off\n"
+              "\t Log messages tagged with at least 'gc' using 'info' level, but turn off logging of messages tagged with 'rt'.\n"
+              "\t (Messages tagged with both 'gc' and 'rt' will not be logged.)\n\n"
+
+              " -Xlog:disable -Xlog:rt=trace:rttrace.txt\n"
+              "\t Turn off all logging, including warnings and errors,\n"
+              "\t and then enable messages tagged with 'rt' using 'trace' level to file 'rttrace.txt'.\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logConfiguration.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGCONFIGURATION_HPP
+#define SHARE_VM_LOGGING_LOGCONFIGURATION_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogOutput;
+class LogDecorators;
+class LogTagLevelExpression;
+
+// Global configuration of logging. Handles parsing and configuration of the logging framework,
+// and manages the list of configured log outputs. The actual tag and level configuration is
+// kept implicitly in the LogTagSets and their LogOutputLists. During configuration the tagsets
+// are iterated over and updated accordingly.
+class LogConfiguration : public AllStatic {
+ private:
+  static LogOutput**  _outputs;
+  static size_t       _n_outputs;
+
+  // Create a new output. Returns NULL if failed.
+  static LogOutput* new_output(char* name, const char* options = NULL);
+
+  // Add an output to the list of configured outputs. Returns the assigned index.
+  static size_t add_output(LogOutput* out);
+
+  // Delete a configured output. The stderr/stdout outputs can not be removed.
+  // Output should be completely disabled before it is deleted.
+  static void delete_output(size_t idx);
+
+  // Disable all logging to the specified output and then delete it (unless it is stdout/stderr).
+  static void disable_output(size_t idx);
+
+  // Get output index by name. Returns SIZE_MAX if output not found.
+  static size_t find_output(const char* name);
+
+  // Configure output (add or update existing configuration) to log on tag-level combination using specified decorators.
+  static void configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators);
+
+ public:
+  // Initialization and finalization of log configuration, to be run at vm startup and shutdown respectively.
+  static void initialize(jlong vm_start_time);
+  static void finalize();
+
+  // Perform necessary post-initialization after VM startup. Enables reconfiguration of logging.
+  static void post_initialize();
+
+  // Disable all logging, equivalent to -Xlog:disable.
+  static void disable_logging();
+
+  // Parse command line configuration. Parameter 'opts' is the string immediately following the -Xlog: argument ("gc" for -Xlog:gc).
+  static bool parse_command_line_arguments(const char* opts = "all");
+
+  // Parse separated configuration arguments (from JCmd/MBean and command line).
+  static bool parse_log_arguments(const char* outputstr,
+                                  const char* what,
+                                  const char* decoratorstr,
+                                  const char* output_options,
+                                  outputStream* errstream);
+
+  // Prints log configuration to outputStream, used by JCmd/MBean.
+  static void describe(outputStream* out);
+
+  // Prints usage help for command line log configuration.
+  static void print_command_line_help(FILE* out);
+};
+
+#endif // SHARE_VM_LOGGING_LOGCONFIGURATION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDecorations.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logDecorations.hpp"
+#include "runtime/os.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "services/management.hpp"
+
+jlong LogDecorations::_vm_start_time_millis = 0;
+
+LogDecorations::LogDecorations(LogLevelType level, const LogTagSet &tagset, const LogDecorators &decorators)
+  : _level(level), _tagset(tagset), _millis(-1) {
+  create_decorations(decorators);
+}
+
+void LogDecorations::create_decorations(const LogDecorators &decorators) {
+  char* position = _decorations_buffer;
+  #define DECORATOR(full_name, abbr) \
+  if (decorators.is_decorator(LogDecorators::full_name##_decorator)) { \
+    _decoration_offset[LogDecorators::full_name##_decorator] = position; \
+    position = create_##full_name##_decoration(position) + 1; \
+  }
+  DECORATOR_LIST
+#undef DECORATOR
+}
+
+jlong LogDecorations::java_millis() {
+  if (_millis < 0) {
+    _millis = os::javaTimeMillis();
+  }
+  return _millis;
+}
+
+#define ASSERT_AND_RETURN(written, pos) \
+    assert(written >= 0, "Decorations buffer overflow"); \
+    return pos + written;
+
+char* LogDecorations::create_time_decoration(char* pos) {
+  char* buf = os::iso8601_time(pos, 29);
+  int written = buf == NULL ? -1 : 29;
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_uptime_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%.3fs", os::elapsedTime());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_timemillis_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ms", java_millis());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_uptimemillis_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer),
+                             INT64_FORMAT "ms", java_millis() - _vm_start_time_millis);
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_timenanos_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ns", os::javaTimeNanos());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_uptimenanos_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ns", os::elapsed_counter());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_pid_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%d", os::current_process_id());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char * LogDecorations::create_tid_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer),
+                             INTX_FORMAT, Thread::current()->osthread()->thread_id());
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char* LogDecorations::create_level_decoration(char* pos) {
+  int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%s", LogLevel::name(_level));
+  ASSERT_AND_RETURN(written, pos)
+}
+
+char* LogDecorations::create_tags_decoration(char* pos) {
+  int written = _tagset.label(pos, DecorationsBufferSize - (pos - _decorations_buffer));
+  ASSERT_AND_RETURN(written, pos)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDecorations.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGDECORATIONS_HPP
+#define SHARE_VM_LOGGING_LOGDECORATIONS_HPP
+
+#include "logging/logDecorators.hpp"
+#include "logging/logTagSet.hpp"
+#include "memory/allocation.hpp"
+
+// Temporary object containing the necessary data for a log call's decorations (timestamps, etc).
+class LogDecorations VALUE_OBJ_CLASS_SPEC {
+ public:
+  static const int DecorationsBufferSize = 256;
+ private:
+  char _decorations_buffer[DecorationsBufferSize];
+  char* _decoration_offset[LogDecorators::Count];
+  LogLevelType _level;
+  LogTagSet _tagset;
+  jlong _millis;
+  static jlong _vm_start_time_millis;
+
+  jlong java_millis();
+  void create_decorations(const LogDecorators& decorators);
+
+#define DECORATOR(name, abbr) char* create_##name##_decoration(char* pos);
+  DECORATOR_LIST
+#undef DECORATOR
+
+ public:
+  LogDecorations(LogLevelType level, const LogTagSet& tagset, const LogDecorators& decorators);
+
+  const char* decoration(LogDecorators::Decorator decorator) const {
+    return _decoration_offset[decorator];
+  }
+
+  static void set_vm_start_time_millis(jlong start_time) {
+    _vm_start_time_millis = start_time;
+  }
+};
+
+#endif // SHARE_VM_LOGGING_LOGDECORATIONS_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDecorators.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logDecorators.hpp"
+#include "runtime/os.inline.hpp"
+
+const char* LogDecorators::_name[][2] = {
+#define DECORATOR(n, a) {#n, #a},
+  DECORATOR_LIST
+#undef DECORATOR
+};
+
+LogDecorators::Decorator LogDecorators::from_string(const char* str) {
+  for (size_t i = 0; i < Count; i++) {
+    Decorator d = static_cast<Decorator>(i);
+    if (strcasecmp(str, name(d)) == 0 || strcasecmp(str, abbreviation(d)) == 0) {
+      return d;
+    }
+  }
+  return Invalid;
+}
+
+bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) {
+  if (decorator_args == NULL || strlen(decorator_args) == 0) {
+    _decorators = DefaultDecoratorsMask;
+    return true;
+  }
+
+  if (strcasecmp(decorator_args, "none") == 0 ) {
+    _decorators = 0;
+    return true;
+  }
+
+  bool result = true;
+  uint tmp_decorators = 0;
+  char* args_copy = os::strdup_check_oom(decorator_args, mtLogging);
+  char* token = args_copy;
+  char* comma_pos;
+  do {
+    comma_pos = strchr(token, ',');
+    if (comma_pos != NULL) {
+      *comma_pos = '\0';
+    }
+    Decorator d = from_string(token);
+    if (d == Invalid) {
+      if (errstream != NULL) {
+        errstream->print_cr("Invalid decorator '%s'.", token);
+      }
+      result = false;
+      break;
+    }
+    tmp_decorators |= mask(d);
+    token = comma_pos + 1;
+  } while (comma_pos != NULL);
+  os::free(args_copy);
+  if (result) {
+    _decorators = tmp_decorators;
+  }
+  return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDecorators.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGDECORATORS_HPP
+#define SHARE_VM_LOGGING_LOGDECORATORS_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// The list of available decorators:
+// time         - Current time and date in ISO-8601 format
+// uptime       - Time since the start of the JVM in seconds and milliseconds (e.g., 6.567s)
+// timemillis   - The same value as generated by System.currentTimeMillis()
+// uptimemillis - Milliseconds since the JVM started
+// timenanos    - The same value as generated by System.nanoTime()
+// uptimenanos  - Nanoseconds since the JVM started
+// pid          - The process identifier
+// tid          - The thread identifier
+// level        - The level associated with the log message
+// tags         - The tag-set associated with the log message
+#define DECORATOR_LIST          \
+  DECORATOR(time,         t)    \
+  DECORATOR(uptime,       u)    \
+  DECORATOR(timemillis,   tm)   \
+  DECORATOR(uptimemillis, um)   \
+  DECORATOR(timenanos,    tn)   \
+  DECORATOR(uptimenanos,  un)   \
+  DECORATOR(pid,          p)    \
+  DECORATOR(tid,          ti)   \
+  DECORATOR(level,        l)    \
+  DECORATOR(tags,         tg)
+
+// LogDecorators represents a selection of decorators that should be prepended to
+// each log message for a given output. Decorators are always prepended in the order
+// declared above. For example, logging with 'uptime, level, tags' decorators results in:
+// [0,943s][info   ][logging] message.
+class LogDecorators VALUE_OBJ_CLASS_SPEC {
+ public:
+  enum Decorator {
+#define DECORATOR(name, abbr) name##_decorator,
+    DECORATOR_LIST
+#undef DECORATOR
+    Count,
+    Invalid
+  };
+
+ private:
+  uint _decorators;
+  static const char* _name[][2];
+  static const uint DefaultDecoratorsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator);
+
+  static uint mask(LogDecorators::Decorator decorator) {
+    return 1 << decorator;
+  }
+
+ public:
+  LogDecorators() : _decorators(DefaultDecoratorsMask) {
+  };
+
+  void clear() {
+    _decorators = 0;
+  }
+
+  static const char* name(LogDecorators::Decorator decorator) {
+    return _name[decorator][0];
+  }
+
+  static const char* abbreviation(LogDecorators::Decorator decorator) {
+    return _name[decorator][1];
+  }
+
+  static LogDecorators::Decorator from_string(const char* str);
+
+  void combine_with(const LogDecorators &source) {
+    _decorators |= source._decorators;
+  }
+
+  bool is_decorator(LogDecorators::Decorator decorator) const {
+    return (_decorators & mask(decorator)) != 0;
+  }
+
+  bool parse(const char* decorator_args, outputStream* errstream = NULL);
+};
+
+#endif // SHARE_VM_LOGGING_LOGDECORATORS_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDiagnosticCommand.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logDiagnosticCommand.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+LogDiagnosticCommand::LogDiagnosticCommand(outputStream* output, bool heap_allocated)
+  : DCmdWithParser(output, heap_allocated),
+    _output("output", "The name or index (#<index>) of output to configure.", "STRING", false),
+    _output_options("output_options", "Options for the output.", "STRING", false),
+    _what("what", "Configures what tags to log.", "STRING", false),
+    _decorators("decorators", "Configures which decorators to use. Use 'none' or an empty value to remove all.", "STRING", false),
+    _disable("disable", "Turns off all logging and clears the log configuration.", "BOOLEAN", false),
+    _list("list", "Lists current log configuration.", "BOOLEAN", false) {
+  _dcmdparser.add_dcmd_option(&_output);
+  _dcmdparser.add_dcmd_option(&_output_options);
+  _dcmdparser.add_dcmd_option(&_what);
+  _dcmdparser.add_dcmd_option(&_decorators);
+  _dcmdparser.add_dcmd_option(&_disable);
+  _dcmdparser.add_dcmd_option(&_list);
+}
+
+int LogDiagnosticCommand::num_arguments() {
+  ResourceMark rm;
+  LogDiagnosticCommand* dcmd = new LogDiagnosticCommand(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+void LogDiagnosticCommand::registerCommand() {
+  uint32_t full_visibility = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean;
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<LogDiagnosticCommand>(full_visibility, true, false));
+}
+
+void LogDiagnosticCommand::execute(DCmdSource source, TRAPS) {
+  bool any_command = false;
+  if (_disable.has_value()) {
+    MutexLocker ml(LogConfiguration_lock);
+    LogConfiguration::disable_logging();
+    any_command = true;
+  }
+
+  if (_output.has_value() || _what.has_value() || _decorators.has_value()) {
+    MutexLocker ml(LogConfiguration_lock);
+    if (!LogConfiguration::parse_log_arguments(_output.value(),
+                                               _what.value(),
+                                               _decorators.value(),
+                                               _output_options.value(),
+                                               output())) {
+      return;
+    }
+    any_command = true;
+  }
+
+  if (_list.has_value()) {
+    MutexLocker ml(LogConfiguration_lock);
+    LogConfiguration::describe(output());
+    any_command = true;
+  }
+
+  if (!any_command) {
+    // If no argument was provided, print usage
+    print_help(LogDiagnosticCommand::name());
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logDiagnosticCommand.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGDIAGNOSTICCOMMAND_HPP
+#define SHARE_VM_LOGGING_LOGDIAGNOSTICCOMMAND_HPP
+
+#include "services/diagnosticCommand.hpp"
+
+// The LogDiagnosticCommand represents the 'VM.log' DCMD
+// that allows configuration of the logging at runtime.
+// It can be used to view or modify the current log configuration.
+// VM.log without additional arguments prints the usage description.
+// The 'list' argument will list all available log tags,
+// levels, decorators and currently configured log outputs.
+// Specifying 'disable' will disable logging completely.
+// The remaining arguments are used to set a log output to log everything
+// with the specified tags and levels using the given decorators.
+class LogDiagnosticCommand : public DCmdWithParser {
+ protected:
+  DCmdArgument<char *> _output;
+  DCmdArgument<char *> _output_options;
+  DCmdArgument<char *> _what;
+  DCmdArgument<char *> _decorators;
+  DCmdArgument<bool> _disable;
+  DCmdArgument<bool> _list;
+
+ public:
+  LogDiagnosticCommand(outputStream* output, bool heap_allocated);
+  void execute(DCmdSource source, TRAPS);
+  static void registerCommand();
+  static int num_arguments();
+
+  static const char* name() {
+    return "VM.log";
+  }
+
+  static const char* description() {
+    return "Lists, enables, disables or changes a log output configuration.";
+  }
+
+  // Used by SecurityManager. This DCMD requires ManagementPermission = control.
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission", "control", NULL};
+    return p;
+  }
+};
+
+#endif // SHARE_VM_LOGGING_LOGDIAGNOSTICCOMMAND_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logFileOutput.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/log.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logFileOutput.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/os.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/defaultStream.hpp"
+
+const char* LogFileOutput::FileOpenMode = "a";
+const char* LogFileOutput::PidFilenamePlaceholder = "%p";
+const char* LogFileOutput::TimestampFilenamePlaceholder = "%t";
+const char* LogFileOutput::TimestampFormat = "%Y-%m-%d_%H-%M-%S";
+const char* LogFileOutput::FileSizeOptionKey = "filesize";
+const char* LogFileOutput::FileCountOptionKey = "filecount";
+char        LogFileOutput::_pid_str[PidBufferSize];
+char        LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
+
+LogFileOutput::LogFileOutput(const char* name)
+    : LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
+      _file_name(NULL), _archive_name(NULL), _archive_name_len(0), _current_size(0),
+      _rotate_size(0), _current_file(1), _file_count(0),
+      _rotation_lock(Mutex::leaf, "LogFileOutput rotation lock", true, Mutex::_safepoint_check_sometimes) {
+  _file_name = make_file_name(name, _pid_str, _vm_start_time_str);
+}
+
+void LogFileOutput::set_file_name_parameters(jlong vm_start_time) {
+  int res = jio_snprintf(_pid_str, sizeof(_pid_str), "%d", os::current_process_id());
+  assert(res > 0, "PID buffer too small");
+
+  struct tm local_time;
+  time_t utc_time = vm_start_time / 1000;
+  os::localtime_pd(&utc_time, &local_time);
+  res = (int)strftime(_vm_start_time_str, sizeof(_vm_start_time_str), TimestampFormat, &local_time);
+  assert(res > 0, "VM start time buffer too small.");
+}
+
+LogFileOutput::~LogFileOutput() {
+  if (_stream != NULL) {
+    if (_archive_name != NULL) {
+      archive();
+    }
+    if (fclose(_stream) != 0) {
+      jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
+                  _file_name, strerror(errno));
+    }
+  }
+  os::free(_archive_name);
+  os::free(_file_name);
+  os::free(const_cast<char*>(_name));
+}
+
+size_t LogFileOutput::parse_value(const char* value_str) {
+  char* end;
+  unsigned long long value = strtoull(value_str, &end, 10);
+  if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
+    return SIZE_MAX;
+  }
+  return value;
+}
+
+bool LogFileOutput::configure_rotation(const char* options) {
+  if (options == NULL || strlen(options) == 0) {
+    return true;
+  }
+  bool success = true;
+  char* opts = os::strdup_check_oom(options, mtLogging);
+
+  char* comma_pos;
+  char* pos = opts;
+  do {
+    comma_pos = strchr(pos, ',');
+    if (comma_pos != NULL) {
+      *comma_pos = '\0';
+    }
+
+    char* equals_pos = strchr(pos, '=');
+    if (equals_pos == NULL) {
+      success = false;
+      break;
+    }
+    char* key = pos;
+    char* value_str = equals_pos + 1;
+    *equals_pos = '\0';
+
+    if (strcmp(FileCountOptionKey, key) == 0) {
+      size_t value = parse_value(value_str);
+      if (value == SIZE_MAX || value >= UINT_MAX) {
+        success = false;
+        break;
+      }
+      _file_count = static_cast<uint>(value);
+      _file_count_max_digits = static_cast<uint>(log10(static_cast<double>(_file_count)) + 1);
+      _archive_name_len = 2 + strlen(_file_name) + _file_count_max_digits;
+      _archive_name = NEW_C_HEAP_ARRAY(char, _archive_name_len, mtLogging);
+    } else if (strcmp(FileSizeOptionKey, key) == 0) {
+      size_t value = parse_value(value_str);
+      if (value == SIZE_MAX || value > SIZE_MAX / K) {
+        success = false;
+        break;
+      }
+      _rotate_size = value * K;
+    } else {
+      success = false;
+      break;
+    }
+    pos = comma_pos + 1;
+  } while (comma_pos != NULL);
+
+  os::free(opts);
+  return success;
+}
+
+bool LogFileOutput::initialize(const char* options) {
+  if (!configure_rotation(options)) {
+    return false;
+  }
+  _stream = fopen(_file_name, FileOpenMode);
+  if (_stream == NULL) {
+    log_error(logging)("Could not open log file '%s' (%s).\n", _file_name, strerror(errno));
+    return false;
+  }
+  return true;
+}
+
+int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
+  if (_stream == NULL) {
+    // An error has occurred with this output, avoid writing to it.
+    return 0;
+  }
+  int written = LogFileStreamOutput::write(decorations, msg);
+  _current_size += written;
+
+  if (should_rotate()) {
+    MutexLockerEx ml(&_rotation_lock, true /* no safepoint check */);
+    if (should_rotate()) {
+      rotate();
+    }
+  }
+
+  return written;
+}
+
+void LogFileOutput::archive() {
+  assert(_archive_name != NULL && _archive_name_len > 0, "Rotation must be configured before using this function.");
+  int ret = jio_snprintf(_archive_name, _archive_name_len, "%s.%0*u",
+                         _file_name, _file_count_max_digits, _current_file);
+  assert(ret >= 0, "Buffer should always be large enough");
+
+  // Attempt to remove possibly existing archived log file before we rename.
+  // Don't care if it fails, we really only care about the rename that follows.
+  remove(_archive_name);
+
+  // Rename the file from ex hotspot.log to hotspot.log.2
+  if (rename(_file_name, _archive_name) == -1) {
+    jio_fprintf(defaultStream::error_stream(), "Could not rename log file '%s' to '%s' (%s).\n",
+                _file_name, _archive_name, strerror(errno));
+  }
+}
+
+void LogFileOutput::rotate() {
+  // Archive the current log file
+  archive();
+
+  // Open the active log file using the same stream as before
+  _stream = freopen(_file_name, FileOpenMode, _stream);
+  if (_stream == NULL) {
+    jio_fprintf(defaultStream::error_stream(), "Could not reopen file '%s' during log rotation (%s).\n",
+                _file_name, strerror(errno));
+    return;
+  }
+
+  // Reset accumulated size, increase current file counter, and check for file count wrap-around.
+  _current_size = 0;
+  _current_file = (_current_file >= _file_count ? 1 : _current_file + 1);
+}
+
+char* LogFileOutput::make_file_name(const char* file_name,
+                                    const char* pid_string,
+                                    const char* timestamp_string) {
+  char* result = NULL;
+
+  // Lets start finding out if we have any %d and/or %t in the name.
+  // We will only replace the first occurrence of any placeholder
+  const char* pid = strstr(file_name, PidFilenamePlaceholder);
+  const char* timestamp = strstr(file_name, TimestampFilenamePlaceholder);
+
+  if (pid == NULL && timestamp == NULL) {
+    // We found no place-holders, return the simple filename
+    return os::strdup_check_oom(file_name, mtLogging);
+  }
+
+  // At least one of the place-holders were found in the file_name
+  const char* first = "";
+  size_t first_pos = -1;
+  size_t first_replace_len = 0;
+
+  const char* second = "";
+  size_t second_pos = -1;
+  size_t second_replace_len = 0;
+
+  // If we found a %p, then setup our variables accordingly
+  if (pid != NULL) {
+    if (timestamp == NULL || pid < timestamp) {
+      first = pid_string;
+      first_pos = pid - file_name;
+      first_replace_len = strlen(PidFilenamePlaceholder);
+    } else {
+      second = pid_string;
+      second_pos = pid - file_name;
+      second_replace_len = strlen(PidFilenamePlaceholder);
+    }
+  }
+
+  if (timestamp != NULL) {
+    if (pid == NULL || timestamp < pid) {
+      first = timestamp_string;
+      first_pos = timestamp - file_name;
+      first_replace_len = strlen(TimestampFilenamePlaceholder);
+    } else {
+      second = timestamp_string;
+      second_pos = timestamp - file_name;
+      second_replace_len = strlen(TimestampFilenamePlaceholder);
+    }
+  }
+
+  size_t first_len = strlen(first);
+  size_t second_len = strlen(second);
+
+  // Allocate the new buffer, size it to hold all we want to put in there +1.
+  size_t result_len =  strlen(file_name) + first_len - first_replace_len + second_len - second_replace_len;
+  result = NEW_C_HEAP_ARRAY(char, result_len + 1, mtLogging);
+
+  // Assemble the strings
+  size_t file_name_pos = 0;
+  size_t i = 0;
+  while (i < result_len) {
+    if (file_name_pos == first_pos) {
+      // We are in the range of the first placeholder
+      strcpy(result + i, first);
+      // Bump output buffer position with length of replacing string
+      i += first_len;
+      // Bump source buffer position to skip placeholder
+      file_name_pos += first_replace_len;
+    } else if (file_name_pos == second_pos) {
+      // We are in the range of the second placeholder
+      strcpy(result + i, second);
+      i += second_len;
+      file_name_pos += second_replace_len;
+    } else {
+      // Else, copy char by char of the original file
+      result[i] = file_name[file_name_pos++];
+      i++;
+    }
+  }
+  // Add terminating char
+  result[result_len] = '\0';
+  return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logFileOutput.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGFILEOUTPUT_HPP
+#define SHARE_VM_LOGGING_LOGFILEOUTPUT_HPP
+
+#include "logging/logFileStreamOutput.hpp"
+#include "runtime/mutex.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogDecorations;
+
+// The log file output, with support for file rotation based on a target size.
+class LogFileOutput : public LogFileStreamOutput {
+ private:
+  static const char*  FileOpenMode;
+  static const char*  FileCountOptionKey;
+  static const char*  FileSizeOptionKey;
+  static const char*  PidFilenamePlaceholder;
+  static const char*  TimestampFilenamePlaceholder;
+  static const char*  TimestampFormat;
+  static const size_t StartTimeBufferSize = 20;
+  static const size_t PidBufferSize       = 21;
+  static char         _pid_str[PidBufferSize];
+  static char         _vm_start_time_str[StartTimeBufferSize];
+
+  Mutex _rotation_lock;
+  const char* _name;
+  char* _file_name;
+  char* _archive_name;
+
+  uint  _current_file;
+  uint  _file_count;
+  uint  _file_count_max_digits;
+
+  size_t  _archive_name_len;
+  size_t  _rotate_size;
+  size_t  _current_size;
+
+  void archive();
+  void rotate();
+  bool configure_rotation(const char* options);
+  char *make_file_name(const char* file_name, const char* pid_string, const char* timestamp_string);
+  static size_t parse_value(const char* value_str);
+
+  bool should_rotate() const {
+    return _file_count > 0 && _rotate_size > 0 && _current_size >= _rotate_size;
+  }
+
+ public:
+  LogFileOutput(const char *name);
+  virtual ~LogFileOutput();
+  virtual bool initialize(const char* options);
+  virtual int write(const LogDecorations& decorations, const char* msg);
+
+  virtual const char* name() const {
+    return _name;
+  }
+
+  static void set_file_name_parameters(jlong start_time);
+};
+
+#endif // SHARE_VM_LOGGING_LOGFILEOUTPUT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logFileStreamOutput.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logDecorators.hpp"
+#include "logging/logDecorations.hpp"
+#include "logging/logFileStreamOutput.hpp"
+#include "memory/allocation.inline.hpp"
+
+LogStdoutOutput LogStdoutOutput::_instance;
+LogStderrOutput LogStderrOutput::_instance;
+
+int LogFileStreamOutput::write(const LogDecorations& decorations, const char* msg) {
+  char decoration_buf[LogDecorations::DecorationsBufferSize];
+  char* position = decoration_buf;
+  int total_written = 0;
+
+  for (uint i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(i);
+    if (!_decorators.is_decorator(decorator)) {
+      continue;
+    }
+    int written = jio_snprintf(position, sizeof(decoration_buf) - total_written, "[%-*s]",
+                               _decorator_padding[decorator],
+                               decorations.decoration(decorator));
+    if (written <= 0) {
+      return -1;
+    } else if (static_cast<size_t>(written - 2) > _decorator_padding[decorator]) {
+      _decorator_padding[decorator] = written - 2;
+    }
+    position += written;
+    total_written += written;
+  }
+
+  if (total_written == 0) {
+    total_written = jio_fprintf(_stream, "%s\n", msg);
+  } else {
+    total_written = jio_fprintf(_stream, "%s %s\n", decoration_buf, msg);
+  }
+  fflush(_stream);
+  return total_written;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logFileStreamOutput.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP
+#define SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP
+
+#include "logging/logDecorators.hpp"
+#include "logging/logOutput.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogDecorations;
+
+// Base class for all FileStream-based log outputs.
+class LogFileStreamOutput : public LogOutput {
+ protected:
+  FILE*               _stream;
+  size_t              _decorator_padding[LogDecorators::Count];
+
+  LogFileStreamOutput(FILE *stream) : _stream(stream) {
+    for (size_t i = 0; i < LogDecorators::Count; i++) {
+      _decorator_padding[i] = 0;
+    }
+    _decorator_padding[LogDecorators::level_decorator] = 7;
+  }
+
+ public:
+  virtual int write(const LogDecorations &decorations, const char* msg);
+};
+
+class LogStdoutOutput : public LogFileStreamOutput {
+  friend class LogOutput;
+ private:
+  static LogStdoutOutput _instance;
+  LogStdoutOutput() : LogFileStreamOutput(stdout) {
+    set_config_string("all=off");
+  }
+  virtual bool initialize(const char* options) {
+    return false;
+  }
+ public:
+  virtual const char* name() const {
+    return "stdout";
+  }
+};
+
+class LogStderrOutput : public LogFileStreamOutput {
+  friend class LogOutput;
+ private:
+  static LogStderrOutput _instance;
+  LogStderrOutput() : LogFileStreamOutput(stderr) {
+    set_config_string("all=warning");
+  }
+  virtual bool initialize(const char* options) {
+    return false;
+  }
+ public:
+  virtual const char* name() const {
+    return "stderr";
+  }
+};
+
+#endif // SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logLevel.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logLevel.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+const char* LogLevel::_name[] = {
+  "off",
+#define LOG_LEVEL(name, printname) #printname,
+  LOG_LEVEL_LIST
+#undef LOG_LEVEL
+};
+
+LogLevelType LogLevel::from_string(const char* str) {
+  for (uint i = 0; i < Count; i++) {
+    if (strcasecmp(str, _name[i]) == 0) {
+      return static_cast<LogLevelType>(i);
+    }
+  }
+  return Invalid;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logLevel.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGLEVEL_HPP
+#define SHARE_VM_LOGGING_LOGLEVEL_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/macros.hpp"
+
+// The list of log levels:
+//
+//  develop - A non-product level that is finer than trace.
+//            Should be used for really expensive and/or
+//            extensive logging, or logging that shouldn't
+//            or can't be included in a product build.
+//
+//  trace   - Finest level of logging in product builds.
+//            Use for extensive/noisy logging that can
+//            give slow-down when enabled.
+//
+//  debug   - A finer level of logging. Use for semi-noisy
+//            logging that is does not fit the info level.
+//
+//  info    - General level of logging. Use for significant
+//            events and/or informative summaries.
+//
+//  warning - Important messages that are not strictly errors.
+//
+//  error   - Critical messages caused by errors.
+//
+#define LOG_LEVEL_LIST \
+  NOT_PRODUCT(LOG_LEVEL(Develop, develop)) \
+  LOG_LEVEL(Trace, trace) \
+  LOG_LEVEL(Debug, debug) \
+  LOG_LEVEL(Info, info) \
+  LOG_LEVEL(Warning, warning) \
+  LOG_LEVEL(Error, error)
+
+class LogLevel : public AllStatic {
+ public:
+  enum type {
+    Off,
+#define LOG_LEVEL(name, printname) name,
+    LOG_LEVEL_LIST
+#undef LOG_LEVEL
+    Count,
+    Invalid,
+    First = Off + 1,
+    Last = Error,
+    Default = Warning,
+    Unspecified = Info
+  };
+
+  static const char *name(LogLevel::type level) {
+    return _name[level];
+  }
+
+  static LogLevel::type from_string(const char* str);
+
+ private:
+  static const char* _name[];
+};
+
+typedef LogLevel::type LogLevelType;
+
+#endif // SHARE_VM_LOGGING_LOGLEVEL_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logOutput.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logFileStreamOutput.hpp"
+#include "logging/logOutput.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/os.inline.hpp"
+
+LogOutput* const LogOutput::Stdout = &LogStdoutOutput::_instance;
+LogOutput* const LogOutput::Stderr = &LogStderrOutput::_instance;
+
+LogOutput::~LogOutput() {
+  os::free(_config_string);
+}
+
+void LogOutput::set_config_string(const char* string) {
+  os::free(_config_string);
+  _config_string = os::strdup_check_oom(string, mtLogging);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logOutput.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGOUTPUT_HPP
+#define SHARE_VM_LOGGING_LOGOUTPUT_HPP
+
+#include "logging/logDecorators.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogDecorations;
+
+// The base class/interface for log outputs.
+// Keeps track of the latest configuration string,
+// and its selected decorators.
+class LogOutput : public CHeapObj<mtLogging> {
+ protected:
+  LogDecorators _decorators;
+  char* _config_string;
+
+ public:
+  static LogOutput* const Stdout;
+  static LogOutput* const Stderr;
+
+  void set_decorators(const LogDecorators &decorators) {
+    _decorators = decorators;
+  }
+
+  const LogDecorators& decorators() const {
+    return _decorators;
+  }
+
+  const char* config_string() const {
+    return _config_string;
+  }
+
+  LogOutput() : _config_string(NULL) {
+  }
+
+  virtual ~LogOutput();
+  void set_config_string(const char* string);
+
+  virtual const char* name() const = 0;
+  virtual bool initialize(const char* options) = 0;
+  virtual int write(const LogDecorations &decorations, const char* msg) = 0;
+};
+
+#endif // SHARE_VM_LOGGING_LOGOUTPUT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logOutputList.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logLevel.hpp"
+#include "logging/logOutputList.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/atomic.inline.hpp"
+#include "runtime/orderAccess.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+jint LogOutputList::increase_readers() {
+  jint result = Atomic::add(1, &_active_readers);
+  assert(_active_readers > 0, "Ensure we have consistent state");
+  return result;
+}
+
+jint LogOutputList::decrease_readers() {
+  jint result = Atomic::add(-1, &_active_readers);
+  assert(result >= 0, "Ensure we have consistent state");
+  return result;
+}
+
+void LogOutputList::wait_until_no_readers() const {
+  OrderAccess::storeload();
+  while (_active_readers != 0) {
+    // Busy wait
+  }
+}
+
+void LogOutputList::set_output_level(LogOutput* output, LogLevelType level) {
+  LogOutputNode* node = find(output);
+  if (level == LogLevel::Off && node != NULL) {
+    remove_output(node);
+  } else if (level != LogLevel::Off && node == NULL) {
+    add_output(output, level);
+  } else if (node != NULL) {
+    update_output_level(node, level);
+  }
+}
+
+LogOutputList::LogOutputNode* LogOutputList::find(LogOutput* output) {
+  for (LogOutputNode* node = _level_start[LogLevel::Last]; node != NULL; node = node->_next) {
+    if (output == node->_value) {
+      return node;
+    }
+  }
+  return NULL;
+}
+
+void LogOutputList::remove_output(LogOutputList::LogOutputNode* node) {
+  assert(node != NULL, "Node must be non-null");
+
+  // Remove node from _level_start first
+  bool found = false;
+  for (uint level = LogLevel::First; level < LogLevel::Count; level++) {
+    if (_level_start[level] == node) {
+      found = true;
+      _level_start[level] = node->_next;
+    }
+  }
+
+  // Now remove it from the linked list
+  for (LogOutputNode* cur = _level_start[LogLevel::Last]; cur != NULL; cur = cur->_next) {
+    if (cur->_next == node) {
+      found = true;
+      cur->_next = node->_next;
+      break;
+    }
+  }
+  assert(found, "Node to be removed should always be found");
+
+  wait_until_no_readers();
+  delete node;
+}
+
+void LogOutputList::add_output(LogOutput* output, LogLevelType level) {
+  LogOutputNode* node = new LogOutputNode();
+  node->_value = output;
+  node->_level = level;
+
+  // Set the next pointer to the first node of a lower level
+  for (node->_next = _level_start[level];
+       node->_next != NULL && node->_next->_level == level;
+       node->_next = node->_next->_next) {
+  }
+
+  // Update the _level_start index
+  for (int l = LogLevel::Last; l >= level; l--) {
+    if (_level_start[l] == NULL || _level_start[l]->_level < level) {
+      _level_start[l] = node;
+    }
+  }
+
+  // Add the node the list
+  for (LogOutputNode* cur = _level_start[LogLevel::Last]; cur != NULL; cur = cur->_next) {
+    if (cur != node && cur->_next == node->_next) {
+      cur->_next = node;
+      break;
+    }
+  }
+}
+
+void LogOutputList::update_output_level(LogOutputList::LogOutputNode* node, LogLevelType level) {
+  add_output(node->_value, level);
+  wait_until_no_readers();
+  remove_output(node);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logOutputList.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGOUTPUTLIST_HPP
+#define SHARE_VM_LOGGING_LOGOUTPUTLIST_HPP
+
+#include "logging/logLevel.hpp"
+#include "memory/allocation.hpp"
+#include "runtime/atomic.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogOutput;
+
+// Data structure to keep track of log outputs for a given tagset.
+// Essentially a sorted linked list going from error level outputs
+// to outputs of finer levels. Keeps an index from each level to
+// the first node in the list for the corresponding level.
+// This allows a log message on, for example, info level to jump
+// straight into the list where the first info level output can
+// be found. The log message will then be printed on that output,
+// as well as all outputs in nodes that follow in the list (which
+// can be additional info level outputs and/or debug and trace outputs).
+//
+// Each instance keeps track of the number of current readers of the list.
+// To remove a node from the list the node must first be unlinked,
+// and the memory for that node can be freed whenever the removing
+// thread observes an active reader count of 0 (after unlinking it).
+class LogOutputList VALUE_OBJ_CLASS_SPEC {
+ private:
+  struct LogOutputNode : public CHeapObj<mtLogging> {
+    LogOutput*      _value;
+    LogOutputNode*  _next;
+    LogLevelType    _level;
+  };
+
+  LogOutputNode*  _level_start[LogLevel::Count];
+  volatile jint   _active_readers;
+
+  LogOutputNode* find(LogOutput* output);
+  void remove_output(LogOutputNode* node);
+  void add_output(LogOutput* output, LogLevelType level);
+  void update_output_level(LogOutputNode* node, LogLevelType level);
+
+ public:
+  LogOutputList() : _active_readers(0) {
+    for (size_t i = 0; i < LogLevel::Count; i++) {
+      _level_start[i] = NULL;
+    }
+  }
+
+  // Test if the outputlist has an output for the given level.
+  bool is_level(LogLevelType level) {
+    return _level_start[level] != NULL;
+  }
+
+  // Set (add/update/remove) the output to the specified level.
+  void set_output_level(LogOutput* output, LogLevelType level);
+
+  // Bookkeeping functions to keep track of number of active readers/iterators for the list.
+  jint increase_readers();
+  jint decrease_readers();
+  void wait_until_no_readers() const;
+
+  class Iterator VALUE_OBJ_CLASS_SPEC {
+    friend class LogOutputList;
+   private:
+    LogOutputNode*  _current;
+    LogOutputList*  _list;
+    Iterator(LogOutputList* list, LogOutputNode* start) : _current(start), _list(list) {
+    }
+
+   public:
+    ~Iterator() {
+      _list->decrease_readers();
+    }
+
+    LogOutput* operator*() {
+      return _current->_value;
+    }
+
+    void operator++(int) {
+      _current = _current->_next;
+    }
+
+    bool operator!=(const LogOutputNode *ref) const {
+      return _current != ref;
+    }
+  };
+
+  Iterator iterator(LogLevelType level = LogLevel::Last) {
+    increase_readers();
+    return Iterator(this, _level_start[level]);
+  }
+
+  LogOutputNode* end() const {
+    return NULL;
+  }
+};
+
+#endif // SHARE_VM_LOGGING_LOGOUTPUTLIST_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logPrefix.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGPREFIX_HPP
+#define SHARE_VM_LOGGING_LOGPREFIX_HPP
+
+#include "gc/shared/gcId.hpp"
+#include "logging/logTag.hpp"
+
+// Prefixes prepend each log message for a specified tagset with the given prefix.
+// A prefix consists of a format string and a value or callback. Prefixes are added
+// after the decorations but before the log message.
+//
+// List of prefixes for specific tags and/or tagsets.
+// Syntax: LOG_PREFIX(<printf format>, <value/callback for value>, LOG_TAGS(<chosen log tags>))
+#define LOG_PREFIX_LIST // Currently unused/empty
+
+// The empty prefix, used when there's no prefix defined.
+template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4, LogTagType GuardTag = LogTag::__NO_TAG>
+struct LogPrefix : public AllStatic {
+  STATIC_ASSERT(GuardTag == LogTag::__NO_TAG);
+  static size_t prefix(char* buf, size_t len) {
+    return 0;
+  }
+};
+
+#define LOG_PREFIX(fmt, fn, ...) \
+template <> struct LogPrefix<__VA_ARGS__> { \
+  static size_t prefix(char* buf, size_t len) { \
+    int ret = jio_snprintf(buf, len, fmt, fn); \
+    assert(ret >= 0, \
+           err_msg("Failed to prefix log message using prefix ('%s', '%s'), log buffer too small?", fmt, #fn)); \
+    return ret; \
+  } \
+};
+LOG_PREFIX_LIST
+#undef LOG_PREFIX
+
+#endif // SHARE_VM_LOGGING_LOGPREFIX_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTag.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logTag.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+const char* LogTag::_name[] = {
+  "", // __NO_TAG
+#define LOG_TAG(name) #name,
+  LOG_TAG_LIST
+#undef LOG_TAG
+};
+
+LogTagType LogTag::from_string(const char* str) {
+  for (uint i = 0; i < LogTag::Count; i++) {
+    if (strcasecmp(str, _name[i]) == 0) {
+      return static_cast<LogTagType>(i);
+    }
+  }
+  return __NO_TAG;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTag.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGTAG_HPP
+#define SHARE_VM_LOGGING_LOGTAG_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// List of available logging tags. New tags should be added here.
+// (The tags 'all', 'disable' and 'help' are special tags that can
+// not be used in log calls, and should not be listed below.)
+#define LOG_TAG_LIST \
+  LOG_TAG(logging)
+
+#define PREFIX_LOG_TAG(T) (LogTag::T)
+
+// Expand a set of log tags to their prefixed names.
+// For error detection purposes, the macro passes one more tag than what is supported.
+// If too many tags are given, a static assert in the log class will fail.
+#define LOG_TAGS_EXPANDED(T0, T1, T2, T3, T4, T5, ...)  PREFIX_LOG_TAG(T0), PREFIX_LOG_TAG(T1), PREFIX_LOG_TAG(T2), \
+                                                        PREFIX_LOG_TAG(T3), PREFIX_LOG_TAG(T4), PREFIX_LOG_TAG(T5)
+// The EXPAND_VARARGS macro is required for MSVC, or it will resolve the LOG_TAGS_EXPANDED macro incorrectly.
+#define EXPAND_VARARGS(x) x
+#define LOG_TAGS(...) EXPAND_VARARGS(LOG_TAGS_EXPANDED(__VA_ARGS__, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG))
+
+// Log tags are used to classify log messages.
+// Each log message can be assigned between 1 to LogTag::MaxTags number of tags.
+// Specifying multiple tags for a log message means that only outputs configured
+// for those exact tags, or a subset of the tags with a wildcard, will see the logging.
+// Multiple tags should be comma separated, e.g. log_error(tag1, tag2)("msg").
+class LogTag : public AllStatic {
+ public:
+  // The maximum number of tags that a single log message can have.
+  // E.g. there might be hundreds of different tags available,
+  // but a specific log message can only be tagged with up to MaxTags of those.
+  static const size_t MaxTags = 5;
+
+  enum type {
+    __NO_TAG,
+#define LOG_TAG(name) name,
+    LOG_TAG_LIST
+#undef LOG_TAG
+    Count
+  };
+
+  static const char* name(LogTag::type tag) {
+    return _name[tag];
+  }
+
+  static LogTag::type from_string(const char *str);
+
+ private:
+  static const char* _name[];
+};
+
+typedef LogTag::type LogTagType;
+
+#endif // SHARE_VM_LOGGING_LOGTAG_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTagLevelExpression.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logTagLevelExpression.hpp"
+#include "logging/logTagSet.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/os.inline.hpp"
+
+const char* LogTagLevelExpression::DefaultExpressionString = "all";
+
+LogTagLevelExpression::~LogTagLevelExpression() {
+  os::free(_string);
+}
+
+void LogTagLevelExpression::clear() {
+  _ntags = 0;
+  _ncombinations = 0;
+  for (size_t combination = 0; combination < MaxCombinations; combination++) {
+    _level[combination] = LogLevel::Invalid;
+    _allow_other_tags[combination] = false;
+    for (size_t tag = 0; tag < LogTag::MaxTags; tag++) {
+      _tags[combination][tag] = LogTag::__NO_TAG;
+    }
+  }
+  os::free(_string);
+  _string = NULL;
+}
+
+bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) {
+  bool success = true;
+  clear();
+  if (str == NULL || strcmp(str, "") == 0) {
+    str = DefaultExpressionString;
+  }
+  char* copy = os::strdup_check_oom(str, mtLogging);
+  // Split string on commas
+  for (char *comma_pos = copy, *cur = copy; success && comma_pos != NULL; cur = comma_pos + 1) {
+    if (_ncombinations == MaxCombinations) {
+      if (errstream != NULL) {
+        errstream->print_cr("Can not have more than " SIZE_FORMAT " tag combinations in a what-expression.",
+                            MaxCombinations);
+      }
+      success = false;
+      break;
+    }
+
+    comma_pos = strchr(cur, ',');
+    if (comma_pos != NULL) {
+      *comma_pos = '\0';
+    }
+
+    // Parse the level, if specified
+    LogLevelType level = LogLevel::Unspecified;
+    char* equals = strchr(cur, '=');
+    if (equals != NULL) {
+      level = LogLevel::from_string(equals + 1);
+      if (level == LogLevel::Invalid) {
+        if (errstream != NULL) {
+          errstream->print_cr("Invalid level '%s' in what-expression.", equals + 1);
+        }
+        success = false;
+        break;
+      }
+      *equals = '\0'; // now ignore "=level" part of substr
+    }
+    set_level(level);
+
+    // Parse special tags such as 'all'
+    if (strcmp(cur, "all") == 0) {
+      set_allow_other_tags();
+      new_combination();
+      continue;
+    }
+
+    // Check for '*' suffix
+    char* asterisk_pos = strchr(cur, '*');
+    if (asterisk_pos != NULL && asterisk_pos[1] == '\0') {
+      set_allow_other_tags();
+      *asterisk_pos = '\0';
+    }
+
+    // Parse the tag expression (t1+t2+...+tn)
+    char* plus_pos;
+    char* cur_tag = cur;
+    do {
+      plus_pos = strchr(cur_tag, '+');
+      if (plus_pos != NULL) {
+        *plus_pos = '\0';
+      }
+      LogTagType tag = LogTag::from_string(cur_tag);
+      if (tag == LogTag::__NO_TAG) {
+        if (errstream != NULL) {
+          errstream->print_cr("Invalid tag '%s' in what-expression.", cur_tag);
+        }
+        success = false;
+        break;
+      }
+      if (_ntags == LogTag::MaxTags) {
+        if (errstream != NULL) {
+          errstream->print_cr("Tag combination exceeds the maximum of " SIZE_FORMAT " tags.",
+                              LogTag::MaxTags);
+        }
+        success = false;
+        break;
+      }
+      add_tag(tag);
+      cur_tag = plus_pos + 1;
+    } while (plus_pos != NULL);
+
+    new_combination();
+  }
+
+  // Save the (unmodified) string for printing purposes.
+  _string = copy;
+  strcpy(_string, str);
+
+  return success;
+}
+
+LogLevelType LogTagLevelExpression::level_for(const LogTagSet& ts) const {
+  LogLevelType level = LogLevel::Off;
+  for (size_t combination = 0; combination < _ncombinations; combination++) {
+    bool contains_all = true;
+    size_t tag_idx;
+    for (tag_idx = 0; tag_idx < LogTag::MaxTags && _tags[combination][tag_idx] != LogTag::__NO_TAG; tag_idx++) {
+      if (!ts.contains(_tags[combination][tag_idx])) {
+        contains_all = false;
+        break;
+      }
+    }
+    // All tags in the expression must be part of the tagset,
+    // and either the expression allows other tags (has a wildcard),
+    // or the number of tags in the expression and tagset must match.
+    if (contains_all && (_allow_other_tags[combination] || tag_idx == ts.ntags())) {
+      level = _level[combination];
+    }
+  }
+  return level;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTagLevelExpression.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
+#define SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
+
+#include "logging/logLevel.hpp"
+#include "logging/logTag.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogTagSet;
+
+// Class used to temporary encode a 'what'-expression during log configuration.
+// Consists of a combination of tags and levels, e.g. "tag1+tag2=level1,tag3*=level2".
+class LogTagLevelExpression : public StackObj {
+ private:
+  static const size_t MaxCombinations = 32;
+  static const char* DefaultExpressionString;
+
+  size_t        _ntags, _ncombinations;
+  LogTagType    _tags[MaxCombinations][LogTag::MaxTags];
+  LogLevelType  _level[MaxCombinations];
+  bool          _allow_other_tags[MaxCombinations];
+  char*         _string;
+
+  void new_combination() {
+    _ncombinations++;
+    _ntags = 0;
+  }
+
+  void add_tag(LogTagType tag) {
+    assert(_ntags < LogTag::MaxTags, "Can't have more tags than MaxTags!");
+    _tags[_ncombinations][_ntags++] = tag;
+  }
+
+  void set_level(LogLevelType level) {
+    _level[_ncombinations] = level;
+  }
+
+  void set_allow_other_tags() {
+    _allow_other_tags[_ncombinations] = true;
+  }
+
+  void clear();
+
+ public:
+  LogTagLevelExpression() : _ntags(0), _ncombinations(0), _string(NULL) {
+  }
+
+  const char* to_string() const {
+    return _string;
+  }
+
+  ~LogTagLevelExpression();
+  bool parse(const char* str, outputStream* errstream = NULL);
+  LogLevelType level_for(const LogTagSet& ts) const;
+};
+
+#endif // SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTagSet.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#include "precompiled.hpp"
+#include "logging/logDecorations.hpp"
+#include "logging/logLevel.hpp"
+#include "logging/logOutput.hpp"
+#include "logging/logTag.hpp"
+#include "logging/logTagSet.hpp"
+
+LogTagSet*  LogTagSet::_list      = NULL;
+size_t      LogTagSet::_ntagsets  = 0;
+
+// This constructor is called only during static initialization.
+// See the declaration in logTagSet.hpp for more information.
+LogTagSet::LogTagSet(LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, LogTagType t4)
+    : _next(_list) {
+  _tag[0] = t0;
+  _tag[1] = t1;
+  _tag[2] = t2;
+  _tag[3] = t3;
+  _tag[4] = t4;
+  for (_ntags = 0; _ntags < LogTag::MaxTags && _tag[_ntags] != LogTag::__NO_TAG; _ntags++) {
+  }
+  _list = this;
+  _ntagsets++;
+
+  // Set the default output to warning and error level for all new tagsets.
+  _output_list.set_output_level(LogOutput::Stderr, LogLevel::Default);
+}
+
+bool LogTagSet::is_level(LogLevelType level) {
+  return _output_list.is_level(level);
+}
+
+void LogTagSet::update_decorators(const LogDecorators& decorator) {
+  LogDecorators new_decorators = decorator;
+  for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
+    new_decorators.combine_with((*it)->decorators());
+  }
+  _decorators = new_decorators;
+}
+
+bool LogTagSet::has_output(const LogOutput* output) {
+  for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
+    if (*it == output) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void LogTagSet::log(LogLevelType level, const char* msg) {
+  LogDecorations decorations(level, *this, _decorators);
+  for (LogOutputList::Iterator it = _output_list.iterator(level); it != _output_list.end(); it++) {
+    (*it)->write(decorations, msg);
+  }
+}
+
+int LogTagSet::label(char* buf, size_t len)  {
+  int tot_written = 0;
+  for (size_t i = 0; i < _ntags; i++) {
+    int written = jio_snprintf(buf + tot_written, len - tot_written, "%s%s",
+                               (i == 0 ? "" : ","),
+                               LogTag::name(_tag[i]));
+    if (written < 0) {
+      return -1;
+    }
+    tot_written += written;
+  }
+  return tot_written;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/vm/logging/logTagSet.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGTAGSET_HPP
+#define SHARE_VM_LOGGING_LOGTAGSET_HPP
+
+#include "logging/logDecorators.hpp"
+#include "logging/logLevel.hpp"
+#include "logging/logOutputList.hpp"
+#include "logging/logTag.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// The tagset represents a combination of tags that occur in a log call somewhere.
+// Tagsets are created automatically by the LogTagSetMappings and should never be
+// instantiated directly somewhere else.
+class LogTagSet VALUE_OBJ_CLASS_SPEC {
+ private:
+  static LogTagSet* _list;
+  static size_t     _ntagsets;
+
+  LogTagSet* const  _next;
+  size_t            _ntags;
+  LogTagType        _tag[LogTag::MaxTags];
+
+  LogOutputList     _output_list;
+  LogDecorators     _decorators;
+
+  // Keep constructor private to prevent incorrect instantiations of this class.
+  // Only LogTagSetMappings can create/contain instances of this class.
+  // The constructor links all tagsets together in a global list of tagsets.
+  // This list is used during configuration to be able to update all tagsets
+  // and their configurations to reflect the new global log configuration.
+  LogTagSet(LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, LogTagType t4);
+
+  template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4>
+  friend class LogTagSetMapping;
+
+ public:
+  static LogTagSet* first() {
+    return _list;
+  }
+
+  LogTagSet* next() {
+    return _next;
+  }
+
+  size_t ntags() const {
+    return _ntags;
+  }
+
+  bool contains(LogTagType tag) const {
+    for (size_t i = 0; _tag[i] != LogTag::__NO_TAG; i++) {
+      if (tag == _tag[i]) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void set_output_level(LogOutput* output, LogLevelType level) {
+    _output_list.set_output_level(output, level);
+  }
+
+  // Refresh the decorators for this tagset to contain the decorators for all
+  // of its current outputs combined with the given decorators.
+  void update_decorators(const LogDecorators& decorator);
+
+  int label(char *buf, size_t len);
+  bool has_output(const LogOutput* output);
+  bool is_level(LogLevelType level);
+  void log(LogLevelType level, const char* msg);
+};
+
+template <LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG,
+          LogTagType T3 = LogTag::__NO_TAG, LogTagType T4 = LogTag::__NO_TAG>
+class LogTagSetMapping : public AllStatic {
+private:
+  static LogTagSet _tagset;
+
+public:
+  static LogTagSet& tagset() {
+    return _tagset;
+  }
+};
+
+// Instantiate the static field _tagset for all tagsets that are used for logging somewhere.
+// (This must be done here rather than the .cpp file because it's a template.)
+// Each combination of tags used as template arguments to the Log class somewhere (via macro or not)
+// will instantiate the LogTagSetMapping template, which in turn creates the static field for that
+// tagset. This _tagset contains the configuration for those tags.
+template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4>
+LogTagSet LogTagSetMapping<T0, T1, T2, T3, T4>::_tagset(T0, T1, T2, T3, T4);
+
+#endif // SHARE_VM_LOGGING_LOGTAGSET_HPP
--- a/src/share/vm/memory/allocation.hpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/memory/allocation.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -154,8 +154,9 @@
   mtChunk             = 0x0C,  // chunk that holds content of arenas
   mtTest              = 0x0D,  // Test type for verifying NMT
   mtTracing           = 0x0E,  // memory used for Tracing
-  mtNone              = 0x0F,  // undefined
-  mt_number_of_types  = 0x10   // number of memory types (mtDontTrack
+  mtLogging           = 0x0F,  // memory for logging
+  mtNone              = 0x10,  // undefined
+  mt_number_of_types  = 0x11   // number of memory types (mtDontTrack
                                  // is not included as validate type)
 };
 
--- a/src/share/vm/precompiled/precompiled.hpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/precompiled/precompiled.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -128,6 +128,7 @@
 # include "interpreter/templateInterpreter.hpp"
 # include "interpreter/templateTable.hpp"
 # include "jvmtifiles/jvmti.h"
+# include "logging/log.hpp"
 # include "memory/allocation.hpp"
 # include "memory/allocation.inline.hpp"
 # include "memory/heap.hpp"
--- a/src/share/vm/runtime/arguments.cpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/runtime/arguments.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -32,6 +32,7 @@
 #include "gc/shared/genCollectedHeap.hpp"
 #include "gc/shared/referenceProcessor.hpp"
 #include "gc/shared/taskqueue.hpp"
+#include "logging/logConfiguration.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/universe.inline.hpp"
 #include "oops/oop.inline.hpp"
@@ -3190,6 +3191,26 @@
       if (FLAG_SET_CMDLINE(bool, PrintGCTimeStamps, true) != Flag::SUCCESS) {
         return JNI_EINVAL;
       }
+    } else if (match_option(option, "-Xlog", &tail)) {
+      bool ret = false;
+      if (strcmp(tail, ":help") == 0) {
+        LogConfiguration::print_command_line_help(defaultStream::output_stream());
+        vm_exit(0);
+      } else if (strcmp(tail, ":disable") == 0) {
+        LogConfiguration::disable_logging();
+        ret = true;
+      } else if (*tail == '\0') {
+        ret = LogConfiguration::parse_command_line_arguments();
+        assert(ret, "-Xlog without arguments should never fail to parse");
+      } else if (*tail == ':') {
+        ret = LogConfiguration::parse_command_line_arguments(tail + 1);
+      }
+      if (ret == false) {
+        jio_fprintf(defaultStream::error_stream(),
+                    "Invalid -Xlog option '-Xlog%s'\n",
+                    tail);
+        return JNI_EINVAL;
+      }
     // JNI hooks
     } else if (match_option(option, "-Xcheck", &tail)) {
       if (!strcmp(tail, ":jni")) {
--- a/src/share/vm/runtime/mutexLocker.cpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/runtime/mutexLocker.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -127,6 +127,7 @@
 Mutex*   Management_lock              = NULL;
 Monitor* Service_lock                 = NULL;
 Monitor* PeriodicTask_lock            = NULL;
+Mutex*   LogConfiguration_lock        = NULL;
 
 #ifdef INCLUDE_TRACE
 Mutex*   JfrStacktrace_lock           = NULL;
@@ -282,6 +283,7 @@
   if (WhiteBoxAPI) {
     def(Compilation_lock           , Monitor, leaf,        false, Monitor::_safepoint_check_never);
   }
+  def(LogConfiguration_lock        , Mutex,   nonleaf,     false, Monitor::_safepoint_check_always);
 
 #ifdef INCLUDE_TRACE
   def(JfrMsg_lock                  , Monitor, leaf,        true,  Monitor::_safepoint_check_always);
--- a/src/share/vm/runtime/mutexLocker.hpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/runtime/mutexLocker.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -126,6 +126,7 @@
 extern Mutex*   Management_lock;                 // a lock used to serialize JVM management
 extern Monitor* Service_lock;                    // a lock used for service thread operation
 extern Monitor* PeriodicTask_lock;               // protects the periodic task structure
+extern Mutex*   LogConfiguration_lock;           // protects configuration of logging
 
 #ifdef INCLUDE_TRACE
 extern Mutex*   JfrStacktrace_lock;              // used to guard access to the JFR stacktrace table
--- a/src/share/vm/runtime/thread.cpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/runtime/thread.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -37,6 +37,7 @@
 #include "interpreter/linkResolver.hpp"
 #include "interpreter/oopMapCache.hpp"
 #include "jvmtifiles/jvmtiEnv.hpp"
+#include "logging/logConfiguration.hpp"
 #include "memory/metaspaceShared.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/universe.inline.hpp"
@@ -3306,6 +3307,10 @@
   // Initialize the os module before using TLS
   os::init();
 
+  // Record VM creation timing statistics
+  TraceVmCreationTime create_vm_timer;
+  create_vm_timer.start();
+
   // Initialize system properties.
   Arguments::init_system_properties();
 
@@ -3315,6 +3320,9 @@
   // Update/Initialize System properties after JDK version number is known
   Arguments::init_version_specific_system_properties();
 
+  // Make sure to initialize log configuration *before* parsing arguments
+  LogConfiguration::initialize(create_vm_timer.begin_time());
+
   // Parse arguments
   jint parse_result = Arguments::parse(args);
   if (parse_result != JNI_OK) return parse_result;
@@ -3341,10 +3349,6 @@
 
   HOTSPOT_VM_INIT_BEGIN();
 
-  // Record VM creation timing statistics
-  TraceVmCreationTime create_vm_timer;
-  create_vm_timer.start();
-
   // Timing (must come after argument parsing)
   TraceTime timer("Create VM", TraceStartupTime);
 
@@ -3492,6 +3496,7 @@
   // debug stuff, that does not work until all basic classes have been initialized.
   set_init_completed();
 
+  LogConfiguration::post_initialize();
   Metaspace::post_initialize();
 
   HOTSPOT_VM_INIT_END();
@@ -3966,6 +3971,8 @@
   // exit_globals() will delete tty
   exit_globals();
 
+  LogConfiguration::finalize();
+
   return true;
 }
 
--- a/src/share/vm/services/management.hpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/services/management.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -118,6 +118,10 @@
   void start()
   { _timer.update_to(0); _begin_time = os::javaTimeMillis(); }
 
+  jlong begin_time() const {
+    return _begin_time;
+  }
+
   /**
    * Only call this if initialization completes successfully; it will
    * crash if PerfMemory_exit() has already been called (usually by
--- a/src/share/vm/services/nmtCommon.cpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/services/nmtCommon.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -40,6 +40,7 @@
   "Arena Chunk",
   "Test",
   "Tracing",
+  "Logging",
   "Unknown"
 };
 
--- a/src/share/vm/utilities/ostream.cpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/utilities/ostream.cpp	Thu Sep 24 12:36:04 2015 +0200
@@ -1440,3 +1440,14 @@
 }
 
 #endif
+
+void logStream::write(const char* s, size_t len) {
+  if (len > 0 && s[len - 1] == '\n') {
+    _current_line.write(s, len - 1);
+    _log_func(_current_line.as_string());
+    _current_line.reset();
+  } else {
+    _current_line.write(s, len);
+    update_position(s, len);
+  }
+}
--- a/src/share/vm/utilities/ostream.hpp	Wed Sep 23 22:04:23 2015 +0300
+++ b/src/share/vm/utilities/ostream.hpp	Thu Sep 24 12:36:04 2015 +0200
@@ -235,6 +235,18 @@
   void flush() {};
 };
 
+class logStream : public outputStream {
+private:
+  stringStream _current_line;
+  void (*_log_func)(const char* fmt, ...);
+public:
+  void write(const char* s, size_t len);
+  logStream(void (*log_func)(const char* fmt, ...)) : _log_func(log_func) {}
+  ~logStream() {
+    guarantee(_current_line.size() == 0, "Buffer not flushed. Missing call to print_cr()?");
+  }
+};
+
 class gcLogFileStream : public fileStream {
  protected:
   const char*  _file_name;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/serviceability/logging/TestBasicLogOutput.java	Thu Sep 24 12:36:04 2015 +0200
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestBasicLogOutput
+ * @summary Ensure logging can be enabled and successfully prints to stdout.
+ * @library /testlibrary
+ */
+
+import jdk.test.lib.ProcessTools;
+import jdk.test.lib.OutputAnalyzer;
+
+public class TestBasicLogOutput {
+
+    public static void main(String[] args) throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:all=trace", "-version");
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        output.shouldContain("[logging]"); // expected tag(s)
+        output.shouldContain("Log configuration fully initialized."); // expected message
+        output.shouldHaveExitValue(0);
+    }
+}
+