1

I am trying to rewrite this batch line in Python:

mkdir %TEMP%\FW >> %LOGDETAILS% 2>&1

When using subprocess.call(), how to set it in way that redirects stdout to a log file if you are using the dictionary configuration for logging?

My Python code looks like this so far (this is just part of it):

    DICT_CONFIG = {#I have my settings in here}
    logging.config.dictConfig(DICT_CONFIG)
    logdetails = logging.getLogger('SikuliScriptDetails_Logger')

    def my_function():
        logdetails.info("---------Benginning Tests--------------")
        #Set Project Name, Disable Feedback Dialogs by setting launches to 41
        returncode = subprocess.call(["regedit", "-s", "MainFolder/FwSetup.reg"], stderr = STDOUT, stdout = logdetails)

I can not test my program for quite a while until I have some other modules ready. But is my attempt at redirection correct? Is this going to be able to log the output of the ["regedit", "-s", "MainFolder/FwSetup.reg"] into my logdetails file logger?

Is it necessary for me to do stderr = STDOUT first like I did before sending stdout to logdetails file logger?

Also, does anyone know what the 41 means? I do not know what to do with it.

PS: I've looked at this already, but I still don't get it since they are not using dictionary configuration for logging.

UPDATE: PS: I've also looked at this information to understand the batch line of code.

UPDATE2: Sorry, I gave you the wrong line of code at the beginning. The batch file I was supposed to give paste here is:

"regedit", "-s", "VBoxScripts/FwSetup.reg"

not this one: mkdir %TEMP%\FW >> %LOGDETAILS% 2>&1 Thanks.

Community
  • 1
  • 1
Alain
  • 157
  • 1
  • 3
  • 14
  • This is a very confusing question! You say you want to do 'mkdir' in python (the answer is to use os.mkdir or perhaps os.makedirs) but then you call regedit. Do you want to use the logging module or just redirect the output to a file? – tdelaney Aug 05 '13 at 20:25
  • Why are you using the `logging` module in the first place, if all you're trying to do is redirect output to a file? – abarnert Aug 05 '13 at 20:29
  • I want to record/log all that the child process generates. – Alain Aug 05 '13 at 21:05
  • @tdelaney I want to use the logging module. – Alain Aug 05 '13 at 21:23
  • @Alain: Well, the `logging` module is not going to do the same thing as your batch example. Using `logging` will add standard log headers to each log message; your batch example just appends the output (and error) unmodified to a text file. So, a question that says "I'm trying to rewrite this batch file" and then shows you trying to use `logging` is bound to confuse people. You have to explain what you actually want to do, or nobody can tell you how to do it. – abarnert Aug 05 '13 at 21:59
  • @abarnert: I totally understand your point. You actually just made me realize what exactly that batch line is meant to achieve. I supposed that is what the tdelaney's answer [below](http://stackoverflow.com/a/18067154/2540349) (first sample code) suggests. So, thanks again. – Alain Aug 05 '13 at 22:15
  • @Alain: Yes, tdelaney's first answer and my first answer both just append to a file (although you really should use a `with` statement rather than leaking the file handle); our second answers both show ways of logging each line from as a log message. – abarnert Aug 05 '13 at 22:19
  • Does anyone know what the **41** means in the batch line: `#REM Set Project Name, Disable Feedback Dialogs by setting launches to 41`? – Alain Aug 05 '13 at 22:33

2 Answers2

0

Your question is a bit confused.


First, there's the logging bit. It doesn't matter which mechanism you use for configuring logging; in the end, you still end up with a Logger object with the same methods. What you're doing seems reasonable, although it is a bit weird to use 'SikuliScriptDetails_Logger' as a name for a logger.


Next, there's this:

Is it necessary for me to do stderr = STDOUT first like I did before sending stdout to logdetails file logger?

Setting stderr=STDOUT means, as the docs explain:

… that the stderr data from the child process should be captured into the same file handle as for stdout.

In other words, if you want to log stdout and stderr together, then yes, you do need to do this; if you want to log just stdout, or just stderr, or to log them each separately, then no, you should not do this.

From your batch line, it sounds like you do want to mix stdout and stderr together, in which case you're doing the right thing.


Finally, there's what you pass for stdout. From the same documentation:

Valid values are PIPE, DEVNULL, an existing file descriptor (a positive integer), an existing file object, and None.

In particular, you cannot give it a Logger object; you have to give it a file object or PIPE.

If you just want it to append stdout and stderr to the same file the logger is using, you can get the logger's file object and pass that. However, a much simpler way to do it would be to not use logging in the first place—just open a file, write the header to that, and pass it as the stdout parameter. For example:

with open(log_details_path, 'a') as logfile:
    logfile.write("---------Benginning Tests--------------\n")
    returncode = subprocess.call(["regedit", "-s", "MainFolder/FwSetup.reg"], stdout=logfile, stderr=STDOUT)

But, given that you're using logging in the first place, it sounds like what you really want is something like this: read each line of stderr, and log it as a separate log message. If so, you need to use a PIPE. If you need to stream it continuously to the log (e.g., it's going to take a long time, send a lot of data, possibly fail in the middle…), you'll need to explicitly read from the PIPE and then log what you get. But if not, you can just use communicate, or even check_output. For example:

