1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 #include "precompiled.hpp"
  25 #include "logging/log.hpp"
  26 #include "logging/logConfiguration.hpp"
  27 #include "logging/logDecorations.hpp"
  28 #include "logging/logDecorators.hpp"
  29 #include "logging/logDiagnosticCommand.hpp"
  30 #include "logging/logFileOutput.hpp"
  31 #include "logging/logOutput.hpp"
  32 #include "logging/logTagLevelExpression.hpp"
  33 #include "logging/logTagSet.hpp"
  34 #include "memory/allocation.inline.hpp"
  35 #include "memory/resourceArea.hpp"
  36 #include "runtime/os.inline.hpp"
  37 #include "utilities/globalDefinitions.hpp"
  38 
  39 LogOutput** LogConfiguration::_outputs = NULL;
  40 size_t      LogConfiguration::_n_outputs = 0;
  41 
  42 void LogConfiguration::post_initialize() {
  43   assert(LogConfiguration_lock != NULL, "Lock must be initialized before post-initialization");
  44   LogDiagnosticCommand::registerCommand();
  45   LogHandle(logging) log;
  46   log.info("Log configuration fully initialized.");
  47   if (log.is_trace()) {
  48     ResourceMark rm;
  49     MutexLocker ml(LogConfiguration_lock);
  50     describe(log.trace_stream());
  51   }
  52 }
  53 
  54 void LogConfiguration::initialize(jlong vm_start_time) {
  55   LogFileOutput::set_file_name_parameters(vm_start_time);
  56   LogDecorations::set_vm_start_time_millis(vm_start_time);
  57 
  58   assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
  59   _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
  60   _outputs[0] = LogOutput::Stdout;
  61   _outputs[1] = LogOutput::Stderr;
  62   _n_outputs = 2;
  63 }
  64 
  65 void LogConfiguration::finalize() {
  66   for (size_t i = 2; i < _n_outputs; i++) {
  67     delete _outputs[i];
  68   }
  69   FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
  70 }
  71 
  72 size_t LogConfiguration::find_output(const char* name) {
  73   for (size_t i = 0; i < _n_outputs; i++) {
  74     if (strcmp(_outputs[i]->name(), name) == 0) {
  75       return i;
  76     }
  77   }
  78   return SIZE_MAX;
  79 }
  80 
  81 LogOutput* LogConfiguration::new_output(char* name, const char* options) {
  82   const char* type;
  83   char* equals_pos = strchr(name, '=');
  84   if (equals_pos == NULL) {
  85     type = "file";
  86   } else {
  87     *equals_pos = '\0';
  88     type = name;
  89     name = equals_pos + 1;
  90   }
  91 
  92   LogOutput* output;
  93   if (strcmp(type, "file") == 0) {
  94     output = new LogFileOutput(name);
  95   } else {
  96     // unsupported log output type
  97     return NULL;
  98   }
  99 
 100   bool success = output->initialize(options);
 101   if (!success) {
 102     delete output;
 103     return NULL;
 104   }
 105   return output;
 106 }
 107 
 108 size_t LogConfiguration::add_output(LogOutput* output) {
 109   size_t idx = _n_outputs++;
 110   _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
 111   _outputs[idx] = output;
 112   return idx;
 113 }
 114 
 115 void LogConfiguration::delete_output(size_t idx) {
 116   assert(idx > 1 && idx < _n_outputs,
 117          "idx must be in range 1 < idx < _n_outputs, but idx = " SIZE_FORMAT
 118          " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs);
 119   LogOutput* output = _outputs[idx];
 120   // Swap places with the last output and shrink the array
 121   _outputs[idx] = _outputs[--_n_outputs];
 122   _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
 123   delete output;
 124 }
 125 
 126 void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) {
 127   assert(idx < _n_outputs, "Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs);
 128   LogOutput* output = _outputs[idx];
 129   output->set_decorators(decorators);
 130   output->set_config_string(tag_level_expression.to_string());
 131   bool enabled = false;
 132   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
 133     LogLevelType level = tag_level_expression.level_for(*ts);
 134     if (level != LogLevel::Off) {
 135       enabled = true;
 136     }
 137     ts->update_decorators(decorators);
 138     ts->set_output_level(output, level);
 139   }
 140 
 141   // If the output is not used by any tagset it should be removed, unless it is stdout/stderr.
 142   if (!enabled && idx > 1) {
 143     delete_output(idx);
 144   }
 145 }
 146 
 147 void LogConfiguration::disable_output(size_t idx) {
 148   LogOutput* out = _outputs[idx];
 149   LogDecorators empty_decorators;
 150   empty_decorators.clear();
 151 
 152   // Remove the output from all tagsets.
 153   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
 154     ts->set_output_level(out, LogLevel::Off);
 155     ts->update_decorators(empty_decorators);
 156   }
 157 
 158   // Delete the output unless stdout/stderr
 159   if (out != LogOutput::Stderr && out != LogOutput::Stdout) {
 160     delete_output(find_output(out->name()));
 161   } else {
 162     out->set_config_string("all=off");
 163   }
 164 }
 165 
 166 void LogConfiguration::disable_logging() {
 167   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 168          "LogConfiguration lock must be held when calling this function");
 169   for (size_t i = 0; i < _n_outputs; i++) {
 170     disable_output(i);
 171   }
 172 }
 173 
 174 bool LogConfiguration::parse_command_line_arguments(const char* opts) {
 175   char* copy = os::strdup_check_oom(opts, mtLogging);
 176 
 177   // Split the option string to its colon separated components.
 178   char* what = NULL;
 179   char* output_str = NULL;
 180   char* decorators_str = NULL;
 181   char* output_options = NULL;
 182 
 183   what = copy;
 184   char* colon = strchr(what, ':');
 185   if (colon != NULL) {
 186     *colon = '\0';
 187     output_str = colon + 1;
 188     colon = strchr(output_str, ':');
 189     if (colon != NULL) {
 190       *colon = '\0';
 191       decorators_str = colon + 1;
 192       colon = strchr(decorators_str, ':');
 193       if (colon != NULL) {
 194         *colon = '\0';
 195         output_options = colon + 1;
 196       }
 197     }
 198   }
 199 
 200   // Parse each argument
 201   char errbuf[512];
 202   stringStream ss(errbuf, sizeof(errbuf));
 203   bool success = parse_log_arguments(output_str, what, decorators_str, output_options, &ss);
 204   if (!success) {
 205     errbuf[strlen(errbuf) - 1] = '\0'; // Strip trailing newline.
 206     log_error(logging)("%s", errbuf);
 207   }
 208 
 209   os::free(copy);
 210   return success;
 211 }
 212 
 213 bool LogConfiguration::parse_log_arguments(const char* outputstr,
 214                                            const char* what,
 215                                            const char* decoratorstr,
 216                                            const char* output_options,
 217                                            outputStream* errstream) {
 218   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 219          "LogConfiguration lock must be held when calling this function");
 220   if (outputstr == NULL || strlen(outputstr) == 0) {
 221     outputstr = "stdout";
 222   }
 223 
 224   size_t idx;
 225   if (outputstr[0] == '#') {
 226     int ret = sscanf(outputstr+1, SIZE_FORMAT, &idx);
 227     if (ret != 1 || idx >= _n_outputs) {
 228       errstream->print_cr("Invalid output index '%s'", outputstr);
 229       return false;
 230     }
 231   } else {
 232     idx = find_output(outputstr);
 233     if (idx == SIZE_MAX) {
 234       char* tmp = os::strdup_check_oom(outputstr, mtLogging);
 235       LogOutput* output = new_output(tmp, output_options);
 236       os::free(tmp);
 237       if (output == NULL) {
 238         errstream->print("Unable to add output '%s'", outputstr);
 239         if (output_options != NULL && strlen(output_options) > 0) {
 240           errstream->print(" with options '%s'", output_options);
 241         }
 242         errstream->cr();
 243         return false;
 244       }
 245       idx = add_output(output);
 246     } else if (output_options != NULL && strlen(output_options) > 0) {
 247       errstream->print_cr("Output options for existing outputs are ignored.");
 248     }
 249   }
 250 
 251   LogTagLevelExpression expr;
 252   if (!expr.parse(what, errstream)) {
 253     return false;
 254   }
 255 
 256   LogDecorators decorators;
 257   if (!decorators.parse(decoratorstr, errstream)) {
 258     return false;
 259   }
 260 
 261   configure_output(idx, expr, decorators);
 262   return true;
 263 }
 264 
 265 void LogConfiguration::describe(outputStream* out) {
 266   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 267          "LogConfiguration lock must be held when calling this function");
 268 
 269   out->print("Available log levels:");
 270   for (size_t i = 0; i < LogLevel::Count; i++) {
 271     out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
 272   }
 273   out->cr();
 274 
 275   out->print("Available log decorators:");
 276   for (size_t i = 0; i < LogDecorators::Count; i++) {
 277     LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
 278     out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
 279   }
 280   out->cr();
 281 
 282   out->print("Available log tags:");
 283   for (size_t i = 1; i < LogTag::Count; i++) {
 284     out->print("%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
 285   }
 286   out->cr();
 287 
 288   out->print_cr("Log output configuration:");
 289   for (size_t i = 0; i < _n_outputs; i++) {
 290     out->print("#" SIZE_FORMAT ": %s %s ", i, _outputs[i]->name(), _outputs[i]->config_string());
 291     for (size_t d = 0; d < LogDecorators::Count; d++) {
 292       LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);
 293       if (_outputs[i]->decorators().is_decorator(decorator)) {
 294         out->print("%s,", LogDecorators::name(decorator));
 295       }
 296     }
 297     out->cr();
 298   }
 299 }
 300 
 301 void LogConfiguration::print_command_line_help(FILE* out) {
 302   jio_fprintf(out, "-Xlog Usage: -Xlog[:[what][:[output][:[decorators][:output-options]]]]\n"
 303               "\t where 'what' is a combination of tags and levels on the form tag1[+tag2...][*][=level][,...]\n"
 304               "\t Unless wildcard (*) is specified, only log messages tagged with exactly the tags specified will be matched.\n\n");
 305 
 306   jio_fprintf(out, "Available log levels:\n");
 307   for (size_t i = 0; i < LogLevel::Count; i++) {
 308     jio_fprintf(out, "%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
 309   }
 310 
 311   jio_fprintf(out, "\n\nAvailable log decorators: \n");
 312   for (size_t i = 0; i < LogDecorators::Count; i++) {
 313     LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
 314     jio_fprintf(out, "%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
 315   }
 316   jio_fprintf(out, "\n Decorators can also be specified as 'none' for no decoration.\n\n");
 317 
 318   jio_fprintf(out, "Available log tags:\n");
 319   for (size_t i = 1; i < LogTag::Count; i++) {
 320     jio_fprintf(out, "%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
 321   }
 322   jio_fprintf(out, "\n Specifying 'all' instead of a tag combination matches all tag combinations.\n\n");
 323 
 324   jio_fprintf(out, "Available log outputs:\n"
 325               " stdout, stderr, file=<filename>\n"
 326               " Specifying %%p and/or %%t in the filename will expand to the JVM's PID and startup timestamp, respectively.\n\n"
 327 
 328               "Some examples:\n"
 329               " -Xlog\n"
 330               "\t Log all messages using 'info' level to stdout with 'uptime', 'levels' and 'tags' decorations.\n"
 331               "\t (Equivalent to -Xlog:all=info:stdout:uptime,levels,tags).\n\n"
 332 
 333               " -Xlog:gc\n"
 334               "\t Log messages tagged with 'gc' tag using 'info' level to stdout, with default decorations.\n\n"
 335 
 336               " -Xlog:gc=debug:file=gc.txt:none\n"
 337               "\t Log messages tagged with 'gc' tag using 'debug' level to file 'gc.txt' with no decorations.\n\n"
 338 
 339               " -Xlog:gc=trace:file=gctrace.txt:uptimemillis,pids:filecount=5,filesize=1024\n"
 340               "\t Log messages tagged with 'gc' tag using 'trace' level to a rotating fileset of 5 files of size 1MB,\n"
 341               "\t using the base name 'gctrace.txt', with 'uptimemillis' and 'pid' decorations.\n\n"
 342 
 343               " -Xlog:gc::uptime,tid\n"
 344               "\t Log messages tagged with 'gc' tag using 'info' level to output 'stdout', using 'uptime' and 'tid' decorations.\n\n"
 345 
 346               " -Xlog:gc*=info,rt*=off\n"
 347               "\t Log messages tagged with at least 'gc' using 'info' level, but turn off logging of messages tagged with 'rt'.\n"
 348               "\t (Messages tagged with both 'gc' and 'rt' will not be logged.)\n\n"
 349 
 350               " -Xlog:disable -Xlog:rt=trace:rttrace.txt\n"
 351               "\t Turn off all logging, including warnings and errors,\n"
 352               "\t and then enable messages tagged with 'rt' using 'trace' level to file 'rttrace.txt'.\n");
 353 }