2

I want to redirect the print() statements of some arbitrary code to a proper logging functionality.

Unfortunately, the following code works only when print() gets a single argument.

import logging

logging.basicConfig(filename="logname",
                    filemode='a',
                    format='%(asctime)s,%(msecs)03d %(name)s %(levelname)s %(message)s',
                    datefmt='%D %H:%M:%S',
                    level=logging.DEBUG)

print = logging.debug
print("hallo")
print("makes", "error")

This results in the following logfile:

03/30/18 14:06:11,881 root DEBUG hallo

And the following error in the console:

Connected to pydev debugger (build 173.4674.37)
--- Logging error ---
Traceback (most recent call last):
  File "/home/user/anaconda3/lib/python3.6/logging/__init__.py", line 992, in emit
    msg = self.format(record)
  File "/home/user/anaconda3/lib/python3.6/logging/__init__.py", line 838, in format
    return fmt.format(record)
  File "/home/user/anaconda3/lib/python3.6/logging/__init__.py", line 575, in format
    record.message = record.getMessage()
  File "/home/user/anaconda3/lib/python3.6/logging/__init__.py", line 338, in getMessage
    msg = msg % self.args
TypeError: not all arguments converted during string formatting
Call stack:
  File "/home/user/install_dir/pycharm/2017-12-12-ultimate-edition/pycharm-2017.3/helpers/pydev/pydevd.py", line 1668, in <module>
    main()
  File "/home/user/install_dir/pycharm/2017-12-12-ultimate-edition/pycharm-2017.3/helpers/pydev/pydevd.py", line 1662, in main
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/home/user/install_dir/pycharm/2017-12-12-ultimate-edition/pycharm-2017.3/helpers/pydev/pydevd.py", line 1072, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/home/user/install_dir/pycharm/2017-12-12-ultimate-edition/pycharm-2017.3/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/home/user/PycharmProjects/my_project/attic.py", line 12, in <module>
    print("makes", "error")
Message: 'makes'
Arguments: ('error',)

Process finished with exit code 0

How do I have to change the code to make it work for an arbitrary number of arguments of print()?

Taku
  • 31,927
  • 11
  • 74
  • 85

1 Answers1

1

print is no longer itself anymore because you redefined print to be logging.debug, debug only takes the first argument as the message. However, you can define print differently, as followed:

def print(*args, sep=" ", **kwargs):
    return logging.debug(sep.join(map(lambda x: str(x), args)), **kwargs)

This loses the ability to use debug's *args to format strings.

args are the arguments which are merged into msg using the string formatting operator. (Note that this means that you can use keywords in the format string, together with a single dictionary argument.)


Instead of redefining print, I'd advise you use a StreamHandler.

Taku
  • 31,927
  • 11
  • 74
  • 85
  • Thanks, that was already very helpful. Now I reached the next issue: with your solution, a `dict` cannot be processed. Do you have any idea how to solve this? –  Mar 30 '18 at 14:06
  • Oops, I forgot something important, I believe I fixed it by joining the string representation of all the arguments(this is what the original `print` does), you can try again. – Taku Mar 30 '18 at 14:12