child_output = subprocess.check_output(["regedit", "-s", "MainFolder/FwSetup.reg"], stderr = STDOUT)
for line in child_output.splitlines():
    logdetails.info(line)
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • Your answer is the closest to what I am intending to do. Thank you for the clear explanation you provided me with. It is the last part of your answer that seems like what I need to implement. But why do I need to add the `returncode = subprocess.call(["regedit", "-s", "MainFolder/FwSetup.reg"], stderr = STDOUT, stdout=logfile)` again at the end? – Alain Aug 05 '13 at 21:20
  • Great! I will make sure I come here if the other [piece of code](http://stackoverflow.com/a/18067154/2540349) doesn't do it. Thanks. – Alain Aug 05 '13 at 22:25
0

Its easy to redirect output to a file, harder to do it through the logging module. Assuming that all you really want is the python equivalent of piping stdout and stderr to a file, you can do:

log_details = "SikuliScriptDetails.log"
returncode = subprocess.call(
    ["regedit", "-s", "MainFolder/FwSetup.reg"],
    stdout=open(log_details, 'a'), 
    stderr=subprocess.STDOUT)

You can use a with clause if you worry about the file closing:

log_details = "SikuliScriptDetails.log"
with open(log_details, 'a') as log_fp:
    returncode = subprocess.call(
        ["regedit", "-s", "MainFolder/FwSetup.reg"],
        stdout=log_fp, 
        stderr=subprocess.STDOUT)

If you want to log stdout as info and stderr as warn, you can use threads:

import threading
import subprocess

def log_thread(pipe, logger):
    for line in pipe:
        logger(line.strip())

def my_call(logger, cmd, **kw):
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        **kw)
    stdout = threading.Thread(target=log_thread, args=(proc.stdout, logger.info))
    stdout.start()
    stderr = threading.Thread(target=log_thread, args=(proc.stderr, logger.warn))
    stderr.start()
    stdout.join()
    stderr.join()
    return proc.wait()
tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • This is quite handy. I wish I had seen such instruction as I started my current project. So, thank you for sharing. Nonetheless, for this very step of the project, I will be making plenty of `subprocess.call()` (or `subprocess.Popen()` when necessary) and wish to be able to do with each one of them what the following batch code does with the `LOGDETAILS = V\SikuliScriptDetails.log` file: `regedit -s VBoxScripts\FwSetup.reg >> %LOGDETAILS% 2>&1` – Alain Aug 05 '13 at 21:11
  • @Alain, I changed the first example to append to the log file. It now does the same thing as your shell example. Your log file name isn't quite correct (those slashes at the front) so I just used one in the local directory. Change it to what you want. – tdelaney Aug 05 '13 at 21:26
  • @Alain, if you want to use the second sample, you need to setup your log handlers separately. This just showed how to get the output into the logger. – tdelaney Aug 05 '13 at 21:28
  • I will be glad to comfirm as soon as I get the first sample to work. It looks easier to manage when making multiple `subprocess.call()`'s. So thank you. I will go ahead and vote the answer as the most promising. – Alain Aug 05 '13 at 22:03
  • In the first example, Do I need to close() log_details? Do I need to close it before I append any more stuff to it from another subprocess.call()? – Alain Aug 06 '13 at 16:51
  • @Alain - if you use cpython (the standard python) the file reference count goes to zero and the file is closed automatically. This is a great feature of python but it has deprecation warnings and may not work in python emulators on Java or C# that delay object destruction. You could open the file in a 'with' clause to be safe. – tdelaney Aug 06 '13 at 17:02
  • Thanks! I ended up using a 'with' clause because, in fact, if automatically closes the file for me. – Alain Aug 06 '13 at 18:50