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 };
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 // Normalizes the given LogOutput name to type=name form.
111 static bool normalize_output_name(const char* full_name, char* buffer, size_t len, outputStream* errstream) {
112 const char* start_quote = strchr(full_name, '"');
113 const char* equals = strchr(full_name, '=');
114 const bool quoted = start_quote != NULL;
115 const bool is_stdout_or_stderr = (strcmp(full_name, "stdout") == 0 || strcmp(full_name, "stderr") == 0);
116
117 // ignore equals sign within quotes
118 if (quoted && equals > start_quote) {
119 equals = NULL;
120 }
121
122 const char* prefix = "";
123 size_t prefix_len = 0;
124 const char* name = full_name;
125 if (equals != NULL) {
126 // split on equals sign
127 name = equals + 1;
128 prefix = full_name;
129 prefix_len = equals - full_name + 1;
130 } else if (!is_stdout_or_stderr) {
131 prefix = "file=";
132 prefix_len = strlen(prefix);
133 }
134 size_t name_len = strlen(name);
135
136 if (quoted) {
137 const char* end_quote = strchr(start_quote + 1, '"');
138 if (end_quote == NULL) {
139 errstream->print_cr("Output name has opening quote but is missing a terminating quote.");
140 return false;
141 }
142 if (start_quote != name || end_quote[1] != '\0') {
143 errstream->print_cr("Output name can not be partially quoted."
144 " Either surround the whole name with quotation marks,"
145 " or do not use quotation marks at all.");
146 return false;
147 }
148 // strip start and end quote
149 name++;
150 name_len -= 2;
151 }
152
153 int ret = jio_snprintf(buffer, len, "%.*s%.*s", prefix_len, prefix, name_len, name);
154 assert(ret > 0, "buffer issue");
155 return true;
156 }
157
158 size_t LogConfiguration::find_output(const char* name) {
159 for (size_t i = 0; i < _n_outputs; i++) {
160 if (strcmp(_outputs[i]->name(), name) == 0) {
161 return i;
162 }
163 }
164 return SIZE_MAX;
165 }
166
167 LogOutput* LogConfiguration::new_output(const char* name,
168 const char* options,
169 outputStream* errstream) {
170 LogOutput* output;
171 if (strncmp(name, "file=", strlen("file=")) == 0) {
172 output = new LogFileOutput(name);
173 } else {
174 errstream->print_cr("Unsupported log output type: %s", name);
175 return NULL;
176 }
177
178 bool success = output->initialize(options, errstream);
179 if (!success) {
180 errstream->print_cr("Initialization of output '%s' using options '%s' failed.", name, options);
181 delete output;
182 return NULL;
183 }
184 return output;
185 }
186
187 size_t LogConfiguration::add_output(LogOutput* output) {
188 size_t idx = _n_outputs++;
189 _outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
190 _outputs[idx] = output;
191 return idx;
378 LogTagLevelExpression expr;
379 if (!expr.parse(what, errstream)) {
380 return false;
381 }
382
383 LogDecorators decorators;
384 if (!decorators.parse(decoratorstr, errstream)) {
385 return false;
386 }
387
388 ConfigurationLock cl;
389 size_t idx;
390 if (outputstr[0] == '#') { // Output specified using index
391 int ret = sscanf(outputstr + 1, SIZE_FORMAT, &idx);
392 if (ret != 1 || idx >= _n_outputs) {
393 errstream->print_cr("Invalid output index '%s'", outputstr);
394 return false;
395 }
396 } else { // Output specified using name
397 // Normalize the name, stripping quotes and ensures it includes type prefix
398 size_t len = strlen(outputstr) + strlen("file=") + 1;
399 char* normalized = NEW_C_HEAP_ARRAY(char, len, mtLogging);
400 if (!normalize_output_name(outputstr, normalized, len, errstream)) {
401 return false;
402 }
403
404 idx = find_output(normalized);
405 if (idx == SIZE_MAX) {
406 // Attempt to create and add the output
407 LogOutput* output = new_output(normalized, output_options, errstream);
408 if (output != NULL) {
409 idx = add_output(output);
410 }
411 } else if (output_options != NULL && strlen(output_options) > 0) {
412 errstream->print_cr("Output options for existing outputs are ignored.");
413 }
414
415 FREE_C_HEAP_ARRAY(char, normalized);
416 if (idx == SIZE_MAX) {
417 return false;
418 }
|
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 };
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;
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 }
|