hotspot/src/share/vm/logging/logFileOutput.cpp
changeset 37465 1d5551f466ee
parent 37113 5a33bf5089ac
child 38065 025c784d9333
equal deleted inserted replaced
37464:5373bbadb433 37465:1d5551f466ee
    39 char        LogFileOutput::_pid_str[PidBufferSize];
    39 char        LogFileOutput::_pid_str[PidBufferSize];
    40 char        LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
    40 char        LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
    41 
    41 
    42 LogFileOutput::LogFileOutput(const char* name)
    42 LogFileOutput::LogFileOutput(const char* name)
    43     : LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
    43     : LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
    44       _file_name(NULL), _archive_name(NULL), _archive_name_len(0), _current_size(0),
    44       _file_name(NULL), _archive_name(NULL), _archive_name_len(0),
    45       _rotate_size(0), _current_file(1), _file_count(0), _rotation_semaphore(1) {
    45       _rotate_size(DefaultFileSize), _file_count(DefaultFileCount),
       
    46       _current_size(0), _current_file(0), _rotation_semaphore(1) {
    46   _file_name = make_file_name(name, _pid_str, _vm_start_time_str);
    47   _file_name = make_file_name(name, _pid_str, _vm_start_time_str);
    47 }
    48 }
    48 
    49 
    49 void LogFileOutput::set_file_name_parameters(jlong vm_start_time) {
    50 void LogFileOutput::set_file_name_parameters(jlong vm_start_time) {
    50   int res = jio_snprintf(_pid_str, sizeof(_pid_str), "%d", os::current_process_id());
    51   int res = jio_snprintf(_pid_str, sizeof(_pid_str), "%d", os::current_process_id());
    57   assert(res > 0, "VM start time buffer too small.");
    58   assert(res > 0, "VM start time buffer too small.");
    58 }
    59 }
    59 
    60 
    60 LogFileOutput::~LogFileOutput() {
    61 LogFileOutput::~LogFileOutput() {
    61   if (_stream != NULL) {
    62   if (_stream != NULL) {
    62     if (_archive_name != NULL) {
       
    63       archive();
       
    64     }
       
    65     if (fclose(_stream) != 0) {
    63     if (fclose(_stream) != 0) {
    66       jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
    64       jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
    67                   _file_name, os::strerror(errno));
    65                   _file_name, os::strerror(errno));
    68     }
    66     }
    69   }
    67   }
    70   os::free(_archive_name);
    68   os::free(_archive_name);
    71   os::free(_file_name);
    69   os::free(_file_name);
    72   os::free(const_cast<char*>(_name));
    70   os::free(const_cast<char*>(_name));
    73 }
    71 }
    74 
    72 
    75 size_t LogFileOutput::parse_value(const char* value_str) {
    73 static size_t parse_value(const char* value_str) {
    76   char* end;
    74   char* end;
    77   unsigned long long value = strtoull(value_str, &end, 10);
    75   unsigned long long value = strtoull(value_str, &end, 10);
    78   if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
    76   if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
    79     return SIZE_MAX;
    77     return SIZE_MAX;
    80   }
    78   }
    81   return value;
    79   return value;
    82 }
    80 }
    83 
    81 
    84 bool LogFileOutput::configure_rotation(const char* options) {
    82 static bool file_exists(const char* filename) {
       
    83   struct stat dummy_stat;
       
    84   return os::stat(filename, &dummy_stat) == 0;
       
    85 }
       
    86 
       
    87 static uint number_of_digits(uint number) {
       
    88   return number < 10 ? 1 : (number < 100 ? 2 : 3);
       
    89 }
       
    90 
       
    91 static bool is_regular_file(const char* filename) {
       
    92   struct stat st;
       
    93   int ret = os::stat(filename, &st);
       
    94   if (ret != 0) {
       
    95     return false;
       
    96   }
       
    97 #ifdef _WINDOWS
       
    98   return (st.st_mode & S_IFMT) == _S_IFREG;
       
    99 #else
       
   100   return S_ISREG(st.st_mode);
       
   101 #endif
       
   102 }
       
   103 
       
   104 // Try to find the next number that should be used for file rotation.
       
   105 // Return UINT_MAX on error.
       
   106 static uint next_file_number(const char* filename,
       
   107                              uint number_of_digits,
       
   108                              uint filecount,
       
   109                              outputStream* errstream) {
       
   110   bool found = false;
       
   111   uint next_num = 0;
       
   112 
       
   113   // len is filename + dot + digits + null char
       
   114   size_t len = strlen(filename) + number_of_digits + 2;
       
   115   char* archive_name = NEW_C_HEAP_ARRAY(char, len, mtLogging);
       
   116   char* oldest_name = NEW_C_HEAP_ARRAY(char, len, mtLogging);
       
   117 
       
   118   for (uint i = 0; i < filecount; i++) {
       
   119     int ret = jio_snprintf(archive_name, len, "%s.%0*u",
       
   120                            filename, number_of_digits, i);
       
   121     assert(ret > 0 && static_cast<size_t>(ret) == len - 1,
       
   122            "incorrect buffer length calculation");
       
   123 
       
   124     if (file_exists(archive_name) && !is_regular_file(archive_name)) {
       
   125       // We've encountered something that's not a regular file among the
       
   126       // possible file rotation targets. Fail immediately to prevent
       
   127       // problems later.
       
   128       errstream->print_cr("Possible rotation target file '%s' already exists "
       
   129                           "but is not a regular file.", archive_name);
       
   130       next_num = UINT_MAX;
       
   131       break;
       
   132     }
       
   133 
       
   134     // Stop looking if we find an unused file name
       
   135     if (!file_exists(archive_name)) {
       
   136       next_num = i;
       
   137       found = true;
       
   138       break;
       
   139     }
       
   140 
       
   141     // Keep track of oldest existing log file
       
   142     if (!found
       
   143         || os::compare_file_modified_times(oldest_name, archive_name) > 0) {
       
   144       strcpy(oldest_name, archive_name);
       
   145       next_num = i;
       
   146       found = true;
       
   147     }
       
   148   }
       
   149 
       
   150   FREE_C_HEAP_ARRAY(char, oldest_name);
       
   151   FREE_C_HEAP_ARRAY(char, archive_name);
       
   152   return next_num;
       
   153 }
       
   154 
       
   155 bool LogFileOutput::parse_options(const char* options, outputStream* errstream) {
    85   if (options == NULL || strlen(options) == 0) {
   156   if (options == NULL || strlen(options) == 0) {
    86     return true;
   157     return true;
    87   }
   158   }
    88   bool success = true;
   159   bool success = true;
    89   char* opts = os::strdup_check_oom(options, mtLogging);
   160   char* opts = os::strdup_check_oom(options, mtLogging);
   105     char* value_str = equals_pos + 1;
   176     char* value_str = equals_pos + 1;
   106     *equals_pos = '\0';
   177     *equals_pos = '\0';
   107 
   178 
   108     if (strcmp(FileCountOptionKey, key) == 0) {
   179     if (strcmp(FileCountOptionKey, key) == 0) {
   109       size_t value = parse_value(value_str);
   180       size_t value = parse_value(value_str);
   110       if (value == SIZE_MAX || value >= UINT_MAX) {
   181       if (value > MaxRotationFileCount) {
       
   182         errstream->print_cr("Invalid option: %s must be in range [0, %u]",
       
   183                             FileCountOptionKey,
       
   184                             MaxRotationFileCount);
   111         success = false;
   185         success = false;
   112         break;
   186         break;
   113       }
   187       }
   114       _file_count = static_cast<uint>(value);
   188       _file_count = static_cast<uint>(value);
   115       _file_count_max_digits = static_cast<uint>(log10(static_cast<double>(_file_count)) + 1);
       
   116       _archive_name_len = 2 + strlen(_file_name) + _file_count_max_digits;
       
   117       _archive_name = NEW_C_HEAP_ARRAY(char, _archive_name_len, mtLogging);
       
   118     } else if (strcmp(FileSizeOptionKey, key) == 0) {
   189     } else if (strcmp(FileSizeOptionKey, key) == 0) {
   119       size_t value = parse_value(value_str);
   190       size_t value = parse_value(value_str);
   120       if (value == SIZE_MAX || value > SIZE_MAX / K) {
   191       if (value == SIZE_MAX || value > SIZE_MAX / K) {
       
   192         errstream->print_cr("Invalid option: %s must be in range [0, "
       
   193                             SIZE_FORMAT "]", FileSizeOptionKey, SIZE_MAX / K);
   121         success = false;
   194         success = false;
   122         break;
   195         break;
   123       }
   196       }
   124       _rotate_size = value * K;
   197       _rotate_size = value * K;
   125     } else {
   198     } else {
       
   199       errstream->print_cr("Invalid option '%s' for log file output.", key);
   126       success = false;
   200       success = false;
   127       break;
   201       break;
   128     }
   202     }
   129     pos = comma_pos + 1;
   203     pos = comma_pos + 1;
   130   } while (comma_pos != NULL);
   204   } while (comma_pos != NULL);
   131 
   205 
   132   os::free(opts);
   206   os::free(opts);
   133   return success;
   207   return success;
   134 }
   208 }
   135 
   209 
   136 bool LogFileOutput::initialize(const char* options) {
   210 bool LogFileOutput::initialize(const char* options, outputStream* errstream) {
   137   if (!configure_rotation(options)) {
   211   if (!parse_options(options, errstream)) {
   138     return false;
   212     return false;
   139   }
   213   }
       
   214 
       
   215   if (_file_count > 0) {
       
   216     // compute digits with filecount - 1 since numbers will start from 0
       
   217     _file_count_max_digits = number_of_digits(_file_count - 1);
       
   218     _archive_name_len = 2 + strlen(_file_name) + _file_count_max_digits;
       
   219     _archive_name = NEW_C_HEAP_ARRAY(char, _archive_name_len, mtLogging);
       
   220   }
       
   221 
       
   222   log_trace(logging)("Initializing logging to file '%s' (filecount: %u"
       
   223                      ", filesize: " SIZE_FORMAT " KiB).",
       
   224                      _file_name, _file_count, _rotate_size / K);
       
   225 
       
   226   if (_file_count > 0 && file_exists(_file_name)) {
       
   227     if (!is_regular_file(_file_name)) {
       
   228       errstream->print_cr("Unable to log to file %s with log file rotation: "
       
   229                           "%s is not a regular file",
       
   230                           _file_name, _file_name);
       
   231       return false;
       
   232     }
       
   233     _current_file = next_file_number(_file_name,
       
   234                                      _file_count_max_digits,
       
   235                                      _file_count,
       
   236                                      errstream);
       
   237     if (_current_file == UINT_MAX) {
       
   238       return false;
       
   239     }
       
   240     log_trace(logging)("Existing log file found, saving it as '%s.%0*u'",
       
   241                        _file_name, _file_count_max_digits, _current_file);
       
   242     archive();
       
   243     increment_file_count();
       
   244   }
       
   245 
   140   _stream = fopen(_file_name, FileOpenMode);
   246   _stream = fopen(_file_name, FileOpenMode);
   141   if (_stream == NULL) {
   247   if (_stream == NULL) {
   142     log_error(logging)("Could not open log file '%s' (%s).\n", _file_name, os::strerror(errno));
   248     errstream->print_cr("Error opening log file '%s': %s",
       
   249                         _file_name, strerror(errno));
   143     return false;
   250     return false;
   144   }
   251   }
       
   252 
       
   253   if (_file_count == 0 && is_regular_file(_file_name)) {
       
   254     log_trace(logging)("Truncating log file");
       
   255     os::ftruncate(os::fileno(_stream), 0);
       
   256   }
       
   257 
   145   return true;
   258   return true;
   146 }
   259 }
   147 
   260 
   148 int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
   261 int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
   149   if (_stream == NULL) {
   262   if (_stream == NULL) {
   208     return;
   321     return;
   209   }
   322   }
   210 
   323 
   211   // Reset accumulated size, increase current file counter, and check for file count wrap-around.
   324   // Reset accumulated size, increase current file counter, and check for file count wrap-around.
   212   _current_size = 0;
   325   _current_size = 0;
   213   _current_file = (_current_file >= _file_count ? 1 : _current_file + 1);
   326   increment_file_count();
   214 }
   327 }
   215 
   328 
   216 char* LogFileOutput::make_file_name(const char* file_name,
   329 char* LogFileOutput::make_file_name(const char* file_name,
   217                                     const char* pid_string,
   330                                     const char* pid_string,
   218                                     const char* timestamp_string) {
   331                                     const char* timestamp_string) {