8341622: Tag-specific disabled default decorators for UnifiedLogging

Reviewed-by: jsjolen, rcastanedalo, aboldtch
This commit is contained in:
Antón Seoane Ampudia 2024-10-17 09:18:22 +00:00 committed by Johan Sjölen
parent 1ea1f33f66
commit 9bdface147
9 changed files with 299 additions and 14 deletions

View File

@ -491,7 +491,7 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr,
return false;
}
LogDecorators decorators;
LogDecorators decorators = selections.get_default_decorators();
if (!decorators.parse(decoratorstr, errstream)) {
return false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,8 @@
#include "logging/logDecorators.hpp"
#include "runtime/os.hpp"
const LogLevelType AnyLevel = LogLevelType::NotMentioned;
template <LogDecorators::Decorator d>
struct AllBitmask {
// Use recursive template deduction to calculate the bitmask of all decorations.
@ -45,6 +47,19 @@ const char* LogDecorators::_name[][2] = {
#undef DECORATOR
};
#define UNDECORATED_DEFAULTS \
UNDECORATED_DEFAULT(AnyLevel, LOG_TAGS(jit, inlining))
const LogDecorators::DefaultUndecoratedSelection LogDecorators::default_decorators[] = {
#define UNDECORATED_DEFAULT(level, ...) LogDecorators::DefaultUndecoratedSelection::make<level, __VA_ARGS__>(),
UNDECORATED_DEFAULTS
#undef UNDECORATED_DEFAULT
};
#undef UNDERCORATED_DEFAULTS
const size_t LogDecorators::number_of_default_decorators = ARRAY_SIZE(default_decorators);
LogDecorators::Decorator LogDecorators::from_string(const char* str) {
for (size_t i = 0; i < Count; i++) {
Decorator d = static_cast<Decorator>(i);
@ -57,7 +72,7 @@ LogDecorators::Decorator LogDecorators::from_string(const char* str) {
bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) {
if (decorator_args == nullptr || strlen(decorator_args) == 0) {
_decorators = DefaultDecoratorsMask;
// No decorators supplied, keep default decorators
return true;
}
@ -93,3 +108,16 @@ bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) {
}
return result;
}
bool LogDecorators::has_disabled_default_decorators(const LogSelection& selection, const DefaultUndecoratedSelection* defaults, size_t defaults_count) {
for (size_t i = 0; i < defaults_count; ++i) {
DefaultUndecoratedSelection current_default = defaults[i];
const bool ignore_level = current_default.selection().level() == AnyLevel;
const bool level_matches = ignore_level || selection.level() == current_default.selection().level();
if (!level_matches) continue;
if (selection.superset_of(current_default.selection())) {
return true;
}
}
return false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,6 +25,7 @@
#define SHARE_LOGGING_LOGDECORATORS_HPP
#include "utilities/globalDefinitions.hpp"
#include "logging/logSelection.hpp"
class outputStream;
@ -59,6 +60,7 @@ class outputStream;
// declared above. For example, logging with 'uptime, level, tags' decorators results in:
// [0,943s][info ][logging] message.
class LogDecorators {
friend class TestLogDecorators;
public:
enum Decorator {
#define DECORATOR(name, abbr) name##_decorator,
@ -68,24 +70,48 @@ class LogDecorators {
Invalid
};
class DefaultUndecoratedSelection {
friend class TestLogDecorators;
LogSelection _selection;
DefaultUndecoratedSelection() : _selection(LogSelection::Invalid) {}
DefaultUndecoratedSelection(LogLevelType level, LogTagType t0, LogTagType t1, LogTagType t2,
LogTagType t3, LogTagType t4) : _selection(LogSelection::Invalid) {
LogTagType tag_arr[LogTag::MaxTags] = { t0, t1, t2, t3, t4 };
_selection = LogSelection(tag_arr, false, level);
}
public:
template <LogLevelType Level, LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG,
LogTagType T3 = LogTag::__NO_TAG, LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
static DefaultUndecoratedSelection make() {
STATIC_ASSERT(GuardTag == LogTag::__NO_TAG);
return DefaultUndecoratedSelection(Level, T0, T1, T2, T3, T4);
}
const LogSelection& selection() const { return _selection; }
};
private:
uint _decorators;
static const char* _name[][2];
static const uint DefaultDecoratorsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator);
static const uint defaultsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator);
static const LogDecorators::DefaultUndecoratedSelection default_decorators[];
static const size_t number_of_default_decorators;
static uint mask(LogDecorators::Decorator decorator) {
return 1 << decorator;
}
constexpr LogDecorators(uint mask) : _decorators(mask) {
}
public:
static const LogDecorators None;
static const LogDecorators All;
LogDecorators() : _decorators(DefaultDecoratorsMask) {
}
constexpr LogDecorators(uint mask) : _decorators(mask) {}
LogDecorators() : _decorators(defaultsMask) {}
void clear() {
_decorators = 0;
@ -99,6 +125,20 @@ class LogDecorators {
return _name[decorator][1];
}
template<typename... Decorators>
static uint mask_from_decorators(LogDecorators::Decorator first, Decorators... rest) {
uint bitmask = 0;
LogDecorators::Decorator decorators[1 + sizeof...(rest)] = { first, rest... };
for (const LogDecorators::Decorator decorator : decorators) {
bitmask |= mask(decorator);
}
return bitmask;
}
// Check if we have some default decorators for a given LogSelection. If that is the case,
// the output parameter mask will contain the defaults-specified decorators mask
static bool has_disabled_default_decorators(const LogSelection& selection, const DefaultUndecoratedSelection* defaults = default_decorators, size_t defaults_count = number_of_default_decorators);
static LogDecorators::Decorator from_string(const char* str);
void combine_with(const LogDecorators &source) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -69,6 +69,23 @@ bool LogSelection::operator!=(const LogSelection& ref) const {
return !operator==(ref);
}
bool LogSelection::superset_of(const LogSelection& other) const {
bool match;
for (size_t i = 0; i < other.ntags(); ++i) {
match = false;
for (size_t j = 0; j < _ntags; ++j) {
if (other._tags[i] == _tags[j]) {
match = true;
break;
}
}
if (!match) return false;
}
return true;
}
static LogSelection parse_internal(char *str, outputStream* errstream) {
// Parse the level, if specified
LogLevelType level = LogLevel::Unspecified;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -54,6 +54,8 @@ class LogSelection : public StackObj {
bool operator==(const LogSelection& ref) const;
bool operator!=(const LogSelection& ref) const;
bool superset_of(const LogSelection& ref) const;
size_t ntags() const;
LogLevelType level() const;
size_t tag_sets_selected() const;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -53,6 +53,14 @@ bool LogSelectionList::verify_selections(outputStream* out) const {
return valid;
}
LogDecorators LogSelectionList::get_default_decorators() const {
for (size_t i = 0; i < _nselections; ++i) {
if (!LogDecorators::has_disabled_default_decorators(_selections[i])) {
return LogDecorators();
}
}
return LogDecorators::None;
}
bool LogSelectionList::parse(const char* str, outputStream* errstream) {
bool success = true;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -60,6 +60,8 @@ class LogSelectionList : public StackObj {
// Returns false if some invalid selection was found. If given an outputstream,
// this function will list all the invalid selections on the stream.
bool verify_selections(outputStream* out = nullptr) const;
LogDecorators get_default_decorators() const;
};
#endif // SHARE_LOGGING_LOGSELECTIONLIST_HPP

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "jvm.h"
#include "logging/logDecorators.hpp"
#include "logging/logTag.hpp"
#include "unittest.hpp"
class TestLogDecorators : public testing::Test {
using LD = LogDecorators;
static const size_t defaults_cnt = 3;
LD::DefaultUndecoratedSelection defaults[defaults_cnt] = {
LD::DefaultUndecoratedSelection::make<LogLevelType::Trace, LOG_TAGS(gc)>(),
LD::DefaultUndecoratedSelection::make<LogLevelType::Trace, LOG_TAGS(jit)>(),
LD::DefaultUndecoratedSelection::make<LogLevelType::NotMentioned, LOG_TAGS(ref)>(),
};
public:
void test_default_decorators() {
LogTagType tags[LogTag::MaxTags] = { LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG };
bool result;
// If a -Xlog selection matches one of the undecorated defaults, the default decorators will be disabled
tags[0] = LogTagType::_jit;
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt);
EXPECT_TRUE(result);
// If a -Xlog selection contains one of the undecorated defaults, the default decorators will be disabled
tags[0] = LogTagType::_jit;
tags[1] = LogTagType::_inlining;
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt);
EXPECT_TRUE(result);
// Wildcards are ignored
tags[0] = LogTag::_compilation;
result = LD::has_disabled_default_decorators(LogSelection(tags, true, LogLevelType::Debug), defaults, defaults_cnt);
EXPECT_FALSE(result);
// If there is no level match, default decorators are kept
tags[0] = LogTagType::_gc;
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Info), defaults, defaults_cnt);
EXPECT_FALSE(result);
// If NotMentioned is specified, it will match every level and so default decorators will never be added if there is a positive tagset match
tags[0] = LogTagType::_ref;
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Error), defaults, defaults_cnt);
EXPECT_TRUE(result);
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Warning), defaults, defaults_cnt);
EXPECT_TRUE(result);
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Info), defaults, defaults_cnt);
EXPECT_TRUE(result);
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Debug), defaults, defaults_cnt);
EXPECT_TRUE(result);
result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt);
EXPECT_TRUE(result);
}
void test_mask_from_decorators() {
// Single tags should yield 2^{decorator_value_in_enum}
EXPECT_EQ(LD::mask_from_decorators(LD::time_decorator), (uint)(1 << LD::time_decorator));
EXPECT_EQ(LD::mask_from_decorators(LD::pid_decorator), (uint)(1 << LD::pid_decorator));
EXPECT_EQ(LD::mask_from_decorators(LD::tid_decorator), (uint)(1 << LD::tid_decorator));
EXPECT_EQ(LD::mask_from_decorators(LD::tags_decorator), (uint)(1 << LD::tags_decorator));
// Combinations of decorators should fill the mask accordingly to their bitmask positions
uint mask = (1 << LD::time_decorator) | (1 << LD::uptimemillis_decorator) | (1 << LD::tid_decorator);
EXPECT_EQ(LD::mask_from_decorators(LD::time_decorator, LD::uptimemillis_decorator, LD::tid_decorator), mask);
}
};
TEST_VM_F(TestLogDecorators, MaskFromDecorators) {
test_mask_from_decorators();
}
TEST_VM_F(TestLogDecorators, HasDefaultDecorators) {
test_default_decorators();
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @requires vm.flagless
* @summary Running -Xlog with tags which have disabled default decorators should not yield decorated logs
* @library /test/lib
* @modules java.base/jdk.internal.misc
* java.management
* @run driver DefaultLogDecoratorsTest
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.regex.Pattern;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
public class DefaultLogDecoratorsTest {
private static Pattern DECORATED_LINE = Pattern.compile("(\\[.+\\])+ .*");
private static void doTest(boolean shouldHave, String... xlog) throws Exception {
List<String> argsList = new ArrayList(Arrays.asList(xlog));
argsList.add(InnerClass.class.getName());
String[] args = argsList.toArray(new String[0]);
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldHaveExitValue(0);
List<String> allLines = Files.readAllLines(Path.of("decorators.log"));
for (String line : allLines) {
if (DECORATED_LINE.matcher(line).find() == !shouldHave) {
throw new RuntimeException("Logging should " + (shouldHave ? "" : "not ") + "contain decorators!");
}
}
}
public static void main(String[] args) throws Exception {
// JIT inlining logging, as per defaults, shall have all decorators disabled
doTest(false, "-Xlog:jit+inlining*=trace:decorators.log");
// If decorators are specified, the defaults are not taken into account
doTest(true, "-Xlog:jit+inlining*=trace:decorators.log:time");
// Even if decorators are only supplied for another tag(s), the defaults are not taken into account
doTest(true, "-Xlog:jit+inlining*=trace:decorators.log", "-Xlog:gc*=info:decorators.log:time");
// Defaults are not taken into account also when another tag implicitly imposes the "standard" defaults
doTest(true, "-Xlog:jit+inlining*=trace:decorators.log", "-Xlog:gc*=info:decorators.log");
// Other logging shall not be affected by a tag with defaults
doTest(true, "-Xlog:gc*=trace:decorators.log");
}
public static class InnerClass {
public static void main(String[] args) throws Exception {
System.out.println("DefaultLogDecorators test");
}
}
}