1 /* 2 * Copyright (c) 2016, 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 "logTestFixture.hpp" 26 #include "logTestUtils.inline.hpp" 27 #include "logging/logConfiguration.hpp" 28 #include "logging/logLevel.hpp" 29 #include "logging/logOutput.hpp" 30 #include "logging/logTag.hpp" 31 #include "logging/logTagSet.hpp" 32 #include "memory/resourceArea.hpp" 33 #include "unittest.hpp" 34 #include "utilities/ostream.hpp" 35 36 class LogConfigurationTest : public LogTestFixture { 37 protected: 38 static char _all_decorators[256]; 39 40 public: 41 static void SetUpTestCase(); 42 }; 43 44 char LogConfigurationTest::_all_decorators[256]; 45 46 // Prepare _all_decorators to contain the full list of decorators (comma separated) 47 void LogConfigurationTest::SetUpTestCase() { 48 char *pos = _all_decorators; 49 for (size_t i = 0; i < LogDecorators::Count; i++) { 50 pos += jio_snprintf(pos, sizeof(_all_decorators) - (pos - _all_decorators), "%s%s", 51 (i == 0 ? "" : ","), 52 LogDecorators::name(static_cast<LogDecorators::Decorator>(i))); 53 } 54 } 55 56 // Check if the given text is included by LogConfiguration::describe() 57 static bool is_described(const char* text) { 58 ResourceMark rm; 59 stringStream ss; 60 LogConfiguration::describe(&ss); 61 return string_contains_substring(ss.as_string(), text); 62 } 63 64 TEST_F(LogConfigurationTest, describe) { 65 ResourceMark rm; 66 stringStream ss; 67 LogConfiguration::describe(&ss); 68 const char* description = ss.as_string(); 69 70 // Verify that stdout and stderr are listed by default 71 EXPECT_PRED2(string_contains_substring, description, LogOutput::Stdout->name()); 72 EXPECT_PRED2(string_contains_substring, description, LogOutput::Stderr->name()); 73 74 // Verify that each tag, level and decorator is listed 75 for (size_t i = 0; i < LogTag::Count; i++) { 76 EXPECT_PRED2(string_contains_substring, description, LogTag::name(static_cast<LogTagType>(i))); 77 } 78 for (size_t i = 0; i < LogLevel::Count; i++) { 79 EXPECT_PRED2(string_contains_substring, description, LogLevel::name(static_cast<LogLevelType>(i))); 80 } 81 for (size_t i = 0; i < LogDecorators::Count; i++) { 82 EXPECT_PRED2(string_contains_substring, description, LogDecorators::name(static_cast<LogDecorators::Decorator>(i))); 83 } 84 85 // Verify that the default configuration is printed 86 char expected_buf[256]; 87 int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "=%s", LogLevel::name(LogLevel::Default)); 88 ASSERT_NE(-1, ret); 89 EXPECT_PRED2(string_contains_substring, description, expected_buf); 90 EXPECT_PRED2(string_contains_substring, description, "#1: stderr all=off"); 91 92 // Verify default decorators are listed 93 LogDecorators default_decorators; 94 expected_buf[0] = '\0'; 95 for (size_t i = 0; i < LogDecorators::Count; i++) { 96 LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i); 97 if (default_decorators.is_decorator(d)) { 98 ASSERT_LT(strlen(expected_buf), sizeof(expected_buf)); 99 ret = jio_snprintf(expected_buf + strlen(expected_buf), 100 sizeof(expected_buf) - strlen(expected_buf), 101 "%s%s", 102 strlen(expected_buf) > 0 ? "," : "", 103 LogDecorators::name(d)); 104 ASSERT_NE(-1, ret); 105 } 106 } 107 EXPECT_PRED2(string_contains_substring, description, expected_buf); 108 109 // Add a new output and verify that it gets described after it has been added 110 const char* what = "all=trace"; 111 EXPECT_FALSE(is_described(TestLogFileName)) << "Test output already exists!"; 112 set_log_config(TestLogFileName, what); 113 EXPECT_TRUE(is_described(TestLogFileName)); 114 EXPECT_TRUE(is_described("logging=trace")); 115 } 116 117 // Test updating an existing log output 118 TEST_F(LogConfigurationTest, update_output) { 119 // Update stdout twice, first using it's name, and the second time its index # 120 const char* test_outputs[] = { "stdout", "#0" }; 121 for (size_t i = 0; i < ARRAY_SIZE(test_outputs); i++) { 122 set_log_config(test_outputs[i], "all=info"); 123 124 // Verify configuration using LogConfiguration::describe 125 EXPECT_TRUE(is_described("#0: stdout")); 126 EXPECT_TRUE(is_described("logging=info")); 127 128 // Verify by iterating over tagsets 129 LogOutput* o = LogOutput::Stdout; 130 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 131 EXPECT_TRUE(ts->has_output(o)); 132 EXPECT_TRUE(ts->is_level(LogLevel::Info)); 133 EXPECT_FALSE(ts->is_level(LogLevel::Debug)); 134 } 135 136 // Now change the level and verify the change propagated 137 set_log_config(test_outputs[i], "all=debug"); 138 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 139 EXPECT_TRUE(ts->has_output(o)); 140 EXPECT_TRUE(ts->is_level(LogLevel::Debug)); 141 EXPECT_FALSE(ts->is_level(LogLevel::Trace)); 142 } 143 } 144 } 145 146 // Test adding a new output to the configuration 147 TEST_F(LogConfigurationTest, add_new_output) { 148 const char* what = "all=trace"; 149 150 ASSERT_FALSE(is_described(TestLogFileName)); 151 set_log_config(TestLogFileName, what); 152 153 // Verify new output using LogConfiguration::describe 154 EXPECT_TRUE(is_described(TestLogFileName)); 155 EXPECT_TRUE(is_described("logging=trace")); 156 157 // Also verify by iterating over tagsets, checking levels on tagsets 158 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 159 EXPECT_TRUE(ts->is_level(LogLevel::Trace)); 160 } 161 } 162 163 TEST_F(LogConfigurationTest, disable_logging) { 164 // Add TestLogFileName as an output 165 set_log_config(TestLogFileName, "logging=info"); 166 167 // Add a second file output 168 char other_file_name[2 * K]; 169 jio_snprintf(other_file_name, sizeof(other_file_name), "%s-other", TestLogFileName); 170 set_log_config(other_file_name, "logging=info"); 171 172 LogConfiguration::disable_logging(); 173 174 // Verify that both file outputs were disabled 175 EXPECT_FALSE(is_described(TestLogFileName)); 176 EXPECT_FALSE(is_described(other_file_name)); 177 delete_file(other_file_name); 178 179 // Verify that no tagset has logging enabled 180 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 181 EXPECT_FALSE(ts->has_output(LogOutput::Stdout)); 182 EXPECT_FALSE(ts->has_output(LogOutput::Stderr)); 183 EXPECT_FALSE(ts->is_level(LogLevel::Error)); 184 } 185 } 186 187 // Test disabling a particular output 188 TEST_F(LogConfigurationTest, disable_output) { 189 // Disable the default configuration for stdout 190 set_log_config("stdout", "all=off"); 191 192 // Verify configuration using LogConfiguration::describe 193 EXPECT_TRUE(is_described("#0: stdout all=off")); 194 195 // Verify by iterating over tagsets 196 LogOutput* o = LogOutput::Stdout; 197 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 198 EXPECT_FALSE(ts->has_output(o)); 199 EXPECT_FALSE(ts->is_level(LogLevel::Error)); 200 } 201 202 // Add a new file output 203 const char* what = "all=debug"; 204 set_log_config(TestLogFileName, what); 205 EXPECT_TRUE(is_described(TestLogFileName)); 206 207 // Now disable it, verifying it is removed completely 208 set_log_config(TestLogFileName, "all=off"); 209 EXPECT_FALSE(is_described(TestLogFileName)); 210 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 211 EXPECT_FALSE(ts->is_level(LogLevel::Error)); 212 } 213 } 214 215 // Test reconfiguration of the selected decorators for an output 216 TEST_F(LogConfigurationTest, reconfigure_decorators) { 217 // Configure stderr with all decorators 218 set_log_config("stderr", "all=off", _all_decorators); 219 char buf[256]; 220 int ret = jio_snprintf(buf, sizeof(buf), "#1: stderr all=off %s", _all_decorators); 221 ASSERT_NE(-1, ret); 222 EXPECT_TRUE(is_described(buf)) << "'" << buf << "' not described after reconfiguration"; 223 224 // Now reconfigure logging on stderr with no decorators 225 set_log_config("stderr", "all=off", "none"); 226 EXPECT_TRUE(is_described("#1: stderr all=off \n")) << "Expecting no decorators"; 227 } 228 229 // Test that invalid options cause configuration errors 230 TEST_F(LogConfigurationTest, invalid_configure_options) { 231 LogConfiguration::disable_logging(); 232 const char* invalid_outputs[] = { "#2", "invalidtype=123", ":invalid/path}to*file?" }; 233 for (size_t i = 0; i < ARRAY_SIZE(invalid_outputs); i++) { 234 EXPECT_FALSE(set_log_config(invalid_outputs[i], "", "", "", true)) 235 << "Accepted invalid output '" << invalid_outputs[i] << "'"; 236 } 237 EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all=invalid_level")); 238 EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("what=invalid")); 239 EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all::invalid_decorator")); 240 } 241 242 // Test empty configuration options 243 TEST_F(LogConfigurationTest, parse_empty_command_line_arguments) { 244 const char* empty_variations[] = { "", ":", "::", ":::", "::::" }; 245 for (size_t i = 0; i < ARRAY_SIZE(empty_variations); i++) { 246 const char* cmdline = empty_variations[i]; 247 bool ret = LogConfiguration::parse_command_line_arguments(cmdline); 248 EXPECT_TRUE(ret) << "Error parsing command line arguments '" << cmdline << "'"; 249 for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) { 250 EXPECT_EQ(LogLevel::Unspecified, ts->level_for(LogOutput::Stdout)); 251 } 252 } 253 } 254 255 // Test basic command line parsing & configuration 256 TEST_F(LogConfigurationTest, parse_command_line_arguments) { 257 // Prepare a command line for logging*=debug on stderr with all decorators 258 int ret; 259 char buf[256]; 260 ret = jio_snprintf(buf, sizeof(buf), "logging*=debug:stderr:%s", _all_decorators); 261 ASSERT_NE(-1, ret); 262 263 bool success = LogConfiguration::parse_command_line_arguments(buf); 264 EXPECT_TRUE(success) << "Error parsing valid command line arguments '" << buf << "'"; 265 // Ensure the new configuration applied 266 EXPECT_TRUE(is_described("logging=debug")); 267 EXPECT_TRUE(is_described(_all_decorators)); 268 269 // Test the configuration of file outputs as well 270 ret = jio_snprintf(buf, sizeof(buf), ":%s", TestLogFileName); 271 ASSERT_NE(-1, ret); 272 EXPECT_TRUE(LogConfiguration::parse_command_line_arguments(buf)); 273 } 274 275 // Test split up log configuration arguments 276 TEST_F(LogConfigurationTest, parse_log_arguments) { 277 ResourceMark rm; 278 stringStream ss; 279 // Verify that it's possible to configure each individual tag 280 for (size_t t = 1 /* Skip _NO_TAG */; t < LogTag::Count; t++) { 281 const LogTagType tag = static_cast<LogTagType>(t); 282 EXPECT_TRUE(LogConfiguration::parse_log_arguments("stdout", LogTag::name(tag), "", "", &ss)); 283 } 284 // Same for each level 285 for (size_t l = 0; l < LogLevel::Count; l++) { 286 const LogLevelType level = static_cast<LogLevelType>(l); 287 char expected_buf[256]; 288 int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "all=%s", LogLevel::name(level)); 289 ASSERT_NE(-1, ret); 290 EXPECT_TRUE(LogConfiguration::parse_log_arguments("stderr", expected_buf, "", "", &ss)); 291 } 292 // And for each decorator 293 for (size_t d = 0; d < LogDecorators::Count; d++) { 294 const LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d); 295 EXPECT_TRUE(LogConfiguration::parse_log_arguments("#0", "", LogDecorators::name(decorator), "", &ss)); 296 } 297 EXPECT_STREQ("", ss.as_string()) << "Error reported while parsing: " << ss.as_string(); 298 }