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/logStream.hpp"
33 #include "logging/logTagLevelExpression.hpp"
34 #include "logging/logTagSet.hpp"
35 #include "memory/allocation.inline.hpp"
36 #include "memory/resourceArea.hpp"
37 #include "runtime/os.inline.hpp"
38 #include "runtime/semaphore.hpp"
39 #include "utilities/globalDefinitions.hpp"
40
41 LogOutput** LogConfiguration::_outputs = NULL;
42 size_t LogConfiguration::_n_outputs = 0;
43
44 LogConfiguration::UpdateListenerFunction* LogConfiguration::_listener_callbacks = NULL;
45 size_t LogConfiguration::_n_listener_callbacks = 0;
46
47 // Stack object to take the lock for configuring the logging.
48 // Should only be held during the critical parts of the configuration
49 // (when calling configure_output or reading/modifying the outputs array).
50 // Thread must never block when holding this lock.
51 class ConfigurationLock : public StackObj {
52 private:
53 // Semaphore used as lock
54 static Semaphore _semaphore;
55 debug_only(static intx _locking_thread_id;)
56 public:
57 ConfigurationLock() {
58 _semaphore.wait();
59 debug_only(_locking_thread_id = os::current_thread_id());
60 }
61 ~ConfigurationLock() {
62 debug_only(_locking_thread_id = -1);
63 _semaphore.signal();
64 }
65 debug_only(static bool current_thread_has_lock();)
66 };
90 }
91 }
92
93 void LogConfiguration::initialize(jlong vm_start_time) {
94 LogFileOutput::set_file_name_parameters(vm_start_time);
95 LogDecorations::initialize(vm_start_time);
96 assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
97 _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
98 _outputs[0] = LogOutput::Stdout;
99 _outputs[1] = LogOutput::Stderr;
100 _n_outputs = 2;
101 }
102
103 void LogConfiguration::finalize() {
104 for (size_t i = 2; i < _n_outputs; i++) {
105 delete _outputs[i];
106 }
107 FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
108 }
109
110 size_t LogConfiguration::find_output(const char* name) {
111 for (size_t i = 0; i < _n_outputs; i++) {
112 if (strcmp(_outputs[i]->name(), name) == 0) {
113 return i;
114 }
115 }
116 return SIZE_MAX;
117 }
118
119 LogOutput* LogConfiguration::new_output(char* name, const char* options, outputStream* errstream) {
120 const char* type;
121 char* equals_pos = strchr(name, '=');
122 if (equals_pos == NULL) {
123 type = "file";
124 } else {
125 *equals_pos = '\0';
126 type = name;
127 name = equals_pos + 1;
128 }
129
130 // Check if name is quoted, and if so, strip the quotes
131 char* quote = strchr(name, '"');
132 if (quote != NULL) {
133 char* end_quote = strchr(name + 1, '"');
134 if (end_quote == NULL) {
135 errstream->print_cr("Output name has opening quote but is missing a terminating quote.");
136 return NULL;
137 } else if (quote != name || end_quote[1] != '\0') {
138 errstream->print_cr("Output name can not be partially quoted."
139 " Either surround the whole name with quotation marks,"
140 " or do not use quotation marks at all.");
141 return NULL;
142 }
143 name++;
144 *end_quote = '\0';
145 }
146
147 LogOutput* output;
148 if (strcmp(type, "file") == 0) {
149 output = new LogFileOutput(name);
150 } else {
151 errstream->print_cr("Unsupported log output type.");
152 return NULL;
153 }
154
155 bool success = output->initialize(options, errstream);
156 if (!success) {
157 errstream->print_cr("Initialization of output '%s' using options '%s' failed.", name, options);
158 delete output;
159 return NULL;
160 }
161 return output;
162 }
163
164 size_t LogConfiguration::add_output(LogOutput* output) {
165 size_t idx = _n_outputs++;
166 _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
167 _outputs[idx] = output;
168 return idx;
169 }
170
171 void LogConfiguration::delete_output(size_t idx) {
347 const char* what,
348 const char* decoratorstr,
349 const char* output_options,
350 outputStream* errstream) {
351 if (outputstr == NULL || strlen(outputstr) == 0) {
352 outputstr = "stdout";
353 }
354
355 LogTagLevelExpression expr;
356 if (!expr.parse(what, errstream)) {
357 return false;
358 }
359
360 LogDecorators decorators;
361 if (!decorators.parse(decoratorstr, errstream)) {
362 return false;
363 }
364
365 ConfigurationLock cl;
366 size_t idx;
367 if (outputstr[0] == '#') {
368 int ret = sscanf(outputstr+1, SIZE_FORMAT, &idx);
369 if (ret != 1 || idx >= _n_outputs) {
370 errstream->print_cr("Invalid output index '%s'", outputstr);
371 return false;
372 }
373 } else {
374 idx = find_output(outputstr);
375 if (idx == SIZE_MAX) {
376 char* tmp = os::strdup_check_oom(outputstr, mtLogging);
377 LogOutput* output = new_output(tmp, output_options, errstream);
378 os::free(tmp);
379 if (output == NULL) {
380 return false;
381 }
382 idx = add_output(output);
383 } else if (output_options != NULL && strlen(output_options) > 0) {
384 errstream->print_cr("Output options for existing outputs are ignored.");
385 }
386 }
387 configure_output(idx, expr, decorators);
388 notify_update_listeners();
389 return true;
390 }
391
392 void LogConfiguration::describe_available(outputStream* out){
393 out->print("Available log levels:");
394 for (size_t i = 0; i < LogLevel::Count; i++) {
395 out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
396 }
397 out->cr();
398
399 out->print("Available log decorators:");
400 for (size_t i = 0; i < LogDecorators::Count; i++) {
401 LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
402 out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
403 }
404 out->cr();
|
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/logStream.hpp"
33 #include "logging/logTagLevelExpression.hpp"
34 #include "logging/logTagSet.hpp"
35 #include "memory/allocation.inline.hpp"
36 #include "memory/resourceArea.hpp"
37 #include "runtime/os.inline.hpp"
38 #include "runtime/semaphore.hpp"
39 #include "utilities/globalDefinitions.hpp"
40
41 LogOutput** LogConfiguration::_outputs = NULL;
42 size_t LogConfiguration::_n_outputs = 0;
43
44 LogConfiguration::UpdateListenerFunction* LogConfiguration::_listener_callbacks = NULL;
45 size_t LogConfiguration::_n_listener_callbacks = 0;
46
47 // LogFileOutput is the default type of output, its type prefix should be used if no type was specified
48 static const char* implicit_output_prefix = LogFileOutput::Prefix;
49
50 // Stack object to take the lock for configuring the logging.
51 // Should only be held during the critical parts of the configuration
52 // (when calling configure_output or reading/modifying the outputs array).
53 // Thread must never block when holding this lock.
54 class ConfigurationLock : public StackObj {
55 private:
56 // Semaphore used as lock
57 static Semaphore _semaphore;
58 debug_only(static intx _locking_thread_id;)
59 public:
60 ConfigurationLock() {
61 _semaphore.wait();
62 debug_only(_locking_thread_id = os::current_thread_id());
63 }
64 ~ConfigurationLock() {
65 debug_only(_locking_thread_id = -1);
66 _semaphore.signal();
67 }
68 debug_only(static bool current_thread_has_lock();)
69 };
93 }
94 }
95
96 void LogConfiguration::initialize(jlong vm_start_time) {
97 LogFileOutput::set_file_name_parameters(vm_start_time);
98 LogDecorations::initialize(vm_start_time);
99 assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
100 _outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
101 _outputs[0] = LogOutput::Stdout;
102 _outputs[1] = LogOutput::Stderr;
103 _n_outputs = 2;
104 }
105
106 void LogConfiguration::finalize() {
107 for (size_t i = 2; i < _n_outputs; i++) {
108 delete _outputs[i];
109 }
110 FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
111 }
112
113 // Normalizes the given LogOutput name to type=name form.
114 // For example, foo, "foo", file="foo", will all be normalized to file=foo (no quotes, prefixed).
115 static bool normalize_output_name(const char* full_name, char* buffer, size_t len, outputStream* errstream) {
116 const char* start_quote = strchr(full_name, '"');
117 const char* equals = strchr(full_name, '=');
118 const bool quoted = start_quote != NULL;
119 const bool is_stdout_or_stderr = (strcmp(full_name, "stdout") == 0 || strcmp(full_name, "stderr") == 0);
120
121 // ignore equals sign within quotes
122 if (quoted && equals > start_quote) {
123 equals = NULL;
124 }
125
126 const char* prefix = "";
127 size_t prefix_len = 0;
128 const char* name = full_name;
129 if (equals != NULL) {
130 // split on equals sign
131 name = equals + 1;
132 prefix = full_name;
133 prefix_len = equals - full_name + 1;
134 } else if (!is_stdout_or_stderr) {
135 prefix = implicit_output_prefix;
136 prefix_len = strlen(prefix);
137 }
138 size_t name_len = strlen(name);
139
140 if (quoted) {
141 const char* end_quote = strchr(start_quote + 1, '"');
142 if (end_quote == NULL) {
143 errstream->print_cr("Output name has opening quote but is missing a terminating quote.");
144 return false;
145 }
146 if (start_quote != name || end_quote[1] != '\0') {
147 errstream->print_cr("Output name can not be partially quoted."
148 " Either surround the whole name with quotation marks,"
149 " or do not use quotation marks at all.");
150 return false;
151 }
152 // strip start and end quote
153 name++;
154 name_len -= 2;
155 }
156
157 int ret = jio_snprintf(buffer, len, "%.*s%.*s", prefix_len, prefix, name_len, name);
158 assert(ret > 0, "buffer issue");
159 return true;
160 }
161
162 size_t LogConfiguration::find_output(const char* name) {
163 for (size_t i = 0; i < _n_outputs; i++) {
164 if (strcmp(_outputs[i]->name(), name) == 0) {
165 return i;
166 }
167 }
168 return SIZE_MAX;
169 }
170
171 LogOutput* LogConfiguration::new_output(const char* name,
172 const char* options,
173 outputStream* errstream) {
174 LogOutput* output;
175 if (strncmp(name, LogFileOutput::Prefix, strlen(LogFileOutput::Prefix)) == 0) {
176 output = new LogFileOutput(name);
177 } else {
178 errstream->print_cr("Unsupported log output type: %s", name);
179 return NULL;
180 }
181
182 bool success = output->initialize(options, errstream);
183 if (!success) {
184 errstream->print_cr("Initialization of output '%s' using options '%s' failed.", name, options);
185 delete output;
186 return NULL;
187 }
188 return output;
189 }
190
191 size_t LogConfiguration::add_output(LogOutput* output) {
192 size_t idx = _n_outputs++;
193 _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
194 _outputs[idx] = output;
195 return idx;
196 }
197
198 void LogConfiguration::delete_output(size_t idx) {
374 const char* what,
375 const char* decoratorstr,
376 const char* output_options,
377 outputStream* errstream) {
378 if (outputstr == NULL || strlen(outputstr) == 0) {
379 outputstr = "stdout";
380 }
381
382 LogTagLevelExpression expr;
383 if (!expr.parse(what, errstream)) {
384 return false;
385 }
386
387 LogDecorators decorators;
388 if (!decorators.parse(decoratorstr, errstream)) {
389 return false;
390 }
391
392 ConfigurationLock cl;
393 size_t idx;
394 if (outputstr[0] == '#') { // Output specified using index
395 int ret = sscanf(outputstr + 1, SIZE_FORMAT, &idx);
396 if (ret != 1 || idx >= _n_outputs) {
397 errstream->print_cr("Invalid output index '%s'", outputstr);
398 return false;
399 }
400 } else { // Output specified using name
401 // Normalize the name, stripping quotes and ensures it includes type prefix
402 size_t len = strlen(outputstr) + strlen(implicit_output_prefix) + 1;
403 char* normalized = NEW_C_HEAP_ARRAY(char, len, mtLogging);
404 if (!normalize_output_name(outputstr, normalized, len, errstream)) {
405 return false;
406 }
407
408 idx = find_output(normalized);
409 if (idx == SIZE_MAX) {
410 // Attempt to create and add the output
411 LogOutput* output = new_output(normalized, output_options, errstream);
412 if (output != NULL) {
413 idx = add_output(output);
414 }
415 } else if (output_options != NULL && strlen(output_options) > 0) {
416 errstream->print_cr("Output options for existing outputs are ignored.");
417 }
418
419 FREE_C_HEAP_ARRAY(char, normalized);
420 if (idx == SIZE_MAX) {
421 return false;
422 }
423 }
424 configure_output(idx, expr, decorators);
425 notify_update_listeners();
426 return true;
427 }
428
429 void LogConfiguration::describe_available(outputStream* out){
430 out->print("Available log levels:");
431 for (size_t i = 0; i < LogLevel::Count; i++) {
432 out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
433 }
434 out->cr();
435
436 out->print("Available log decorators:");
437 for (size_t i = 0; i < LogDecorators::Count; i++) {
438 LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
439 out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
440 }
441 out->cr();
|