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