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 bool        LogConfiguration::_post_initialized = false;
  42 
  43 void LogConfiguration::post_initialize() {
  44   assert(LogConfiguration_lock != NULL, "Lock must be initialized before post-initialization");
  45   LogDiagnosticCommand::registerCommand();
  46   LogHandle(logging) log;
  47   log.info("Log configuration fully initialized.");
  48   if (log.is_trace()) {
  49     ResourceMark rm;
  50     MutexLocker ml(LogConfiguration_lock);
  51     describe(log.trace_stream());
  52   }
  53 
  54   _post_initialized = true;
  55 }
  56 
  57 void LogConfiguration::initialize(jlong vm_start_time) {
  58   LogFileOutput::set_file_name_parameters(vm_start_time);
  59   LogDecorations::set_vm_start_time_millis(vm_start_time);
  60 
  61   assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
  62   _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
  63   _outputs[0] = LogOutput::Stdout;
  64   _outputs[1] = LogOutput::Stderr;
  65   _n_outputs = 2;
  66 }
  67 
  68 void LogConfiguration::finalize() {
  69   for (size_t i = 2; i < _n_outputs; i++) {
  70     delete _outputs[i];
  71   }
  72   FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
  73 }
  74 
  75 size_t LogConfiguration::find_output(const char* name) {
  76   for (size_t i = 0; i < _n_outputs; i++) {
  77     if (strcmp(_outputs[i]->name(), name) == 0) {
  78       return i;
  79     }
  80   }
  81   return SIZE_MAX;
  82 }
  83 
  84 LogOutput* LogConfiguration::new_output(char* name, const char* options) {
  85   const char* type;
  86   char* equals_pos = strchr(name, '=');
  87   if (equals_pos == NULL) {
  88     type = "file";
  89   } else {
  90     *equals_pos = '\0';
  91     type = name;
  92     name = equals_pos + 1;
  93   }
  94 
  95   LogOutput* output;
  96   if (strcmp(type, "file") == 0) {
  97     output = new LogFileOutput(name);
  98   } else {
  99     // unsupported log output type
 100     return NULL;
 101   }
 102 
 103   bool success = output->initialize(options);
 104   if (!success) {
 105     delete output;
 106     return NULL;
 107   }
 108   return output;
 109 }
 110 
 111 size_t LogConfiguration::add_output(LogOutput* output) {
 112   size_t idx = _n_outputs++;
 113   _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
 114   _outputs[idx] = output;
 115   return idx;
 116 }
 117 
 118 void LogConfiguration::delete_output(size_t idx) {
 119   assert(idx > 1 && idx < _n_outputs,
 120          "idx must be in range 1 < idx < _n_outputs, but idx = " SIZE_FORMAT
 121          " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs);
 122   LogOutput* output = _outputs[idx];
 123   // Swap places with the last output and shrink the array
 124   _outputs[idx] = _outputs[--_n_outputs];
 125   _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
 126   delete output;
 127 }
 128 
 129 void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) {
 130   assert(idx < _n_outputs, "Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs);
 131   LogOutput* output = _outputs[idx];
 132   output->set_decorators(decorators);
 133   output->set_config_string(tag_level_expression.to_string());
 134   bool enabled = false;
 135   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
 136     LogLevelType level = tag_level_expression.level_for(*ts);
 137     if (level != LogLevel::Off) {
 138       enabled = true;
 139     }
 140     ts->update_decorators(decorators);
 141     ts->set_output_level(output, level);
 142   }
 143 
 144   // If the output is not used by any tagset it should be removed, unless it is stdout/stderr.
 145   if (!enabled && idx > 1) {
 146     delete_output(idx);
 147   }
 148 }
 149 
 150 void LogConfiguration::disable_output(size_t idx) {
 151   LogOutput* out = _outputs[idx];
 152   LogDecorators empty_decorators;
 153   empty_decorators.clear();
 154 
 155   // Remove the output from all tagsets.
 156   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
 157     ts->set_output_level(out, LogLevel::Off);
 158     ts->update_decorators(empty_decorators);
 159   }
 160 
 161   // Delete the output unless stdout/stderr
 162   if (out != LogOutput::Stderr && out != LogOutput::Stdout) {
 163     delete_output(find_output(out->name()));
 164   } else {
 165     out->set_config_string("all=off");
 166   }
 167 }
 168 
 169 void LogConfiguration::disable_logging() {
 170   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 171          "LogConfiguration lock must be held when calling this function");
 172   for (size_t i = 0; i < _n_outputs; i++) {
 173     disable_output(i);
 174   }
 175 }
 176 
 177 bool LogConfiguration::parse_command_line_arguments(const char* opts) {
 178   char* copy = os::strdup_check_oom(opts, mtLogging);
 179 
 180   // Split the option string to its colon separated components.
 181   char* what = NULL;
 182   char* output_str = NULL;
 183   char* decorators_str = NULL;
 184   char* output_options = NULL;
 185 
 186   what = copy;
 187   char* colon = strchr(what, ':');
 188   if (colon != NULL) {
 189     *colon = '\0';
 190     output_str = colon + 1;
 191     colon = strchr(output_str, ':');
 192     if (colon != NULL) {
 193       *colon = '\0';
 194       decorators_str = colon + 1;
 195       colon = strchr(decorators_str, ':');
 196       if (colon != NULL) {
 197         *colon = '\0';
 198         output_options = colon + 1;
 199       }
 200     }
 201   }
 202 
 203   // Parse each argument
 204   char errbuf[512];
 205   stringStream ss(errbuf, sizeof(errbuf));
 206   bool success = parse_log_arguments(output_str, what, decorators_str, output_options, &ss);
 207   if (!success) {
 208     errbuf[strlen(errbuf) - 1] = '\0'; // Strip trailing newline.
 209     log_error(logging)("%s", errbuf);
 210   }
 211 
 212   os::free(copy);
 213   return success;
 214 }
 215 
 216 bool LogConfiguration::parse_log_arguments(const char* outputstr,
 217                                            const char* what,
 218                                            const char* decoratorstr,
 219                                            const char* output_options,
 220                                            outputStream* errstream) {
 221   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 222          "LogConfiguration lock must be held when calling this function");
 223   if (outputstr == NULL || strlen(outputstr) == 0) {
 224     outputstr = "stdout";
 225   }
 226 
 227   size_t idx;
 228   if (outputstr[0] == '#') {
 229     int ret = sscanf(outputstr+1, SIZE_FORMAT, &idx);
 230     if (ret != 1 || idx >= _n_outputs) {
 231       errstream->print_cr("Invalid output index '%s'", outputstr);
 232       return false;
 233     }
 234   } else {
 235     idx = find_output(outputstr);
 236     if (idx == SIZE_MAX) {
 237       char* tmp = os::strdup_check_oom(outputstr, mtLogging);
 238       LogOutput* output = new_output(tmp, output_options);
 239       os::free(tmp);
 240       if (output == NULL) {
 241         errstream->print("Unable to add output '%s'", outputstr);
 242         if (output_options != NULL && strlen(output_options) > 0) {
 243           errstream->print(" with options '%s'", output_options);
 244         }
 245         errstream->cr();
 246         return false;
 247       }
 248       idx = add_output(output);
 249     } else if (output_options != NULL && strlen(output_options) > 0) {
 250       errstream->print_cr("Output options for existing outputs are ignored.");
 251     }
 252   }
 253 
 254   LogTagLevelExpression expr;
 255   if (!expr.parse(what, errstream)) {
 256     return false;
 257   }
 258 
 259   LogDecorators decorators;
 260   if (!decorators.parse(decoratorstr, errstream)) {
 261     return false;
 262   }
 263 
 264   configure_output(idx, expr, decorators);
 265   return true;
 266 }
 267 
 268 void LogConfiguration::describe(outputStream* out) {
 269   assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
 270          "LogConfiguration lock must be held when calling this function");
 271 
 272   out->print("Available log levels:");
 273   for (size_t i = 0; i < LogLevel::Count; i++) {
 274     out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
 275   }
 276   out->cr();
 277 
 278   out->print("Available log decorators:");
 279   for (size_t i = 0; i < LogDecorators::Count; i++) {
 280     LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
 281     out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
 282   }
 283   out->cr();
 284 
 285   out->print("Available log tags:");
 286   for (size_t i = 1; i < LogTag::Count; i++) {
 287     out->print("%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
 288   }
 289   out->cr();
 290 
 291   out->print_cr("Log output configuration:");
 292   for (size_t i = 0; i < _n_outputs; i++) {
 293     out->print("#" SIZE_FORMAT ": %s %s ", i, _outputs[i]->name(), _outputs[i]->config_string());
 294     for (size_t d = 0; d < LogDecorators::Count; d++) {
 295       LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);
 296       if (_outputs[i]->decorators().is_decorator(decorator)) {
 297         out->print("%s,", LogDecorators::name(decorator));
 298       }
 299     }
 300     out->cr();
 301   }
 302 }
 303 
 304 void LogConfiguration::print_command_line_help(FILE* out) {
 305   jio_fprintf(out, "-Xlog Usage: -Xlog[:[what][:[output][:[decorators][:output-options]]]]\n"
 306               "\t where 'what' is a combination of tags and levels on the form tag1[+tag2...][*][=level][,...]\n"
 307               "\t Unless wildcard (*) is specified, only log messages tagged with exactly the tags specified will be matched.\n\n");
 308 
 309   jio_fprintf(out, "Available log levels:\n");
 310   for (size_t i = 0; i < LogLevel::Count; i++) {
 311     jio_fprintf(out, "%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
 312   }
 313 
 314   jio_fprintf(out, "\n\nAvailable log decorators: \n");
 315   for (size_t i = 0; i < LogDecorators::Count; i++) {
 316     LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
 317     jio_fprintf(out, "%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
 318   }
 319   jio_fprintf(out, "\n Decorators can also be specified as 'none' for no decoration.\n\n");
 320 
 321   jio_fprintf(out, "Available log tags:\n");
 322   for (size_t i = 1; i < LogTag::Count; i++) {
 323     jio_fprintf(out, "%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
 324   }
 325   jio_fprintf(out, "\n Specifying 'all' instead of a tag combination matches all tag combinations.\n\n");
 326 
 327   jio_fprintf(out, "Available log outputs:\n"
 328               " stdout, stderr, file=<filename>\n"
 329               " Specifying %%p and/or %%t in the filename will expand to the JVM's PID and startup timestamp, respectively.\n\n"
 330 
 331               "Some examples:\n"
 332               " -Xlog\n"
 333               "\t Log all messages using 'info' level to stdout with 'uptime', 'levels' and 'tags' decorations.\n"
 334               "\t (Equivalent to -Xlog:all=info:stdout:uptime,levels,tags).\n\n"
 335 
 336               " -Xlog:gc\n"
 337               "\t Log messages tagged with 'gc' tag using 'info' level to stdout, with default decorations.\n\n"
 338 
 339               " -Xlog:gc=debug:file=gc.txt:none\n"
 340               "\t Log messages tagged with 'gc' tag using 'debug' level to file 'gc.txt' with no decorations.\n\n"
 341 
 342               " -Xlog:gc=trace:file=gctrace.txt:uptimemillis,pids:filecount=5,filesize=1024\n"
 343               "\t Log messages tagged with 'gc' tag using 'trace' level to a rotating fileset of 5 files of size 1MB,\n"
 344               "\t using the base name 'gctrace.txt', with 'uptimemillis' and 'pid' decorations.\n\n"
 345 
 346               " -Xlog:gc::uptime,tid\n"
 347               "\t Log messages tagged with 'gc' tag using 'info' level to output 'stdout', using 'uptime' and 'tid' decorations.\n\n"
 348 
 349               " -Xlog:gc*=info,rt*=off\n"
 350               "\t Log messages tagged with at least 'gc' using 'info' level, but turn off logging of messages tagged with 'rt'.\n"
 351               "\t (Messages tagged with both 'gc' and 'rt' will not be logged.)\n\n"
 352 
 353               " -Xlog:disable -Xlog:rt=trace:rttrace.txt\n"
 354               "\t Turn off all logging, including warnings and errors,\n"
 355               "\t and then enable messages tagged with 'rt' using 'trace' level to file 'rttrace.txt'.\n");
 356 }
 357 
 358 void LogConfiguration::rotate_all_outputs() {
 359   for (size_t idx = 0; idx < _n_outputs; idx++) {
 360     if (_outputs[idx]->is_rotatable()) {
 361       _outputs[idx]->rotate(true);
 362     }
 363   }
 364 }
 365