-
Notifications
You must be signed in to change notification settings - Fork 342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New command logger logcm as a replacement of print calls (updated) #3036
base: master
Are you sure you want to change the base?
Conversation
@t-woerner thanks for the patch. I have one small suggestion: in order to simplify maintenance backports, please split commits that convert individual files to separate commits. This way, for example, a commit that changes |
ipaserver/install/adtrust.py
Outdated
"You will not be able to establish trusts with Active " | ||
"Directory unless\nthe realm name of the IPA server matches its " | ||
"domain name.\n\n") | ||
logger.info_cmd( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this kind of stuff it should be possible to increase the log level to WARNING.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be an extra commit.
I don't understand how this approach is going to reduce the amount of merge conflicts in backports. How do multiple commits reduce the amount of conflicts when none of the commits are cherry-picked into the backport branch? |
May I propose a slightly different approach that avoids monkey patching of classes and reduces the amount of code reformatting?
|
Please only modify the installer code and unpatch the following files.
|
Suppose you have to backport a fix that was committed after these changes landed, in a proximity of the changes. When the changes split into smaller commits, a backport would result in porting a smaller base commit that adds logging support and the small patch from the affected area. If this would have been a single commit that touches multiple files, you would have to create a manual backport that will involve quite a lot of effort to make sure all pieces are not conflicting with something else. |
f26a4cd
to
5cb810a
Compare
9933545
to
341d4e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be no no calls to .format()
or %
in logcm()
. I marked some of the calls, but there are many more.
341d4e7
to
6858b59
Compare
self.ldap_uri) | ||
logcm( | ||
" Waiting for keys to appear on host: %s, please " | ||
"wait until this has completed.", self.ldap_uri |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -1052,7 +1054,7 @@ def change_admin_password(self, password): | |||
ipautil.run(args, env=env) | |||
logger.debug("ldappasswd done") | |||
except ipautil.CalledProcessError as e: | |||
print("Unable to set admin password", e) | |||
logcm("Unable to set admin password %s", e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -254,7 +256,7 @@ def record_in_hosts(ip, host_name=None, conf_file=paths.HOSTS): | |||
return None | |||
return (hosts_ip, names) | |||
except IndexError: | |||
print("Warning: Erroneous line '%s' in %s" % (line, conf_file)) | |||
logcm("Warning: Erroneous line '%s' in %s", line, conf_file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
@@ -278,7 +280,7 @@ def read_ip_addresses(): | |||
try: | |||
ip_parsed = ipautil.CheckedIPAddress(ip) | |||
except Exception as e: | |||
print("Error: Invalid IP Address %s: %s" % (ip, e)) | |||
logcm("Error: Invalid IP Address %s: %s", ip, e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -288,12 +290,12 @@ def read_ip_addresses(): | |||
def read_dns_forwarders(): | |||
addrs = [] | |||
if ipautil.user_input("Do you want to configure DNS forwarders?", True): | |||
print("Following DNS servers are configured in /etc/resolv.conf: %s" % | |||
logcm("Following DNS servers are configured in /etc/resolv.conf: %s", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
print("Error: Invalid IP Address %s: %s" % (ip, e)) | ||
print("DNS forwarder %s not added." % ip) | ||
logcm("Error: Invalid IP Address %s: %s", ip, e) | ||
logcm("DNS forwarder %s not added.", ip) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And these two lines
continue | ||
|
||
print("DNS forwarder %s added. You may add another." % ip) | ||
logcm("DNS forwarder %s added. You may add another.", ip) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -393,7 +395,7 @@ def update_file(filename, orig, subst): | |||
os.chown(filename, st.st_uid, st.st_gid) # reset perms | |||
return 0 | |||
else: | |||
print("File %s doesn't exist." % filename) | |||
logcm("File %s doesn't exist.", filename) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -544,8 +546,8 @@ def update_hosts_file(ip_addresses, host_name, fstore): | |||
for ip_address in ip_addresses: | |||
if record_in_hosts(str(ip_address)): | |||
continue | |||
print("Adding [{address!s} {name}] to your /etc/hosts file".format( | |||
address=ip_address, name=host_name)) | |||
logcm("Adding [%s %s] to your /etc/hosts file", ip_address, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@t-woerner I found a bunch of same type conversion errors that need to be fixed. |
The code is correct and uses the preferred format for logging. The logger does not perform string formatting by itself. The format message and arguments are passed to handlers and then formatted inside the handler's log formatter. |
@t-woerner @tiran my fault (too late after work, you know..) So please ignore it. ;) LGTM |
Uses LOGGING_FORMAT_CMD_OUTPUT for info_cmd, otherwise normal logging | ||
format. For info_cmd record.__dict__["cmd_output"] is set to True. | ||
""" | ||
if record.levelno >= logging.INFO and \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic should not be in the formatter but rather in the handler's formatter configuration. With this approach the debug logs will miss context and time information for logcm
events. Please use a different formatter for log files and stdout logging here:
freeipa/ipapython/ipa_log_manager.py
Lines 166 to 179 in b79ea6a
file_handler.setLevel(logging.DEBUG) | |
file_handler.setFormatter(Formatter(LOGGING_FORMAT_STANDARD_FILE)) | |
root_logger.addHandler(file_handler) | |
level = logging.ERROR | |
if verbose: | |
level = logging.INFO | |
if debug: | |
level = logging.DEBUG | |
console_handler = logging.StreamHandler() | |
console_handler.setLevel(level) | |
console_handler.setFormatter(Formatter(console_format)) | |
root_logger.addHandler(console_handler) |
Lines 450 to 522 in b79ea6a
root_logger = logging.getLogger() | |
# If logging has already been configured somewhere else (like in the | |
# installer), don't add handlers or change levels: | |
if root_logger.handlers or self.env.validate_api: | |
return | |
if self.env.debug: | |
level = logging.DEBUG | |
else: | |
level = logging.INFO | |
root_logger.setLevel(level) | |
for attr in self.env: | |
match = re.match(r'^log_logger_level_' | |
r'(debug|info|warn|warning|error|critical|\d+)$', | |
attr) | |
if not match: | |
continue | |
level = ipa_log_manager.convert_log_level(match.group(1)) | |
value = getattr(self.env, attr) | |
regexps = re.split('\s*,\s*', value) | |
# Add the regexp, it maps to the configured level | |
for regexp in regexps: | |
root_logger.addFilter(ipa_log_manager.Filter(regexp, level)) | |
# Add stderr handler: | |
level = logging.INFO | |
if self.env.debug: | |
level = logging.DEBUG | |
else: | |
if self.env.context == 'cli': | |
if self.env.verbose > 0: | |
level = logging.INFO | |
else: | |
level = logging.WARNING | |
handler = logging.StreamHandler() | |
handler.setLevel(level) | |
handler.setFormatter(ipa_log_manager.Formatter(LOGGING_FORMAT_STDERR)) | |
root_logger.addHandler(handler) | |
# check after logging is set up but before we create files. | |
fse = sys.getfilesystemencoding() | |
if fse.lower() not in {'utf-8', 'utf8'}: | |
raise errors.SystemEncodingError(encoding=fse) | |
# Add file handler: | |
if self.env.mode in ('dummy', 'unit_test'): | |
return # But not if in unit-test mode | |
if self.env.log is None: | |
return | |
log_dir = path.dirname(self.env.log) | |
if not path.isdir(log_dir): | |
try: | |
os.makedirs(log_dir) | |
except OSError: | |
logger.error('Could not create log_dir %r', log_dir) | |
return | |
level = logging.INFO | |
if self.env.debug: | |
level = logging.DEBUG | |
try: | |
handler = logging.FileHandler(self.env.log) | |
except IOError as e: | |
logger.error('Cannot open log file %r: %s', self.env.log, e) | |
return | |
handler.setLevel(level) | |
handler.setFormatter(ipa_log_manager.Formatter(LOGGING_FORMAT_FILE)) | |
root_logger.addHandler(handler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the wrong approach, see flow diagram. The logic should be in the handler configuration. You have to use multiple handlers:
- One stdout handler with a filter for
cmd_output
records, a filter forlevel < WARNING
records, and acmd_output_format
formatter. - One stderr handler with a filter for
cmd_output
records, a filter forlevel >= WARNING
records, and acmd_output_format
formatter. - One stdout handler with a filter for non-
cmd_output
records andconsole_format
formatter.
Handler 3 could be split into two separate handlers. I think it would be a good idea to not print a full traceback in cmd_output_formatter
(see formatException
).
ipapython/ipa_log_manager.py
Outdated
@@ -164,6 +238,7 @@ def standard_logging_setup(filename=None, verbose=False, debug=False, | |||
finally: | |||
os.umask(umask) | |||
file_handler.setLevel(logging.DEBUG) | |||
file_handler.addFilter(InfoCommandFilter()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you add the filter to the file logger instead of the console logger? Isn't the filter suppose to filter which events end up in stdout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the filter to the installation log fil e handler makes sure that the print output is not in the log file (like it has been up to now) and is by default in the normal installer (log) output. This makes sure that an existing application using FreeIPA parts does not need to enable this explicitly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have some kind of misunderstanding. Having the output in the log files is one of the benefits for me. It makes it easier to correlate debug logs with installer console output.
I proposed the cmd_output
flag to mark log entries that also land on stdout (console logger).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is behavior change that needs to be verified also in all the tests.
|
The use of print calls in the installer parts are resulting in the need to redirect stdout using redirect_stdout in ansible-freeipa. redirect_stdout is creating a new scope. The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. Usage: import logging from ipapython.ipa_log_manager import CommandOutput logger = logging.getLogger(__name__) logcm = CommandOutput(logger) def install(): logcm("Installing ...") Design note ----------- The command logger instance is called ``logcm``, because the name has the same length as the ``print`` function. It may not be the prettiest name, but it avoids code reformatting and therefore merge conflicts. At first we considered to add a ``log_cmd`` method to all loggers. The ``logging.setLoggerClass`` is the public function to set a new logger class. To make it work, is has to be called before the first logger objects is instantiated. It's not appropriate to modify the logger early in ``ipalib``, because ``ipalib`` is also designed to get embedded into applications. And monkey-patching the ``logging.Logger`` class is a hack. The log format for CommandOutput is defined in LOGGING_FORMAT_CMD_OUTPUT and is applied with the extended Formatter class for the log level INFO only. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…t calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…alls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore and formatting using .format has been replaced by old style formatting. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…nt calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…rint calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…alls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…int calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…t calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
… calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
…f print calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
… calls The CommandOutput type is a wrapper for a logger instance. It marks log calls to be printed out on stdout using logger.INFO level. The extra argument { "cmd_output": True } is set to make sure that a filter could be used to filter this out for the installer log files. A new import of CommandOutput has been added and a call to create logcm. Additonally the import of print_function has been removed because print is not used anymore. Fixes: https://pagure.io/freeipa/issue/7914 Signed-off-by: Thomas Woerner <twoerner@redhat.com>
ipapython.admintool._setup_logging, which is used in ipapython.admintool.setup_logging is using standard_logging_setup. Therefore it should not be needed to do further changes here. |
6858b59
to
f8aba87
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still a bunch of print() calls in the installer code.
logcm("WARNING: ...")
and print(file=sys.stderr)
should be replaced with logcm(..., level=logging.WARNING)
and a proper formatter/handler combination. The formatter could prepend WARN
.
Uses LOGGING_FORMAT_CMD_OUTPUT for info_cmd, otherwise normal logging | ||
format. For info_cmd record.__dict__["cmd_output"] is set to True. | ||
""" | ||
if record.levelno >= logging.INFO and \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the wrong approach, see flow diagram. The logic should be in the handler configuration. You have to use multiple handlers:
- One stdout handler with a filter for
cmd_output
records, a filter forlevel < WARNING
records, and acmd_output_format
formatter. - One stderr handler with a filter for
cmd_output
records, a filter forlevel >= WARNING
records, and acmd_output_format
formatter. - One stdout handler with a filter for non-
cmd_output
records andconsole_format
formatter.
Handler 3 could be split into two separate handlers. I think it would be a good idea to not print a full traceback in cmd_output_formatter
(see formatException
).
New command logger logcm as a replacement of print calls
The use of print calls in the installer parts are resulting in the need to
redirect stdout using redirect_stdout in ansible-freeipa. redirect_stdout
is creating a new scope.
The CommandOutput type is a wrapper for a logger instance. It marks log
calls to be printed out on stdout using logger.INFO level. The extra
argument { "cmd_output": True } is set to make sure that a filter could
be used to filter this out for the installer log files.
Usage:
Design note
The command logger instance is called
logcm
, because the name has thesame length as the
print
function. It may not be the prettiest name,but it avoids code reformatting and therefore merge conflicts.
At first we considered to add a
log_cmd
method to all loggers.The
logging.setLoggerClass
is the public function to set a new loggerclass. To make it work, is has to be called before the first logger
objects is instantiated. It's not appropriate to modify the logger
early in
ipalib
, becauseipalib
is also designed to get embeddedinto applications. And monkey-patching the
logging.Logger
class isa hack.
The log format for CommandOutput is defined in LOGGING_FORMAT_CMD_OUTPUT
and is applied with the extended Formatter class for the log level INFO
only.