From 03f7bb03e6c63b16e2992a53b4d9abab80fce07f Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Thu, 15 Jun 2023 16:38:08 -0400 Subject: [PATCH] Tests: add a recording rule listing test MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add a test that validates that recording rules can be added to a channel and listed back. Most of the changes are to the lttngtest package in order to provide the requisite capabilities to the test itself. This test is also intended as a reproducer for #1373. Signed-off-by: Jérémie Galarneau Change-Id: I1c6b5e072934ba9760cb1f838395e18a4c6bb211 --- tests/regression/Makefile.am | 3 +- tests/regression/tools/client/Makefile.am | 4 +- .../tools/client/test_event_rule_listing.py | 89 ++++++ tests/utils/lttngtest/lttng.py | 291 ++++++++++++++++-- tests/utils/lttngtest/lttngctl.py | 219 ++++++++++++- 5 files changed, 565 insertions(+), 41 deletions(-) create mode 100755 tests/regression/tools/client/test_event_rule_listing.py diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am index a95feee98..fc7dfc17e 100644 --- a/tests/regression/Makefile.am +++ b/tests/regression/Makefile.am @@ -65,7 +65,8 @@ TESTS = tools/base-path/test_ust \ tools/trigger/name/test_trigger_name_backwards_compat \ tools/trigger/hidden/test_hidden_trigger \ tools/context/test_ust.py \ - tools/client/test_session_commands.py + tools/client/test_session_commands.py \ + tools/client/test_event_rule_listing.py # Only build kernel tests on Linux. if IS_LINUX diff --git a/tests/regression/tools/client/Makefile.am b/tests/regression/tools/client/Makefile.am index e96a24b13..4cc740ec0 100644 --- a/tests/regression/tools/client/Makefile.am +++ b/tests/regression/tools/client/Makefile.am @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only -noinst_SCRIPTS = test_session_commands.py -EXTRA_DIST = test_session_commands.py +noinst_SCRIPTS = test_session_commands.py test_event_rule_listing.py +EXTRA_DIST = test_session_commands.py test_event_rule_listing.py all-local: @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ diff --git a/tests/regression/tools/client/test_event_rule_listing.py b/tests/regression/tools/client/test_event_rule_listing.py new file mode 100755 index 000000000..e06deeea2 --- /dev/null +++ b/tests/regression/tools/client/test_event_rule_listing.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 Jérémie Galarneau +# +# SPDX-License-Identifier: GPL-2.0-only + +import pathlib +import sys +import os +from typing import Any, Callable, Type, Dict, Iterator +import random +import string +from collections.abc import Mapping + +""" +Test the listing of recording rules associated to a channel. +""" + +# Import in-tree test utils +test_utils_import_path = pathlib.Path(__file__).absolute().parents[3] / "utils" +sys.path.append(str(test_utils_import_path)) + +import lttngtest + + +def test_identical_recording_rules_except_log_level_rule_type(tap, test_env): + # type: (lttngtest.TapGenerator, lttngtest._Environment) -> None + tap.diagnostic( + "Test adding and listing event rules that differ only by their log level rule type" + ) + + client = lttngtest.LTTngClient(test_env, log=tap.diagnostic) + + session = client.create_session() + channel = session.add_channel(lttngtest.TracingDomain.User) + session.start() + + app = test_env.launch_wait_trace_test_application(100) + + llr_exactly = lttngtest.LogLevelRuleExactly(lttngtest.UserLogLevel.DEBUG_LINE) + llr_as_severe_as = lttngtest.LogLevelRuleAsSevereAs( + lttngtest.UserLogLevel.DEBUG_LINE + ) + + recording_rule_log_at_level = lttngtest.UserTracepointEventRule( + "lttng*", None, llr_exactly, None + ) + recording_rule_log_at_least_level = lttngtest.UserTracepointEventRule( + "lttng*", None, llr_as_severe_as, None + ) + recording_rule_no_log_level = lttngtest.UserTracepointEventRule( + "lttng*", None, None, None + ) + + with tap.case("Adding a recording rule with an `exact` log level rule"): + channel.add_recording_rule(recording_rule_log_at_level) + + with tap.case("Adding a recording rule with an `as severe as` log level rule"): + channel.add_recording_rule(recording_rule_log_at_least_level) + + with tap.case( + "Adding a recording rule without a log level rule (all log levels enabled)" + ): + channel.add_recording_rule(recording_rule_no_log_level) + + rule_match_count = 0 + for rule in channel.recording_rules: + if ( + rule != recording_rule_no_log_level + and rule != recording_rule_log_at_level + and rule != recording_rule_log_at_least_level + ): + continue + + rule_match_count = rule_match_count + 1 + + tap.test( + rule_match_count == 3, + "Recording rules are added and listed", + ) + + +tap = lttngtest.TapGenerator(4) +tap.diagnostic("Test the addition and listing of event rules associated to a channel") + +with lttngtest.test_environment(with_sessiond=True, log=tap.diagnostic) as test_env: + test_identical_recording_rules_except_log_level_rule_type(tap, test_env) + +sys.exit(0 if tap.is_successful else 1) diff --git a/tests/utils/lttngtest/lttng.py b/tests/utils/lttngtest/lttng.py index 9dd21fb03..76c60b5b0 100644 --- a/tests/utils/lttngtest/lttng.py +++ b/tests/utils/lttngtest/lttng.py @@ -6,9 +6,8 @@ from . import lttngctl, logger, environment -import pathlib import os -from typing import Callable, Optional, Type, Union +from typing import Callable, Optional, Type, Union, Iterator import shlex import subprocess import enum @@ -31,20 +30,32 @@ class InvalidMI(lttngctl.ControlException): super().__init__(msg) +class ChannelNotFound(lttngctl.ControlException): + def __init__(self, msg): + # type: (str) -> None + super().__init__(msg) + + def _get_domain_option_name(domain): # type: (lttngctl.TracingDomain) -> str - if domain == lttngctl.TracingDomain.User: - return "userspace" - elif domain == lttngctl.TracingDomain.Kernel: - return "kernel" - elif domain == lttngctl.TracingDomain.Log4j: - return "log4j" - elif domain == lttngctl.TracingDomain.JUL: - return "jul" - elif domain == lttngctl.TracingDomain.Python: - return "python" - else: - raise Unsupported("Domain `{domain_name}` is not supported by the LTTng client") + return { + lttngctl.TracingDomain.User: "userspace", + lttngctl.TracingDomain.Kernel: "kernel", + lttngctl.TracingDomain.Log4j: "log4j", + lttngctl.TracingDomain.Python: "python", + lttngctl.TracingDomain.JUL: "jul", + }[domain] + + +def _get_domain_xml_mi_name(domain): + # type: (lttngctl.TracingDomain) -> str + return { + lttngctl.TracingDomain.User: "UST", + lttngctl.TracingDomain.Kernel: "KERNEL", + lttngctl.TracingDomain.Log4j: "LOG4J", + lttngctl.TracingDomain.Python: "PYTHON", + lttngctl.TracingDomain.JUL: "JUL", + }[domain] def _get_context_type_name(context): @@ -67,6 +78,117 @@ def _get_context_type_name(context): ) +def _get_log_level_argument_name(log_level): + # type: (lttngctl.LogLevel) -> str + if isinstance(log_level, lttngctl.UserLogLevel): + return { + lttngctl.UserLogLevel.EMERGENCY: "EMER", + lttngctl.UserLogLevel.ALERT: "ALERT", + lttngctl.UserLogLevel.CRITICAL: "CRIT", + lttngctl.UserLogLevel.ERROR: "ERR", + lttngctl.UserLogLevel.WARNING: "WARNING", + lttngctl.UserLogLevel.NOTICE: "NOTICE", + lttngctl.UserLogLevel.INFO: "INFO", + lttngctl.UserLogLevel.DEBUG_SYSTEM: "DEBUG_SYSTEM", + lttngctl.UserLogLevel.DEBUG_PROGRAM: "DEBUG_PROGRAM", + lttngctl.UserLogLevel.DEBUG_PROCESS: "DEBUG_PROCESS", + lttngctl.UserLogLevel.DEBUG_MODULE: "DEBUG_MODULE", + lttngctl.UserLogLevel.DEBUG_UNIT: "DEBUG_UNIT", + lttngctl.UserLogLevel.DEBUG_FUNCTION: "DEBUG_FUNCTION", + lttngctl.UserLogLevel.DEBUG_LINE: "DEBUG_LINE", + lttngctl.UserLogLevel.DEBUG: "DEBUG", + }[log_level] + elif isinstance(log_level, lttngctl.JULLogLevel): + return { + lttngctl.JULLogLevel.OFF: "OFF", + lttngctl.JULLogLevel.SEVERE: "SEVERE", + lttngctl.JULLogLevel.WARNING: "WARNING", + lttngctl.JULLogLevel.INFO: "INFO", + lttngctl.JULLogLevel.CONFIG: "CONFIG", + lttngctl.JULLogLevel.FINE: "FINE", + lttngctl.JULLogLevel.FINER: "FINER", + lttngctl.JULLogLevel.FINEST: "FINEST", + lttngctl.JULLogLevel.ALL: "ALL", + }[log_level] + elif isinstance(log_level, lttngctl.Log4jLogLevel): + return { + lttngctl.Log4jLogLevel.OFF: "OFF", + lttngctl.Log4jLogLevel.FATAL: "FATAL", + lttngctl.Log4jLogLevel.ERROR: "ERROR", + lttngctl.Log4jLogLevel.WARN: "WARN", + lttngctl.Log4jLogLevel.INFO: "INFO", + lttngctl.Log4jLogLevel.DEBUG: "DEBUG", + lttngctl.Log4jLogLevel.TRACE: "TRACE", + lttngctl.Log4jLogLevel.ALL: "ALL", + }[log_level] + elif isinstance(log_level, lttngctl.PythonLogLevel): + return { + lttngctl.PythonLogLevel.CRITICAL: "CRITICAL", + lttngctl.PythonLogLevel.ERROR: "ERROR", + lttngctl.PythonLogLevel.WARNING: "WARNING", + lttngctl.PythonLogLevel.INFO: "INFO", + lttngctl.PythonLogLevel.DEBUG: "DEBUG", + lttngctl.PythonLogLevel.NOTSET: "NOTSET", + }[log_level] + + raise TypeError("Unknown log level type") + + +def _get_log_level_from_mi_log_level_name(mi_log_level_name): + # type: (str) -> lttngctl.LogLevel + return { + "TRACE_EMERG": lttngctl.UserLogLevel.EMERGENCY, + "TRACE_ALERT": lttngctl.UserLogLevel.ALERT, + "TRACE_CRIT": lttngctl.UserLogLevel.CRITICAL, + "TRACE_ERR": lttngctl.UserLogLevel.ERROR, + "TRACE_WARNING": lttngctl.UserLogLevel.WARNING, + "TRACE_NOTICE": lttngctl.UserLogLevel.NOTICE, + "TRACE_INFO": lttngctl.UserLogLevel.INFO, + "TRACE_DEBUG_SYSTEM": lttngctl.UserLogLevel.DEBUG_SYSTEM, + "TRACE_DEBUG_PROGRAM": lttngctl.UserLogLevel.DEBUG_PROGRAM, + "TRACE_DEBUG_PROCESS": lttngctl.UserLogLevel.DEBUG_PROCESS, + "TRACE_DEBUG_MODULE": lttngctl.UserLogLevel.DEBUG_MODULE, + "TRACE_DEBUG_UNIT": lttngctl.UserLogLevel.DEBUG_UNIT, + "TRACE_DEBUG_FUNCTION": lttngctl.UserLogLevel.DEBUG_FUNCTION, + "TRACE_DEBUG_LINE": lttngctl.UserLogLevel.DEBUG_LINE, + "TRACE_DEBUG": lttngctl.UserLogLevel.DEBUG, + "JUL_OFF": lttngctl.JULLogLevel.OFF, + "JUL_SEVERE": lttngctl.JULLogLevel.SEVERE, + "JUL_WARNING": lttngctl.JULLogLevel.WARNING, + "JUL_INFO": lttngctl.JULLogLevel.INFO, + "JUL_CONFIG": lttngctl.JULLogLevel.CONFIG, + "JUL_FINE": lttngctl.JULLogLevel.FINE, + "JUL_FINER": lttngctl.JULLogLevel.FINER, + "JUL_FINEST": lttngctl.JULLogLevel.FINEST, + "JUL_ALL": lttngctl.JULLogLevel.ALL, + "LOG4J_OFF": lttngctl.Log4jLogLevel.OFF, + "LOG4J_FATAL": lttngctl.Log4jLogLevel.FATAL, + "LOG4J_ERROR": lttngctl.Log4jLogLevel.ERROR, + "LOG4J_WARN": lttngctl.Log4jLogLevel.WARN, + "LOG4J_INFO": lttngctl.Log4jLogLevel.INFO, + "LOG4J_DEBUG": lttngctl.Log4jLogLevel.DEBUG, + "LOG4J_TRACE": lttngctl.Log4jLogLevel.TRACE, + "LOG4J_ALL": lttngctl.Log4jLogLevel.ALL, + "PYTHON_CRITICAL": lttngctl.PythonLogLevel.CRITICAL, + "PYTHON_ERROR": lttngctl.PythonLogLevel.ERROR, + "PYTHON_WARNING": lttngctl.PythonLogLevel.WARNING, + "PYTHON_INFO": lttngctl.PythonLogLevel.INFO, + "PYTHON_DEBUG": lttngctl.PythonLogLevel.DEBUG, + "PYTHON_NOTSET": lttngctl.PythonLogLevel.NOTSET, + }[mi_log_level_name] + + +def _get_tracepoint_event_rule_class_from_domain_type(domain_type): + # type: (lttngctl.TracingDomain) -> Type[lttngctl.UserTracepointEventRule] | Type[lttngctl.Log4jTracepointEventRule] | Type[lttngctl.JULTracepointEventRule] | Type[lttngctl.PythonTracepointEventRule] | Type[lttngctl.KernelTracepointEventRule] + return { + lttngctl.TracingDomain.User: lttngctl.UserTracepointEventRule, + lttngctl.TracingDomain.JUL: lttngctl.JULTracepointEventRule, + lttngctl.TracingDomain.Log4j: lttngctl.Log4jTracepointEventRule, + lttngctl.TracingDomain.Python: lttngctl.PythonTracepointEventRule, + lttngctl.TracingDomain.Kernel: lttngctl.KernelTracepointEventRule, + }[domain_type] + + class _Channel(lttngctl.Channel): def __init__( self, @@ -120,11 +242,15 @@ class _Channel(lttngctl.Channel): if rule.log_level_rule: if isinstance(rule.log_level_rule, lttngctl.LogLevelRuleAsSevereAs): client_args = client_args + " --loglevel {log_level}".format( - log_level=rule.log_level_rule.level + log_level=_get_log_level_argument_name( + rule.log_level_rule.level + ) ) elif isinstance(rule.log_level_rule, lttngctl.LogLevelRuleExactly): client_args = client_args + " --loglevel-only {log_level}".format( - log_level=rule.log_level_rule.level + log_level=_get_log_level_argument_name( + rule.log_level_rule.level + ) ) else: raise Unsupported( @@ -158,6 +284,115 @@ class _Channel(lttngctl.Channel): # type: () -> lttngctl.TracingDomain return self._domain + @property + def recording_rules(self): + # type: () -> Iterator[lttngctl.EventRule] + list_session_xml = self._client._run_cmd( + "list '{session_name}'".format(session_name=self._session.name), + LTTngClient.CommandOutputFormat.MI_XML, + ) + + root = xml.etree.ElementTree.fromstring(list_session_xml) + command_output = LTTngClient._mi_get_in_element(root, "output") + sessions = LTTngClient._mi_get_in_element(command_output, "sessions") + + # The channel's session is supposed to be the only session returned by the command + if len(sessions) != 1: + raise InvalidMI( + "Only one session expected when listing with an explicit session name" + ) + session = sessions[0] + + # Look for the channel's domain + target_domain = None + target_domain_mi_name = _get_domain_xml_mi_name(self.domain) + for domain in LTTngClient._mi_get_in_element(session, "domains"): + if ( + LTTngClient._mi_get_in_element(domain, "type").text + == target_domain_mi_name + ): + target_domain = domain + + if target_domain is None: + raise ChannelNotFound( + "Failed to find channel `{channel_name}`: no channel in target domain".format( + channel_name=self.name + ) + ) + + target_channel = None + for channel in LTTngClient._mi_get_in_element(target_domain, "channels"): + if LTTngClient._mi_get_in_element(channel, "name").text == self.name: + target_channel = channel + break + + if target_channel is None: + raise ChannelNotFound( + "Failed to find channel `{channel_name}`: no such channel in target domain".format( + channel_name=self.name + ) + ) + + tracepoint_event_rule_class = None + + for event in LTTngClient._mi_get_in_element(target_channel, "events"): + # Note that the "enabled" property is ignored as it is not exposed by + # the EventRule interface. + pattern = LTTngClient._mi_get_in_element(event, "name").text + type = LTTngClient._mi_get_in_element(event, "type").text + + filter_expression = None + filter_expression_element = LTTngClient._mi_find_in_element( + event, "filter_expression" + ) + if filter_expression_element: + filter_expression = filter_expression_element.text + + exclusions = [] + for exclusion in LTTngClient._mi_get_in_element(event, "exclusions"): + exclusions.append(exclusion.text) + + exclusions = exclusions if len(exclusions) > 0 else None + + if type != "TRACEPOINT": + raise Unsupported( + "Non-tracepoint event rules are not supported by this Controller implementation" + ) + + tracepoint_event_rule_class = ( + _get_tracepoint_event_rule_class_from_domain_type(self.domain) + ) + event_rule = None + if self.domain != lttngctl.TracingDomain.Kernel: + log_level_element = LTTngClient._mi_find_in_element(event, "loglevel") + log_level_type_element = LTTngClient._mi_find_in_element( + event, "loglevel_type" + ) + + log_level_rule = None + if log_level_element is not None and log_level_type_element is not None: + if log_level_element.text is None: + raise InvalidMI("`loglevel` element of event rule has no text") + + if log_level_type_element.text == "RANGE": + log_level_rule = lttngctl.LogLevelRuleAsSevereAs( + _get_log_level_from_mi_log_level_name( + log_level_element.text + ) + ) + elif log_level_type_element.text == "SINGLE": + log_level_rule = lttngctl.LogLevelRuleExactly( + _get_log_level_from_mi_log_level_name( + log_level_element.text + ) + ) + + yield tracepoint_event_rule_class( + pattern, filter_expression, log_level_rule, exclusions + ) + else: + yield tracepoint_event_rule_class(pattern, filter_expression) + @enum.unique class _ProcessAttribute(enum.Enum): @@ -305,11 +540,11 @@ class _Session(lttngctl.Session): ) root = xml.etree.ElementTree.fromstring(list_session_xml) - command_output = LTTngClient._mi_find_in_element(root, "output") - sessions = LTTngClient._mi_find_in_element(command_output, "sessions") - session_mi = LTTngClient._mi_find_in_element(sessions, "session") + command_output = LTTngClient._mi_get_in_element(root, "output") + sessions = LTTngClient._mi_get_in_element(command_output, "sessions") + session_mi = LTTngClient._mi_get_in_element(sessions, "session") - enabled_text = LTTngClient._mi_find_in_element(session_mi, "enabled").text + enabled_text = LTTngClient._mi_get_in_element(session_mi, "enabled").text if enabled_text not in ["true", "false"]: raise InvalidMI( "Expected boolean value in element '{}': value='{}'".format( @@ -426,6 +661,7 @@ class LTTngClient(logger._Logger, lttngctl.Controller): decoded_output = out.decode("utf-8") for error_line in decoded_output.splitlines(): self._log(error_line) + raise LTTngClientError(command_args, decoded_output) else: return out.decode("utf-8") @@ -509,8 +745,13 @@ class LTTngClient(logger._Logger, lttngctl.Controller): @staticmethod def _mi_find_in_element(element, sub_element_name): + # type: (xml.etree.ElementTree.Element, str) -> Optional[xml.etree.ElementTree.Element] + return element.find(LTTngClient._namespaced_mi_element(sub_element_name)) + + @staticmethod + def _mi_get_in_element(element, sub_element_name): # type: (xml.etree.ElementTree.Element, str) -> xml.etree.ElementTree.Element - result = element.find(LTTngClient._namespaced_mi_element(sub_element_name)) + result = LTTngClient._mi_find_in_element(element, sub_element_name) if result is None: raise InvalidMI( "Failed to find element '{}' within command MI element '{}'".format( @@ -527,14 +768,14 @@ class LTTngClient(logger._Logger, lttngctl.Controller): ) root = xml.etree.ElementTree.fromstring(list_sessions_xml) - command_output = self._mi_find_in_element(root, "output") - sessions = self._mi_find_in_element(command_output, "sessions") + command_output = self._mi_get_in_element(root, "output") + sessions = self._mi_get_in_element(command_output, "sessions") ctl_sessions = [] # type: list[lttngctl.Session] for session_mi in sessions: - name = self._mi_find_in_element(session_mi, "name").text - path = self._mi_find_in_element(session_mi, "path").text + name = self._mi_get_in_element(session_mi, "name").text + path = self._mi_get_in_element(session_mi, "path").text if name is None: raise InvalidMI( diff --git a/tests/utils/lttngtest/lttngctl.py b/tests/utils/lttngtest/lttngctl.py index 2595c6eb4..c62e74f47 100644 --- a/tests/utils/lttngtest/lttngctl.py +++ b/tests/utils/lttngtest/lttngctl.py @@ -107,28 +107,92 @@ class EventRule(abc.ABC): class LogLevelRule: + def __eq__(self, other): + # type (LogLevelRule) -> bool + if type(self) != type(other): + return False + + return self.level == other.level + + +@enum.unique +class LogLevel(enum.Enum): pass +@enum.unique +class UserLogLevel(LogLevel): + EMERGENCY = 0 + ALERT = 1 + CRITICAL = 2 + ERROR = 3 + WARNING = 4 + NOTICE = 5 + INFO = 6 + DEBUG_SYSTEM = 7 + DEBUG_PROGRAM = 8 + DEBUG_PROCESS = 9 + DEBUG_MODULE = 10 + DEBUG_UNIT = 11 + DEBUG_FUNCTION = 12 + DEBUG_LINE = 13 + DEBUG = 14 + + +@enum.unique +class JULLogLevel(LogLevel): + OFF = 2147483647 + SEVERE = 1000 + WARNING = 900 + INFO = 800 + CONFIG = 700 + FINE = 500 + FINER = 400 + FINEST = 300 + ALL = -2147483648 + + +@enum.unique +class Log4jLogLevel(LogLevel): + OFF = 2147483647 + FATAL = 50000 + ERROR = 40000 + WARN = 30000 + INFO = 20000 + DEBUG = 10000 + TRACE = 5000 + ALL = -2147483648 + + +@enum.unique +class PythonLogLevel(LogLevel): + CRITICAL = 50 + ERROR = 40 + WARNING = 30 + INFO = 20 + DEBUG = 10 + NOTSET = 0 + + class LogLevelRuleAsSevereAs(LogLevelRule): def __init__(self, level): - # type: (int) + # type: (LogLevel) self._level = level @property def level(self): - # type: () -> int + # type: () -> LogLevel return self._level class LogLevelRuleExactly(LogLevelRule): def __init__(self, level): - # type: (int) + # type: (LogLevel) self._level = level @property def level(self): - # type: () -> int + # type: () -> LogLevel return self._level @@ -137,15 +201,27 @@ class TracepointEventRule(EventRule): self, name_pattern=None, # type: Optional[str] filter_expression=None, # type: Optional[str] - log_level_rule=None, # type: Optional[LogLevelRule] - name_pattern_exclusions=None, # type: Optional[List[str]] ): self._name_pattern = name_pattern # type: Optional[str] self._filter_expression = filter_expression # type: Optional[str] - self._log_level_rule = log_level_rule # type: Optional[LogLevelRule] - self._name_pattern_exclusions = ( - name_pattern_exclusions - ) # type: Optional[List[str]] + + def _equals(self, other): + # type (TracepointEventRule) -> bool + # Overridden by derived classes that have supplementary attributes. + return True + + def __eq__(self, other): + # type (TracepointEventRule) -> bool + if type(self) != type(other): + return False + + if self.name_pattern != other.name_pattern: + return False + + if self.filter_expression != other.filter_expression: + return False + + return self._equals(other) @property def name_pattern(self): @@ -157,6 +233,31 @@ class TracepointEventRule(EventRule): # type: () -> Optional[str] return self._filter_expression + +class UserTracepointEventRule(TracepointEventRule): + def __init__( + self, + name_pattern=None, # type: Optional[str] + filter_expression=None, # type: Optional[str] + log_level_rule=None, # type: Optional[LogLevelRule] + name_pattern_exclusions=None, # type: Optional[List[str]] + ): + TracepointEventRule.__init__(self, name_pattern, filter_expression) + self._log_level_rule = log_level_rule # type: Optional[LogLevelRule] + self._name_pattern_exclusions = ( + name_pattern_exclusions + ) # type: Optional[List[str]] + + if log_level_rule and not isinstance(log_level_rule.level, UserLogLevel): + raise ValueError("Log level rule must use a UserLogLevel as its value") + + def _equals(self, other): + # type (UserTracepointEventRule) -> bool + return ( + self.log_level_rule == other.log_level_rule + and self.name_pattern_exclusions == other.name_pattern_exclusions + ) + @property def log_level_rule(self): # type: () -> Optional[LogLevelRule] @@ -168,7 +269,7 @@ class TracepointEventRule(EventRule): return self._name_pattern_exclusions -class UserTracepointEventRule(TracepointEventRule): +class Log4jTracepointEventRule(TracepointEventRule): def __init__( self, name_pattern=None, # type: Optional[str] @@ -176,16 +277,108 @@ class UserTracepointEventRule(TracepointEventRule): log_level_rule=None, # type: Optional[LogLevelRule] name_pattern_exclusions=None, # type: Optional[List[str]] ): - TracepointEventRule.__init__(**locals()) + TracepointEventRule.__init__(self, name_pattern, filter_expression) + self._log_level_rule = log_level_rule # type: Optional[LogLevelRule] + self._name_pattern_exclusions = ( + name_pattern_exclusions + ) # type: Optional[List[str]] + if log_level_rule and not isinstance(log_level_rule.level, Log4jLogLevel): + raise ValueError("Log level rule must use a Log4jLogLevel as its value") -class KernelTracepointEventRule(TracepointEventRule): + def _equals(self, other): + # type (Log4jTracepointEventRule) -> bool + return ( + self.log_level_rule == other.log_level_rule + and self.name_pattern_exclusions == other.name_pattern_exclusions + ) + + @property + def log_level_rule(self): + # type: () -> Optional[LogLevelRule] + return self._log_level_rule + + @property + def name_pattern_exclusions(self): + # type: () -> Optional[List[str]] + return self._name_pattern_exclusions + + +class JULTracepointEventRule(TracepointEventRule): def __init__( self, name_pattern=None, # type: Optional[str] filter_expression=None, # type: Optional[str] log_level_rule=None, # type: Optional[LogLevelRule] name_pattern_exclusions=None, # type: Optional[List[str]] + ): + TracepointEventRule.__init__(self, name_pattern, filter_expression) + self._log_level_rule = log_level_rule # type: Optional[LogLevelRule] + self._name_pattern_exclusions = ( + name_pattern_exclusions + ) # type: Optional[List[str]] + + if log_level_rule and not isinstance(log_level_rule.level, JULLogLevel): + raise ValueError("Log level rule must use a JULLogLevel as its value") + + def _equals(self, other): + # type (JULTracepointEventRule) -> bool + return ( + self.log_level_rule == other.log_level_rule + and self.name_pattern_exclusions == other.name_pattern_exclusions + ) + + @property + def log_level_rule(self): + # type: () -> Optional[LogLevelRule] + return self._log_level_rule + + @property + def name_pattern_exclusions(self): + # type: () -> Optional[List[str]] + return self._name_pattern_exclusions + + +class PythonTracepointEventRule(TracepointEventRule): + def __init__( + self, + name_pattern=None, # type: Optional[str] + filter_expression=None, # type: Optional[str] + log_level_rule=None, # type: Optional[LogLevelRule] + name_pattern_exclusions=None, # type: Optional[List[str]] + ): + TracepointEventRule.__init__(self, name_pattern, filter_expression) + self._log_level_rule = log_level_rule # type: Optional[LogLevelRule] + self._name_pattern_exclusions = ( + name_pattern_exclusions + ) # type: Optional[List[str]] + + if log_level_rule and not isinstance(log_level_rule.level, PythonLogLevel): + raise ValueError("Log level rule must use a PythonLogLevel as its value") + + def _equals(self, other): + # type (PythonTracepointEventRule) -> bool + return ( + self.log_level_rule == other.log_level_rule + and self.name_pattern_exclusions == other.name_pattern_exclusions + ) + + @property + def log_level_rule(self): + # type: () -> Optional[LogLevelRule] + return self._log_level_rule + + @property + def name_pattern_exclusions(self): + # type: () -> Optional[List[str]] + return self._name_pattern_exclusions + + +class KernelTracepointEventRule(TracepointEventRule): + def __init__( + self, + name_pattern=None, # type: Optional[str] + filter_expression=None, # type: Optional[str] ): TracepointEventRule.__init__(**locals()) -- 2.34.1