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/logFileOutput.hpp"
28 #include "memory/allocation.inline.hpp"
29 #include "runtime/os.inline.hpp"
30 #include "utilities/globalDefinitions.hpp"
31 #include "utilities/defaultStream.hpp"
32
33 const char* LogFileOutput::FileOpenMode = "a";
34 const char* LogFileOutput::PidFilenamePlaceholder = "%p";
35 const char* LogFileOutput::TimestampFilenamePlaceholder = "%t";
36 const char* LogFileOutput::TimestampFormat = "%Y-%m-%d_%H-%M-%S";
37 const char* LogFileOutput::FileSizeOptionKey = "filesize";
38 const char* LogFileOutput::FileCountOptionKey = "filecount";
39 char LogFileOutput::_pid_str[PidBufferSize];
40 char LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
41
42 LogFileOutput::LogFileOutput(const char* name)
43 : LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
44 _file_name(NULL), _archive_name(NULL), _archive_name_len(0), _current_size(0),
45 _rotate_size(0), _current_file(1), _file_count(0), _rotation_semaphore(1) {
46 _file_name = make_file_name(name, _pid_str, _vm_start_time_str);
47 }
48
49 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 assert(res > 0, "PID buffer too small");
52
53 struct tm local_time;
54 time_t utc_time = vm_start_time / 1000;
55 os::localtime_pd(&utc_time, &local_time);
56 res = (int)strftime(_vm_start_time_str, sizeof(_vm_start_time_str), TimestampFormat, &local_time);
57 assert(res > 0, "VM start time buffer too small.");
58 }
59
60 LogFileOutput::~LogFileOutput() {
61 if (_stream != NULL) {
62 if (_archive_name != NULL) {
63 archive();
64 }
65 if (fclose(_stream) != 0) {
66 jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
67 _file_name, strerror(errno));
68 }
69 }
70 os::free(_archive_name);
71 os::free(_file_name);
72 os::free(const_cast<char*>(_name));
73 }
74
75 size_t LogFileOutput::parse_value(const char* value_str) {
76 char* end;
77 unsigned long long value = strtoull(value_str, &end, 10);
78 if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
79 return SIZE_MAX;
80 }
81 return value;
82 }
83
84 bool LogFileOutput::configure_rotation(const char* options) {
85 if (options == NULL || strlen(options) == 0) {
86 return true;
87 }
88 bool success = true;
89 char* opts = os::strdup_check_oom(options, mtLogging);
90
91 char* comma_pos;
92 char* pos = opts;
93 do {
94 comma_pos = strchr(pos, ',');
95 if (comma_pos != NULL) {
96 *comma_pos = '\0';
97 }
98
99 char* equals_pos = strchr(pos, '=');
100 if (equals_pos == NULL) {
101 success = false;
102 break;
103 }
104 char* key = pos;
105 char* value_str = equals_pos + 1;
106 *equals_pos = '\0';
107
108 if (strcmp(FileCountOptionKey, key) == 0) {
109 size_t value = parse_value(value_str);
110 if (value == SIZE_MAX || value >= UINT_MAX) {
111 success = false;
112 break;
113 }
114 _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) {
119 size_t value = parse_value(value_str);
120 if (value == SIZE_MAX || value > SIZE_MAX / K) {
121 success = false;
122 break;
123 }
124 _rotate_size = value * K;
125 } else {
126 success = false;
127 break;
128 }
129 pos = comma_pos + 1;
130 } while (comma_pos != NULL);
131
132 os::free(opts);
133 return success;
134 }
135
136 bool LogFileOutput::initialize(const char* options) {
137 if (!configure_rotation(options)) {
138 return false;
139 }
140 _stream = fopen(_file_name, FileOpenMode);
141 if (_stream == NULL) {
142 log_error(logging)("Could not open log file '%s' (%s).\n", _file_name, strerror(errno));
143 return false;
144 }
145 return true;
146 }
147
148 int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
149 if (_stream == NULL) {
150 // An error has occurred with this output, avoid writing to it.
151 return 0;
152 }
153
154 _rotation_semaphore.wait();
155 int written = LogFileStreamOutput::write(decorations, msg);
156 _current_size += written;
157
158 if (should_rotate()) {
159 rotate();
160 }
161 _rotation_semaphore.signal();
162
163 return written;
164 }
|
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/logFileOutput.hpp"
28 #include "memory/allocation.inline.hpp"
29 #include "runtime/os.inline.hpp"
30 #include "utilities/globalDefinitions.hpp"
31 #include "utilities/defaultStream.hpp"
32
33 const char* LogFileOutput::FileOpenMode = "a";
34 const char* LogFileOutput::PidFilenamePlaceholder = "%p";
35 const char* LogFileOutput::TimestampFilenamePlaceholder = "%t";
36 const char* LogFileOutput::TimestampFormat = "%Y-%m-%d_%H-%M-%S";
37 const char* LogFileOutput::FileSizeOptionKey = "filesize";
38 const char* LogFileOutput::FileCountOptionKey = "filecount";
39 const char* LogFileOutput::ExistingFileModeKey = "mode";
40 char LogFileOutput::_pid_str[PidBufferSize];
41 char LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
42
43 LogFileOutput::LogFileOutput(const char* name)
44 : LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
45 _file_name(NULL), _archive_name(NULL), _archive_name_len(0), _current_size(0),
46 _rotate_size(0), _current_file(1), _file_count(0), _rotation_semaphore(1),
47 _existing_file_mode(Archive) {
48 _file_name = make_file_name(name, _pid_str, _vm_start_time_str);
49 }
50
51 void LogFileOutput::set_file_name_parameters(jlong vm_start_time) {
52 int res = jio_snprintf(_pid_str, sizeof(_pid_str), "%d", os::current_process_id());
53 assert(res > 0, "PID buffer too small");
54
55 struct tm local_time;
56 time_t utc_time = vm_start_time / 1000;
57 os::localtime_pd(&utc_time, &local_time);
58 res = (int)strftime(_vm_start_time_str, sizeof(_vm_start_time_str), TimestampFormat, &local_time);
59 assert(res > 0, "VM start time buffer too small.");
60 }
61
62 LogFileOutput::~LogFileOutput() {
63 if (_stream != NULL) {
64 if (_archive_name != NULL) {
65 archive();
66 }
67 if (fclose(_stream) != 0) {
68 jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
69 _file_name, strerror(errno));
70 }
71 }
72 os::free(_archive_name);
73 os::free(_file_name);
74 os::free(const_cast<char*>(_name));
75 }
76
77 size_t parse_value(const char* value_str) {
78 char* end;
79 unsigned long long value = strtoull(value_str, &end, 10);
80 if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
81 return SIZE_MAX;
82 }
83 return value;
84 }
85
86 bool LogFileOutput::configure(const char* options) {
87 if (options == NULL || strlen(options) == 0) {
88 return true;
89 }
90 bool success = true;
91 char* opts = os::strdup_check_oom(options, mtLogging);
92
93 char* comma_pos;
94 char* pos = opts;
95 do {
96 comma_pos = strchr(pos, ',');
97 if (comma_pos != NULL) {
98 *comma_pos = '\0';
99 }
100
101 char* equals_pos = strchr(pos, '=');
102 if (equals_pos == NULL) {
103 success = false;
104 break;
105 }
106 char* key = pos;
107 char* value_str = equals_pos + 1;
108 *equals_pos = '\0';
109
110 if (strcmp(FileCountOptionKey, key) == 0) {
111 size_t value = parse_value(value_str);
112 if (value == SIZE_MAX || value >= UINT_MAX) {
113 success = false;
114 break;
115 }
116 _file_count = static_cast<uint>(value);
117 _file_count_max_digits = static_cast<uint>(log10(static_cast<double>(_file_count)) + 1);
118 _archive_name_len = 2 + strlen(_file_name) + _file_count_max_digits;
119 _archive_name = NEW_C_HEAP_ARRAY(char, _archive_name_len, mtLogging);
120 } else if (strcmp(FileSizeOptionKey, key) == 0) {
121 size_t value = parse_value(value_str);
122 if (value == SIZE_MAX || value > SIZE_MAX / K) {
123 success = false;
124 break;
125 }
126 _rotate_size = value * K;
127 } else if (strcmp(ExistingFileModeKey, key) == 0) {
128 if (strcmp("archive", value_str) == 0) {
129 _existing_file_mode = Archive;
130 } else if (strcmp("append", value_str) == 0) {
131 _existing_file_mode = Append;
132 } else if (strcmp("truncate", value_str) == 0) {
133 _existing_file_mode = Truncate;
134 } else {
135 success = false;
136 break;
137 }
138 } else {
139 success = false;
140 break;
141 }
142 pos = comma_pos + 1;
143 } while (comma_pos != NULL);
144
145 os::free(opts);
146 return success;
147 }
148
149 static bool file_exists(const char* filename) {
150 struct stat dummy_stat;
151 return os::stat(filename, &dummy_stat) == 0;
152 }
153
154 // Renames the given file (if it exists) from for example "jvm.log" to "jvm.log.X"
155 // where X is the lowest number such that the resulting filename doesn't already exist.
156 // This is done to prevent overwriting old log files by mistake.
157 static void archive_file(const char* filename) {
158 // Nothing to archive if the given file doesn't exist
159 if (!file_exists(filename)) {
160 return;
161 }
162
163 // Size should be filename-length + dot + max digits in SIZE_MAX + null char
164 const size_t size = strlen(filename) + ceil(log10(static_cast<double>(SIZE_MAX))) + 2;
165 char* archive_name = NEW_C_HEAP_ARRAY(char, size, mtLogging);
166
167 bool archived = false;
168 for (size_t i = 0; i < SIZE_MAX; i++) {
169 jio_snprintf(archive_name, size, "%s." SIZE_FORMAT, filename, i);
170 if (file_exists(archive_name)) {
171 continue;
172 }
173 // Unique archive name found!
174 int ret = rename(filename, archive_name);
175 if (ret == 0) {
176 archived = true;
177 }
178 break;
179 }
180
181 FREE_C_HEAP_ARRAY(char, archive_name);
182 if (archived) {
183 return;
184 }
185
186 jio_fprintf(defaultStream::error_stream(),
187 "Unable to archive pre-existing log file: %s. "
188 "File will be appended instead.\n", filename);
189 }
190
191 bool LogFileOutput::initialize(const char* options) {
192 if (!configure(options)) {
193 return false;
194 }
195
196 if (_existing_file_mode == Archive) {
197 archive_file(_file_name);
198 }
199
200 _stream = fopen(_file_name, FileOpenMode);
201 if (_stream == NULL) {
202 log_error(logging)("Could not open log file '%s' (%s).", _file_name, strerror(errno));
203 return false;
204 }
205
206 if (_existing_file_mode == Truncate) {
207 os::ftruncate(fileno(_stream), 0);
208 }
209
210 return true;
211 }
212
213 int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
214 if (_stream == NULL) {
215 // An error has occurred with this output, avoid writing to it.
216 return 0;
217 }
218
219 _rotation_semaphore.wait();
220 int written = LogFileStreamOutput::write(decorations, msg);
221 _current_size += written;
222
223 if (should_rotate()) {
224 rotate();
225 }
226 _rotation_semaphore.signal();
227
228 return written;
229 }
|