2

I need to save to file same logs as it shows in console so I edit sys.stdout. (Using code from Redirect stdout to a file in Python?)

But the problem shows when i tried to edit text in write function by adding something before it. In result this "[stack]" adds before and after the text variable.

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.file = open("log.txt", "a")
    def flush(self):
        self.terminal.flush()
        self.file.flush()
    def write(self, text):
        self.terminal.write("[stack]" + text)
        self.file.write(text)
        self.flush();

sys.stdout = Logger()

print "Test log"
print "Another test log"

Result:

[stack]Test log[stack]
[stack]Another test log[stack]
Community
  • 1
  • 1
JJaniszewski
  • 25
  • 1
  • 5
  • `print "Test log"` is executed as `write("Test log")` and `write("\n")` - so you get `write("[stack]" + "Test log")` and `write("[stack]" + "\n")`. Try with commas `print "Test", "log"` and you get even more `[stack]` - `[stack]Test[stack] [stack]log[stack]` :) – furas Jan 08 '17 at 14:14

2 Answers2

4

I was scratching my head on this one until I figured I should just run it through a debugger. The reason why this happens is that when you use print, it will try to write both the body of the text as well as the configured end, which by default is a newline.

Each print statement therefore results in two separate calls to Logger.write, one which is (e.g.) Test Log and the second which is \n. This results in the output [stack] Test Log[stack]\n.

Here is a corrected implementation:

import sys


class Logger(object):
    def __init__(self, stream, default_sep=' ', default_end='\n'):
        self.terminal = stream
        self.default_sep = default_sep
        self.default_end = default_end
        self.continuing_same_print = False
        self.file = open("log.txt", "a")

    def flush(self):
        self.terminal.flush()
        self.file.flush()

    def write(self, text):
        if text is self.default_end:
            self.continuing_same_print = False
        elif text is self.default_sep:
            self.continuing_same_print = True

        new_text = text
        if text in {self.default_sep, self.default_end}:
            pass
        elif self.continuing_same_print:
            pass
        else:
            new_text = '[stack]' + new_text

        self.terminal.write(new_text)
        self.file.write(text)
        self.flush()


sys.stdout = Logger(sys.stdout)

print("Test", "log")
print("Another test log")
print()

Output

[stack]Test log
[stack]Another test log

Edit

Updated implementation to support multiple objects being printed within a print statement.

Tagc
  • 8,736
  • 7
  • 61
  • 114
0

If you are using Python3, you can overload builtin print to customize the output. Below code gives you expected output:

import sys
import builtins

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.file = open("log.txt", "a")
    def flush(self):
        self.terminal.flush()
        self.file.flush()
    def write(self, text):
        self.terminal.write(text)
        self.file.write(text)
        self.flush();

def myprint(string):
  builtins.print("%s%s" % ("[stack]", string))

print = myprint

sys.stdout = Logger()
volatilevar
  • 1,626
  • 14
  • 16