Merge "Enforce log messages not being translated"
This commit is contained in:
@@ -17,7 +17,7 @@ Neutron Library Specific Commandments
|
|||||||
- [N529] Method's default argument shouldn't be mutable
|
- [N529] Method's default argument shouldn't be mutable
|
||||||
- [N530] No importing of neutron; should be ignored in neutron itself
|
- [N530] No importing of neutron; should be ignored in neutron itself
|
||||||
- [N532] Validate that LOG.warning is used instead of LOG.warn. The latter is deprecated.
|
- [N532] Validate that LOG.warning is used instead of LOG.warn. The latter is deprecated.
|
||||||
- [N533] Validate that debug level logs are not translated
|
|
||||||
- [N534] Exception messages should be translated
|
- [N534] Exception messages should be translated
|
||||||
- [N535] Usage of Python eventlet module not allowed
|
- [N535] Usage of Python eventlet module not allowed
|
||||||
- [N536] Use assertIsNone/assertIsNotNone rather than assertEqual/assertIs to check None values.
|
- [N536] Use assertIsNone/assertIsNotNone rather than assertEqual/assertIs to check None values.
|
||||||
|
- [N537] Don't translate logs.
|
||||||
|
|||||||
@@ -20,15 +20,5 @@ import oslo_i18n
|
|||||||
|
|
||||||
_translators = oslo_i18n.TranslatorFactory(domain='neutron_lib')
|
_translators = oslo_i18n.TranslatorFactory(domain='neutron_lib')
|
||||||
|
|
||||||
# The primary translation function using the well-known name "_"
|
# The translation function using the well-known name "_"
|
||||||
_ = _translators.primary
|
_ = _translators.primary
|
||||||
|
|
||||||
# Translators for log levels.
|
|
||||||
#
|
|
||||||
# The abbreviated names are meant to reflect the usual use of a short
|
|
||||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
|
||||||
# the level.
|
|
||||||
_LI = _translators.log_info
|
|
||||||
_LW = _translators.log_warning
|
|
||||||
_LE = _translators.log_error
|
|
||||||
_LC = _translators.log_critical
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import collections
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import reflection
|
from oslo_utils import reflection
|
||||||
|
|
||||||
from neutron_lib._i18n import _LE
|
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
from neutron_lib.callbacks import exceptions
|
from neutron_lib.callbacks import exceptions
|
||||||
from neutron_lib.db import utils as db_utils
|
from neutron_lib.db import utils as db_utils
|
||||||
@@ -176,8 +175,8 @@ class CallbacksManager(object):
|
|||||||
event.startswith(events.PRECOMMIT)
|
event.startswith(events.PRECOMMIT)
|
||||||
)
|
)
|
||||||
if not abortable_event:
|
if not abortable_event:
|
||||||
LOG.exception(_LE("Error during notification for "
|
LOG.exception("Error during notification for "
|
||||||
"%(callback)s %(resource)s, %(event)s"),
|
"%(callback)s %(resource)s, %(event)s",
|
||||||
{'callback': callback_id,
|
{'callback': callback_id,
|
||||||
'resource': resource, 'event': event})
|
'resource': resource, 'event': event})
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ def factory(register):
|
|||||||
register(check_python3_no_iteritems)
|
register(check_python3_no_iteritems)
|
||||||
register(no_mutable_default_args)
|
register(no_mutable_default_args)
|
||||||
register(check_neutron_namespace_imports)
|
register(check_neutron_namespace_imports)
|
||||||
register(translation_checks.no_translate_debug_logs)
|
register(translation_checks.no_translate_logs)
|
||||||
register(translation_checks.check_log_warn_deprecated)
|
register(translation_checks.check_log_warn_deprecated)
|
||||||
register(translation_checks.check_raised_localized_exceptions)
|
register(translation_checks.check_raised_localized_exceptions)
|
||||||
register(assert_equal_none)
|
register(assert_equal_none)
|
||||||
|
|||||||
@@ -15,14 +15,22 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
_all_log_levels = {'critical', 'error', 'exception', 'info', 'warning'}
|
||||||
_all_hints = {'_LC', '_LE', '_LI', '_', '_LW'}
|
_all_hints = {'_LC', '_LE', '_LI', '_', '_LW'}
|
||||||
|
|
||||||
|
|
||||||
_log_warn = re.compile(
|
_log_warn = re.compile(
|
||||||
r"(.)*LOG\.(warn)\(\s*('|\"|_)")
|
r"(.)*LOG\.(warn)\(\s*('|\")")
|
||||||
|
|
||||||
|
|
||||||
def _translation_is_not_expected(filename):
|
_log_translation_hint = re.compile(
|
||||||
|
r".*LOG\.(%(levels)s)\(\s*(%(hints)s)\(" % {
|
||||||
|
'levels': '|'.join(_all_log_levels),
|
||||||
|
'hints': '|'.join(_all_hints),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def _translation_checks_not_enforced(filename):
|
||||||
# Do not do these validations on tests
|
# Do not do these validations on tests
|
||||||
return any(pat in filename for pat in ["/tests/", "rally-jobs/plugins/"])
|
return any(pat in filename for pat in ["/tests/", "rally-jobs/plugins/"])
|
||||||
|
|
||||||
@@ -41,28 +49,6 @@ def check_log_warn_deprecated(logical_line, filename):
|
|||||||
yield (0, msg)
|
yield (0, msg)
|
||||||
|
|
||||||
|
|
||||||
def no_translate_debug_logs(logical_line, filename):
|
|
||||||
"""N533 - Don't translate debug level logs.
|
|
||||||
|
|
||||||
Check for 'LOG.debug(_(' and 'LOG.debug(_Lx('
|
|
||||||
|
|
||||||
As per our translation policy,
|
|
||||||
https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation
|
|
||||||
we shouldn't translate debug level logs.
|
|
||||||
|
|
||||||
* This check assumes that 'LOG' is a logger.
|
|
||||||
|
|
||||||
:param logical_line: The logical line to check.
|
|
||||||
:param filename: The file name where the logical line exists.
|
|
||||||
:returns: None if the logical line passes the check, otherwise a tuple
|
|
||||||
is yielded that contains the offending index in logical line and a
|
|
||||||
message describe the check validation failure.
|
|
||||||
"""
|
|
||||||
for hint in _all_hints:
|
|
||||||
if logical_line.startswith("LOG.debug(%s(" % hint):
|
|
||||||
yield(0, "N533 Don't translate debug level logs")
|
|
||||||
|
|
||||||
|
|
||||||
def check_raised_localized_exceptions(logical_line, filename):
|
def check_raised_localized_exceptions(logical_line, filename):
|
||||||
"""N534 - Untranslated exception message.
|
"""N534 - Untranslated exception message.
|
||||||
|
|
||||||
@@ -72,7 +58,7 @@ def check_raised_localized_exceptions(logical_line, filename):
|
|||||||
is yielded that contains the offending index in logical line and a
|
is yielded that contains the offending index in logical line and a
|
||||||
message describe the check validation failure.
|
message describe the check validation failure.
|
||||||
"""
|
"""
|
||||||
if _translation_is_not_expected(filename):
|
if _translation_checks_not_enforced(filename):
|
||||||
return
|
return
|
||||||
|
|
||||||
logical_line = logical_line.strip()
|
logical_line = logical_line.strip()
|
||||||
@@ -83,3 +69,28 @@ def check_raised_localized_exceptions(logical_line, filename):
|
|||||||
if exception_msg.startswith("\"") or exception_msg.startswith("\'"):
|
if exception_msg.startswith("\"") or exception_msg.startswith("\'"):
|
||||||
msg = "N534: Untranslated exception message."
|
msg = "N534: Untranslated exception message."
|
||||||
yield (logical_line.index(exception_msg), msg)
|
yield (logical_line.index(exception_msg), msg)
|
||||||
|
|
||||||
|
|
||||||
|
def no_translate_logs(logical_line, filename):
|
||||||
|
"""N537 - Don't translate logs.
|
||||||
|
|
||||||
|
Check for 'LOG.*(_(' and 'LOG.*(_Lx('
|
||||||
|
|
||||||
|
Translators don't provide translations for log messages, and operators
|
||||||
|
asked not to translate them.
|
||||||
|
|
||||||
|
* This check assumes that 'LOG' is a logger.
|
||||||
|
|
||||||
|
:param logical_line: The logical line to check.
|
||||||
|
:param filename: The file name where the logical line exists.
|
||||||
|
:returns: None if the logical line passes the check, otherwise a tuple
|
||||||
|
is yielded that contains the offending index in logical line and a
|
||||||
|
message describe the check validation failure.
|
||||||
|
"""
|
||||||
|
if _translation_checks_not_enforced(filename):
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = "N537: Log messages should not be translated!"
|
||||||
|
match = _log_translation_hint.match(logical_line)
|
||||||
|
if match:
|
||||||
|
yield (logical_line.index(match.group()), msg)
|
||||||
|
|||||||
@@ -134,15 +134,24 @@ class HackingTestCase(base.BaseTestCase):
|
|||||||
self.assertLineFails(f, 'from neutron import context')
|
self.assertLineFails(f, 'from neutron import context')
|
||||||
self.assertLineFails(f, 'import neutron.common.config')
|
self.assertLineFails(f, 'import neutron.common.config')
|
||||||
|
|
||||||
def test_no_translate_debug_logs(self):
|
def test_no_log_translations(self):
|
||||||
|
for log in tc._all_log_levels:
|
||||||
for hint in tc._all_hints:
|
for hint in tc._all_hints:
|
||||||
bad = "LOG.debug(%s('bad'))" % hint
|
bad = 'LOG.%s(%s("Bad"))' % (log, hint)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
1, len(list(tc.no_translate_debug_logs(bad, 'f'))))
|
1, len(list(tc.no_translate_logs(bad, 'f'))))
|
||||||
|
# Catch abuses when used with a variable and not a literal
|
||||||
|
bad = 'LOG.%s(%s(msg))' % (log, hint)
|
||||||
|
self.assertEqual(
|
||||||
|
1, len(list(tc.no_translate_logs(bad, 'f'))))
|
||||||
|
# Do not do validations in tests
|
||||||
|
ok = 'LOG.%s(_("OK - unit tests"))' % log
|
||||||
|
self.assertEqual(
|
||||||
|
0, len(list(tc.no_translate_logs(ok, 'f/tests/f'))))
|
||||||
|
|
||||||
def test_check_log_warn_deprecated(self):
|
def test_check_log_warn_deprecated(self):
|
||||||
bad = "LOG.warn(_LW('i am deprecated!'))"
|
bad = "LOG.warn('i am deprecated!')"
|
||||||
good = "LOG.warning(_LW('zlatan is the best'))"
|
good = "LOG.warning('zlatan is the best')"
|
||||||
f = tc.check_log_warn_deprecated
|
f = tc.check_log_warn_deprecated
|
||||||
self.assertLineFails(f, bad, '')
|
self.assertLineFails(f, bad, '')
|
||||||
self.assertLinePasses(f, good, '')
|
self.assertLinePasses(f, good, '')
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
New ``N537`` hacking check is introduced that enforces no logging message
|
||||||
|
translations, in any logging level. The check is enabled by default. Also,
|
||||||
|
the ``N533`` hacking check is now removed because it is covered by
|
||||||
|
``N537``.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
Library consumers may need to adopt their code to new requirements of ``N537`` hacking check,
|
||||||
|
removing translation markers from all logging messages. If for some reason
|
||||||
|
it doesn't fit the project, consumers can disable the new hacking check
|
||||||
|
using ``ignore`` statement in ``flake8`` section of their ``tox.ini`` file,
|
||||||
|
or by other means.
|
||||||
Reference in New Issue
Block a user