Sys.stdout bir günlük dosyasına nasıl kopyalanır?


151

Düzenleme: Görünüşe göre ya bir çözüm yok ya da hiç kimsenin bilmediği standart dışı bir şey yapıyorum - sorumu şu soruyu da gözden geçireceğim: Bir python uygulaması bir çok sayıda sistem çağrısı?

Uygulamamın iki modu var. Etkileşimli modda, tüm çıktıların ekrana ve sistem çağrılarının çıktıları dahil bir günlük dosyasına gitmesini istiyorum. Arka plan programı modunda, tüm çıktılar günlüğe gider. Daemon modu kullanımıyla harika çalışıyor os.dup2(). Her bir sistem çağrısını değiştirmeden, tüm çıktıları etkileşimli modda bir günlüğe "göndermenin" bir yolunu bulamıyorum.


Başka bir deyişle, bir python uygulaması tarafından oluşturulan herhangi bir çıktı için sistem çağrısı çıktısı dahil 'tee' komut satırı işlevselliğini istiyorum .

Netleştirmek için:

Tüm çıktıları yeniden yönlendirmek için böyle bir şey yapıyorum ve harika çalışıyor:

# open our log file
so = se = open("%s.log" % self.name, 'w', 0)

# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

Bununla ilgili güzel olan şey, kodun geri kalanından özel bir yazdırma çağrısı gerektirmemesidir. Kod ayrıca bazı kabuk komutlarını da çalıştırır, bu nedenle çıktılarının her birini ayrı ayrı ele almak zorunda kalmamak güzel.

Basitçe, yeniden yönlendirme yerine çoğaltma dışında aynısını yapmak istiyorum .

İlk düşündüğümde, dup2' s'leri tersine çevirmenin işe yaraması gerektiğini düşündüm . Neden olmuyor? İşte benim testim:

import os, sys

### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###

print("foo bar")

os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

"A.log" dosyası, ekranda görüntülenen ile aynı olmalıdır.


Man sayfasına ( manpagez.com/man/2/dup2 ) bakarsanız, dup2 için 2. argüman her zaman kapalıdır (zaten açıksa). Yani sizin "bozuk çözümünüzde" bu kadar kapanıyor ve se ve sonra da dosyalarının sys.stdout'a yeniden atanması.
Jacob Gabrielson

1
Re: Düzenlemeniz: Bu alışılmadık bir durum değil, benzerini birkaç kez yaptım (diğer dillerde). Unix, aynı dosya tanıtıcısı için birden çok "takma ada" izin verirken, bir dosya tanıtıcısını "bölmez" (birden çok kişiye kopyalar). Yani "tee" yi kendiniz uygulamanız gerekir (veya sadece "tee" kullanın, ham cevabıma bakın).
Jacob Gabrielson

Sanırım JohnT cevabı, kabul edilen gerçek cevaptan daha iyi. Kabul edilen cevabı değiştirmek isteyebilirsiniz.
Phong

"Çok standart olmayan bir şey yapıyorum" - gerçekten öylesiniz, insanlar sadece günlüklerini stderr'e gönderiyor ve komut satırından ilgileniyor.
khachik

Yanıtlar:


55

Kodunuzdan harici süreçler üretmekte rahat olduğunuz için, teekendini kullanabilirsiniz . Tam olarak ne yaptığını yapan herhangi bir Unix sistem çağrısı bilmiyorum tee.

# Note this version was written circa Python 2.6, see below for
# an updated 3.3+-compatible version.
import subprocess, os, sys

# Unbuffer output (this ensures the output is in the correct order)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

print "\nstdout"
print >>sys.stderr, "stderr"
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

Çoklu işlem paketini teekullanarak da taklit edebilirsiniz (veya Python 2.5 veya önceki bir sürümünü kullanıyorsanız işlemeyi kullanabilirsiniz ).

Güncelleme

İşte Python 3.3 + ile uyumlu bir sürüm:

import subprocess, os, sys

tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE)
# Cause tee's stdin to get a copy of our stdin/stdout (as well as that
# of any child processes we spawn)
os.dup2(tee.stdin.fileno(), sys.stdout.fileno())
os.dup2(tee.stdin.fileno(), sys.stderr.fileno())

# The flush flag is needed to guarantee these lines are written before
# the two spawned /bin/ls processes emit any output
print("\nstdout", flush=True)
print("stderr", file=sys.stderr, flush=True)

# These child processes' stdin/stdout are 
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

29
Bu cevap işe yarıyor, bu yüzden kabul edeceğim. Yine de kendimi kirli hissettiriyor .
kur

2
Herhangi bir platformda çalışabilen ve ayrıca farklı günlükleme yapılandırmalarında kullanılabilen saf bir python uygulaması (py2 / 3 uyumlu) yayınladım. stackoverflow.com/questions/616645/…
sorin

8
Python makinelerimden birinde çalışıyorsa ve çözüm çalışmıyorsa, o zaman bu pitonik bir çözüm değildir. Bu yüzden olumsuz oy verildi.
anatoly techtonik

2
Bu gönderiye göre , satır sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)artık python 3.3'ten beri çalışmıyor (bkz. PEP 3116)
Ken Myers

2
İyi bir şey, şunu eklemeliyim: (... , preexec_fn=lambda: signal.signal(signal.SIGINT, signal.SIG_IGN) Kullanıcı "Ctrl + C" ye bastığında, önce "tee" işlemi öldürülüyor, stdout \ stderr FD'leri kapatıyor, ardından ana iş parçacığında çıktı yok. Bu sorunu çözer :)
Aviad

136

Aynı sorunu daha önce yaşadım ve bu pasajı çok yararlı buldum:

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()
    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()

from: http://mail.python.org/pipermail/python-list/2007-May/438106.html


7
Tee nesnesini silerek günlüğe kaydetmeyi sonlandırabilmeniz için sys.stdout yeniden atamasını dahili olarak işlemek için +1
Ben Blank

12
Buna bir floş eklerdim. Örneğin: 'self.file.flush ()'
Luke Stanley

4
Loglama modülü konusunda katılmıyorum. Biraz uğraşmak için mükemmel. Günlük kaydı bunun için çok büyük.
Kobor42

4
Bu takipteki revize edilmiş versiyonu cevaptaki bağlantılı tartışmaya not ettiğinizden emin olun .
martineau

4
Bu işe yaramayacak. __del__infazın sonuna kadar çağrılmaz. Stackoverflow.com/questions/6104535/… sayfasına
Nux

79

printDeyim arayacak write()size sys.stdout atadığınız herhangi bir nesnenin yöntemini.

Aynı anda iki yere yazmak için küçük bir sınıfı döndürürdüm ...

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("log.dat", "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)  

sys.stdout = Logger()

Şimdi printifade hem ekrana yankılanacak hem de günlük dosyanıza eklenecektir:

# prints "1 2" to <stdout> AND log.dat
print "%d %d" % (1,2)

Bu açıkça hızlı ve kirli. Bazı notlar:

  • Muhtemelen günlük dosya adını parametrelemelisiniz.
  • Muhtemelen sys.stdout'a geri dönmelisiniz <stdout>Program süresince oturum açmayacaksanız .
  • Aynı anda birden çok günlük dosyasına yazma veya farklı günlük düzeylerini kullanma vb.

Bunların hepsi yeterince açık ve onları okuyucu için egzersiz olarak bırakırken rahatım. Buradaki temel fikir, printatanmış "dosya benzeri bir nesneyi" çağırmasıdır sys.stdout.


Tam olarak ne yazacaktım, hemen hemen. Kendi kendine tartışmadan yazarak sorunu çözdüğünüzde +1. Ayrıca, yazacağınız dosyanın aktarılması için daha iyi bir tasarım olacaktır. Cehennem,
standart çıkışın geçilmesi

@Devin, evet bu hızlı ve kirliydi, olası erken iyileştirmeler için bazı notlar alacağım.
Triptych

7
Bu yanıtı çok erken seçtim. "Yazdır" için harika çalışıyor, ancak harici komut çıktısı için çok değil.
nöbet

2
Logger sınıfı ayrıca "def flush (): self.terminal.flush (); self.log.flush ()" gibi bir flush () yöntemini tanımlamalıdır
blokeley

5
Diyorsun The print statement will call the write() method of any object you assign to sys.stdout. Peki ya stdout'a veri gönderen diğer işlevler kullanmıyor print? Örneğin subprocess.call, çıktısını kullanarak bir işlem oluşturursam konsola gider ancak log.datdosyalamaya gitmezsem ... bunu düzeltmenin bir yolu var mı?
jpo38

64

Gerçekten istediğiniz, loggingstandart kitaplıktaki modüldür. Bir kaydedici oluşturun ve iki işleyici ekleyin, biri bir dosyaya, diğeri stdout veya stderr'e yazacaktır.

Bkz birden fazla hedefe Günlüğü detayları için


9
Günlük kaydı modülü istisnaları ve diğer önemli çıktıları standart çıktıya kaydetmez; bu, derleme sunucusundaki günlükleri analiz ederken yararlı olabilir (örneğin).
anatoly techtonik

2
loggingmodülos.write(1, b'stdout')
jfs

17

İşte diğerlerinden daha genel olan başka bir çözüm - çıktının sys.stdoutherhangi bir sayıda dosya benzeri nesneye bölünmesini (yazılan ) destekler . __stdout__Kendisinin dahil edilmesi şartı yoktur .

import sys

class multifile(object):
    def __init__(self, files):
        self._files = files
    def __getattr__(self, attr, *args):
        return self._wrap(attr, *args)
    def _wrap(self, attr, *args):
        def g(*a, **kw):
            for f in self._files:
                res = getattr(f, attr, *args)(*a, **kw)
            return res
        return g

# for a tee-like behavior, use like this:
sys.stdout = multifile([ sys.stdout, open('myfile.txt', 'w') ])

# all these forms work:
print 'abc'
print >>sys.stdout, 'line2'
sys.stdout.write('line3\n')

NOT: Bu bir kavram kanıtıdır. Buradaki uygulama, yalnızca dosya benzeri nesnelerin yöntemlerini (örneğin write), üyeleri / özellikleri / setattr'ı vb. Dışarıda bıraktığından tam değildir. Ancak, şu anda olduğu gibi çoğu insan için muhtemelen yeterince iyidir.

Bu konuda ne gibi onun genelliği dışında, bu anlamda herhangi bir doğrudan görüşmeleri için yapmaz temiz olmasıdır write, flush, os.dup2vb


3
İnit'in * dosyaları değil * dosyalarını almasını isterdim , aksi takdirde evet, bu. Diğer çözümlerin hiçbiri, diğer sorunları çözmeye çalışmadan "tee" işlevselliğini izole etmez. Çıktısını aldığınız her şeye bir önek koymak istiyorsanız, bu sınıfı bir önek-yazar sınıfına sarabilirsiniz. (Tek bir akışa bir önek koymak istiyorsanız, bir akışı sarar ve bu sınıfa verirsiniz.) Bu aynı zamanda çok dosyalı ([]) her şeyi yok sayan bir dosya oluşturma avantajına da sahiptir (open ('/ dev /boş')).
Ben

Neden _wrapburadayım? Oradaki kodu kopyalayamaz __getattr__mısın ve aynı şekilde çalışıyor mu?
timotree

@Ben aslında yöntemlerinden birini her çağırdığınızda multifile([])ortaya çıkan bir dosya oluşturur UnboundLocalError. ( resatanmadan iade edilir)
timotree

Çok yönlü olduğu için sys.stderr de ekleyebilirsiniz. Windows'ta da çalışıyor. Tek dezavantaj: Bir istisna olarak, print Traceback python 2.x'te kaybolur ve günlüğe kaydedilemez.
Eric H.

13

Başka bir yerde açıklandığı gibi, belki de en iyi çözüm, günlük kaydı modülünü doğrudan kullanmaktır:

import logging

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')
logging.info('this should to write to the log file')

Bununla birlikte, gerçekten istediğiniz bazı (nadir) durumlar vardır. stdout'u yeniden yönlendirmek . Django'nun print kullanan runserver komutunu genişletirken böyle bir durum yaşadım: django kaynağını kesmek istemedim, ancak bir dosyaya gitmek için print ifadelerine ihtiyacım vardı.

Bu, günlükleme modülünü kullanarak stdout ve stderr'i kabuktan uzağa yeniden yönlendirmenin bir yoludur:

import logging, sys

class LogFile(object):
    """File-like object to log text using the `logging` module."""

    def __init__(self, name=None):
        self.logger = logging.getLogger(name)

    def write(self, msg, level=logging.INFO):
        self.logger.log(level, msg)

    def flush(self):
        for handler in self.logger.handlers:
            handler.flush()

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')

# Redirect stdout and stderr
sys.stdout = LogFile('stdout')
sys.stderr = LogFile('stderr')

print 'this should to write to the log file'

Bu LogFile uygulamasını yalnızca günlükleme modülünü gerçekten doğrudan kullanamıyorsanız kullanmalısınız.


11

tee()Python'da çoğu durumda çalışması gereken bir uygulama yazdım ve Windows'ta da çalışıyor.

https://github.com/pycontribs/tendo

Ayrıca isterseniz loggingPython'dan modül ile birlikte kullanabilirsiniz .


Hmm - bu bağlantı artık çalışmıyor - başka herhangi bir yerde bulunabilir mi?
Danny Staple

1
vay canına, paketiniz harika, özellikle Windows konsolu kültürünün ne kadar hantal olduğunu biliyorsanız, ancak çalışmasını sağlamaktan vazgeçmediyseniz!
n611x007

10

Bu sorunun tekrar tekrar cevaplandığını biliyorum, ancak bunun için John T'nin cevabından ana cevabı aldım ve önerilen yıkamayı içerecek şekilde değiştirdim ve bağlantılı revize edilmiş versiyonunu takip ettim. Ayrıca cladmi'nin with ifadesiyle kullanım için yanıtında belirtildiği gibi giriş ve çıkışı da ekledim . Ek olarak, dokümantasyon dosyaları kullanarak yıkamaktan bahsediyor, os.fsync()bu yüzden onu da ekledim. Buna gerçekten ihtiyacın var mı bilmiyorum ama orada.

import sys, os

class Logger(object):
    "Lumberjack class - duplicates sys.stdout to a log file and it's okay"
    #source: https://stackoverflow.com/q/616645
    def __init__(self, filename="Red.Wood", mode="a", buff=0):
        self.stdout = sys.stdout
        self.file = open(filename, mode, buff)
        sys.stdout = self

    def __del__(self):
        self.close()

    def __enter__(self):
        pass

    def __exit__(self, *args):
        self.close()

    def write(self, message):
        self.stdout.write(message)
        self.file.write(message)

    def flush(self):
        self.stdout.flush()
        self.file.flush()
        os.fsync(self.file.fileno())

    def close(self):
        if self.stdout != None:
            sys.stdout = self.stdout
            self.stdout = None

        if self.file != None:
            self.file.close()
            self.file = None

Daha sonra kullanabilirsin

with Logger('My_best_girlie_by_my.side'):
    print("we'd sing sing sing")

veya

Log=Logger('Sleeps_all.night')
print('works all day')
Log.close()

Birçok Thnaks @ Status sorumu çözdünüz ( stackoverflow.com/questions/39143417/… ). Çözümünüze bir bağlantı koyacağım.
Mohammad ElNesr

1
@MohammadElNesr Bir with ifadesiyle kullanıldığında kodla ilgili bir sorun olduğunu fark ettim. Bunu düzelttim ve şimdi bir bloğun sonunda doğru şekilde kapanıyor.
Durum

3
Bu benim için harika çalıştı, yalnızca modu işlev mode="ab"olarak ve writeişlevde değiştirmek gerekiyorduself.file.write(message.encode("utf-8"))
ennetws

8

(Ah, sorunuzu tekrar okuyun ve bunun pek de geçerli olmadığını görün.)

İşte python günlükleme modülünü kullanan örnek bir program . Bu kayıt modülü, 2.3'ten beri tüm sürümlerde bulunmaktadır. Bu örnekte günlük kaydı, komut satırı seçenekleriyle yapılandırılabilir.

Sessiz modda yalnızca bir dosyaya, normal modda hem bir dosyaya hem de konsola günlüğe kaydedilir.

import os
import sys
import logging
from optparse import OptionParser

def initialize_logging(options):
    """ Log information based upon users options"""

    logger = logging.getLogger('project')
    formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
    level = logging.__dict__.get(options.loglevel.upper(),logging.DEBUG)
    logger.setLevel(level)

    # Output logging information to screen
    if not options.quiet:
        hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)

    # Output logging information to file
    logfile = os.path.join(options.logdir, "project.log")
    if options.clean and os.path.isfile(logfile):
        os.remove(logfile)
    hdlr2 = logging.FileHandler(logfile)
    hdlr2.setFormatter(formatter)
    logger.addHandler(hdlr2)

    return logger

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    # Setup command line options
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("-l", "--logdir", dest="logdir", default=".", help="log DIRECTORY (default ./)")
    parser.add_option("-v", "--loglevel", dest="loglevel", default="debug", help="logging level (debug, info, error)")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="do not log to console")
    parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="remove old log file")

    # Process command line options
    (options, args) = parser.parse_args(argv)

    # Setup logger format and output locations
    logger = initialize_logging(options)

    # Examples
    logger.error("This is an error message.")
    logger.info("This is an info message.")
    logger.debug("This is a debug message.")

if __name__ == "__main__":
    sys.exit(main())

İyi cevap. Konsola günlük kaydını kopyalamanın gerçekten karmaşık yollarını gördüm, ancak stderr ile bir StreamHandler yapmak aradığım
cevaptı

Kod güzel, soruyu cevaplamıyor - bu, günlüğü bir dosyaya ve stderr'e çıkarıyor, asıl soru stderr'i bir günlük dosyasına kopyalamak istiyordu.
emem

8

John T yanıtını tamamlamak için: https://stackoverflow.com/a/616686/395687

Bu kodu veren anahtar kelimeyle bir bağlam yöneticisi olarak kullanmak için ekledim __enter__ve __exit__yöntemlerwith

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self

    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()

    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)

    def __enter__(self):
        pass

    def __exit__(self, _type, _value, _traceback):
        pass

Daha sonra şu şekilde kullanılabilir

with Tee('outfile.log', 'w'):
    print('I am written to both stdout and outfile.log')

1
Ben hareket edeceğini __del__içine işlevselliğini__exit__
vontrapp

1
Doğrusu, kullanmanın __del__kötü bir fikir olduğunu düşünüyorum . Çağrılan bir 'kapat' işlevine taşınmalıdır __exit__.
cladmi

4

günlükleme modülünü kullanan başka bir çözüm:

import logging
import sys

log = logging.getLogger('stdxxx')

class StreamLogger(object):

    def __init__(self, stream, prefix=''):
        self.stream = stream
        self.prefix = prefix
        self.data = ''

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()

        self.data += data
        tmp = str(self.data)
        if '\x0a' in tmp or '\x0d' in tmp:
            tmp = tmp.rstrip('\x0a\x0d')
            log.info('%s%s' % (self.prefix, tmp))
            self.data = ''


logging.basicConfig(level=logging.INFO,
                    filename='text.log',
                    filemode='a')

sys.stdout = StreamLogger(sys.stdout, '[stdout] ')

print 'test for stdout'

3

Yukarıdaki cevaplardan hiçbiri, ortaya konulan soruna gerçekten cevap vermiyor gibi görünüyor. Bunun eski bir konu olduğunu biliyorum, ancak bu sorunun herkesin yaptığından çok daha basit olduğunu düşünüyorum:

class tee_err(object):

 def __init__(self):
    self.errout = sys.stderr

    sys.stderr = self

    self.log = 'logfile.log'
    log = open(self.log,'w')
    log.close()

 def write(self, line):

    log = open(self.log,'a')
    log.write(line)
    log.close()   

    self.errout.write(line)

Şimdi bu her şeyi normal sys.stderr işleyicisine ve dosyanıza tekrarlayacaktır. İçin başka bir sınıf tee_outoluşturun sys.stdout.


2
Benzer, daha iyi bir cevap bundan iki yıl önce yayınlandı: stackoverflow.com/a/616686 . Yönteminiz çok pahalıdır: Her çağrı tee=tee_err();tee.write('');tee.write('');...açılır + her biri için bir dosya kapatır write. Bu uygulamaya karşı argümanlar için stackoverflow.com/q/4867468 ve stackoverflow.com/q/164053 adreslerine bakın .
Rob W

3

@ John T'nin cevabı altındaki yorumlarda @ user5359531 tarafından yapılan bir talebe göre , işte bu cevaptaki bağlantılı tartışmanın revize edilmiş versiyonuna atıfta bulunulan gönderinin bir kopyası:

Issue of redirecting the stdout to both file and screen
Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon May 28 12:45:51 CEST 2007

    Previous message: Issue of redirecting the stdout to both file and screen
    Next message: Formal interfaces with Python
    Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

En Mon, 28 May 2007 06:17:39 -0300, 人言落日是天涯,望极天涯不见家
<kelvin.you at gmail.com> escribió:

> I wanna print the log to both the screen and file, so I simulatered a
> 'tee'
>
> class Tee(file):
>
>     def __init__(self, name, mode):
>         file.__init__(self, name, mode)
>         self.stdout = sys.stdout
>         sys.stdout = self
>
>     def __del__(self):
>         sys.stdout = self.stdout
>         self.close()
>
>     def write(self, data):
>         file.write(self, data)
>         self.stdout.write(data)
>
> Tee('logfile', 'w')
> print >>sys.stdout, 'abcdefg'
>
> I found that it only output to the file, nothing to screen. Why?
> It seems the 'write' function was not called when I *print* something.

You create a Tee instance and it is immediately garbage collected. I'd
restore sys.stdout on Tee.close, not __del__ (you forgot to call the
inherited __del__ method, btw).
Mmm, doesn't work. I think there is an optimization somewhere: if it looks
like a real file object, it uses the original file write method, not yours.
The trick would be to use an object that does NOT inherit from file:

import sys
class TeeNoFile(object):
     def __init__(self, name, mode):
         self.file = open(name, mode)
         self.stdout = sys.stdout
         sys.stdout = self
     def close(self):
         if self.stdout is not None:
             sys.stdout = self.stdout
             self.stdout = None
         if self.file is not None:
             self.file.close()
             self.file = None
     def write(self, data):
         self.file.write(data)
         self.stdout.write(data)
     def flush(self):
         self.file.flush()
         self.stdout.flush()
     def __del__(self):
         self.close()

tee=TeeNoFile('logfile', 'w')
print 'abcdefg'
print 'another line'
tee.close()
print 'screen only'
del tee # should do nothing

--
Gabriel Genellina

1

Cmd-line komut dosyalarını çalıştırmak için bir komut dosyası yazıyorum. (Çünkü bazı durumlarda, rsync örneğinde olduğu gibi, bir Linux komutunun yerine geçebilir bir alternatif yoktur.)

Gerçekten istediğim şey, bunu yapmanın mümkün olduğu her durumda varsayılan python günlüğe kaydetme mekanizmasını kullanmak, ancak beklenmedik bir şeyler ters gittiğinde yine de herhangi bir hatayı yakalamaktı.

Bu kod hile yapıyor gibi görünüyor. Özellikle zarif veya verimli olmayabilir (string + = string kullanmasa da, en azından o özel potansiyel dar boğazına sahip değildir). Başka birine yararlı fikirler verirse diye gönderiyorum.

import logging
import os, sys
import datetime

# Get name of module, use as application name
try:
  ME=os.path.split(__file__)[-1].split('.')[0]
except:
  ME='pyExec_'

LOG_IDENTIFIER="uuu___( o O )___uuu "
LOG_IDR_LENGTH=len(LOG_IDENTIFIER)

class PyExec(object):

  # Use this to capture all possible error / output to log
  class SuperTee(object):
      # Original reference: http://mail.python.org/pipermail/python-list/2007-May/442737.html
      def __init__(self, name, mode):
          self.fl = open(name, mode)
          self.fl.write('\n')
          self.stdout = sys.stdout
          self.stdout.write('\n')
          self.stderr = sys.stderr

          sys.stdout = self
          sys.stderr = self

      def __del__(self):
          self.fl.write('\n')
          self.fl.flush()
          sys.stderr = self.stderr
          sys.stdout = self.stdout
          self.fl.close()

      def write(self, data):
          # If the data to write includes the log identifier prefix, then it is already formatted
          if data[0:LOG_IDR_LENGTH]==LOG_IDENTIFIER:
            self.fl.write("%s\n" % data[LOG_IDR_LENGTH:])
            self.stdout.write(data[LOG_IDR_LENGTH:])

          # Otherwise, we can give it a timestamp
          else:

            timestamp=str(datetime.datetime.now())
            if 'Traceback' == data[0:9]:
              data='%s: %s' % (timestamp, data)
              self.fl.write(data)
            else:
              self.fl.write(data)

            self.stdout.write(data)


  def __init__(self, aName, aCmd, logFileName='', outFileName=''):

    # Using name for 'logger' (context?), which is separate from the module or the function
    baseFormatter=logging.Formatter("%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")
    errorFormatter=logging.Formatter(LOG_IDENTIFIER + "%(asctime)s \t %(levelname)s \t %(name)s:%(module)s:%(lineno)d \t %(message)s")

    if logFileName:
      # open passed filename as append
      fl=logging.FileHandler("%s.log" % aName)
    else:
      # otherwise, use log filename as a one-time use file
      fl=logging.FileHandler("%s.log" % aName, 'w')

    fl.setLevel(logging.DEBUG)
    fl.setFormatter(baseFormatter)

    # This will capture stdout and CRITICAL and beyond errors

    if outFileName:
      teeFile=PyExec.SuperTee("%s_out.log" % aName)
    else:
      teeFile=PyExec.SuperTee("%s_out.log" % aName, 'w')

    fl_out=logging.StreamHandler( teeFile )
    fl_out.setLevel(logging.CRITICAL)
    fl_out.setFormatter(errorFormatter)

    # Set up logging
    self.log=logging.getLogger('pyExec_main')
    log=self.log

    log.addHandler(fl)
    log.addHandler(fl_out)

    print "Test print statement."

    log.setLevel(logging.DEBUG)

    log.info("Starting %s", ME)
    log.critical("Critical.")

    # Caught exception
    try:
      raise Exception('Exception test.')
    except Exception,e:
      log.exception(str(e))

    # Uncaught exception
    a=2/0


PyExec('test_pyExec',None)

Açıkçası, benim kadar kaprisli değilseniz, LOG_IDENTIFIER'ı birisinin bir günlüğe yazmasını görmek istemediğiniz başka bir dizeyle değiştirin.


0

Tüm çıktıları bir dosyaya kaydetmek VE bunu bir metin dosyasına çıkarmak isterseniz, aşağıdakileri yapabilirsiniz. Biraz karmaşık ama işe yarıyor:

import logging
debug = input("Debug or not")
if debug == "1":
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string):
        old_print(string)
        logging.info(string)
print("OMG it works!")

DÜZENLEME: sys.stderr'i sys.stdout'a yönlendirmediğiniz sürece bunun hataları günlüğe kaydetmediğini unutmayın.

DÜZENLEME2: İkinci bir sorun, yerleşik işlevden farklı olarak 1 bağımsız değişken geçirmeniz gerektiğidir.

DÜZENLEME3: stdin ve stdout'u konsola yazmadan önce kodu görün ve stderr yalnızca dosyaya gidecek şekilde dosya

import logging, sys
debug = input("Debug or not")
if debug == "1":
    old_input = input
    sys.stderr.write = logging.info
    def input(string=""):
        string_in = old_input(string)
        logging.info("STRING IN " + string_in)
        return string_in
    logging.basicConfig(level=logging.DEBUG, filename='./OUT.txt')
    old_print = print
    def print(string="", string2=""):
        old_print(string, string2)
        logging.info(string)
        logging.info(string2)
print("OMG")
b = input()
print(a) ## Deliberate error for testing

0

Ayrıca stderr, shx2'nin yukarıdaki cevabına göre şunu kullanarak da ekleyebilirsiniz class multifile:

class Log(object):

    def __init__(self, path_log, mode="w", encoding="utf-8"):
        h = open(path_log, mode, encoding=encoding)
        sys.stdout = multifile([ sys.stdout, h ])
        sys.stderr = multifile([ sys.stderr, h ])

    def __enter__(self):
        """ Necessary if called by with (or with... as) """
        return self     # only necessary if "as"

    def __exit__(self, type, value, tb):
        """ Necessary if call by with """
        pass

    def __del__(self):
        if sys is not None:
            # restoring
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__

log = Log("test.txt")
print("line 1")
print("line 2", file=sys.stderr)
del log
print("line 3 only on screen")

Tek dezavantajı python 2.x'te bir istisna ortaya çıktığında: mesaj kaybolmuş gibi görünüyor.
Eric H.

-1

Ben için tam yerine yazdım sys.stderrve sadece yeniden adlandırma kodu çoğaltılamaz stderriçin stdoutyerine de kullanılabilir hale getirmek içinsys.stdout .

Bunu yapmak için, mevcut stderrve ile aynı nesne türünü oluşturuyorum stdoutve tüm yöntemleri orijinal sisteme iletiyorum stderrve stdout:

import os
import sys
import logging

class StdErrReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stderr` permanently.
        """
        global _stderr_singleton
        global _stderr_default
        global _stderr_default_class_type

        # On Sublime Text, `sys.__stderr__` is set to None, because they already replaced `sys.stderr`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stderr__:
            sys.__stderr__ = sys.stderr

        try:
            _stderr_default
            _stderr_default_class_type

        except NameError:
            _stderr_default = sys.stderr
            _stderr_default_class_type = type( _stderr_default )

        # Recreate the sys.stderr logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stderr_write = _stderr_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stderr_write
            global _sys_stderr_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stderr_write`
            def _sys_stderr_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stderr_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stderr_write` function pointer ever
            try:
                _sys_stderr_write

            except NameError:

                def _sys_stderr_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stderr.write` and our custom wrapper around it.
                    """
                    _sys_stderr_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stderr_singleton

        except NameError:

            class StdErrReplamentHidden(_stderr_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stderr_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stderr_default.__abstractmethods__

                if hasattr( _stderr_default, "__base__" ):
                    __base__ = _stderr_default.__base__

                if hasattr( _stderr_default, "__bases__" ):
                    __bases__ = _stderr_default.__bases__

                if hasattr( _stderr_default, "__basicsize__" ):
                    __basicsize__ = _stderr_default.__basicsize__

                if hasattr( _stderr_default, "__call__" ):
                    __call__ = _stderr_default.__call__

                if hasattr( _stderr_default, "__class__" ):
                    __class__ = _stderr_default.__class__

                if hasattr( _stderr_default, "__delattr__" ):
                    __delattr__ = _stderr_default.__delattr__

                if hasattr( _stderr_default, "__dict__" ):
                    __dict__ = _stderr_default.__dict__

                if hasattr( _stderr_default, "__dictoffset__" ):
                    __dictoffset__ = _stderr_default.__dictoffset__

                if hasattr( _stderr_default, "__dir__" ):
                    __dir__ = _stderr_default.__dir__

                if hasattr( _stderr_default, "__doc__" ):
                    __doc__ = _stderr_default.__doc__

                if hasattr( _stderr_default, "__eq__" ):
                    __eq__ = _stderr_default.__eq__

                if hasattr( _stderr_default, "__flags__" ):
                    __flags__ = _stderr_default.__flags__

                if hasattr( _stderr_default, "__format__" ):
                    __format__ = _stderr_default.__format__

                if hasattr( _stderr_default, "__ge__" ):
                    __ge__ = _stderr_default.__ge__

                if hasattr( _stderr_default, "__getattribute__" ):
                    __getattribute__ = _stderr_default.__getattribute__

                if hasattr( _stderr_default, "__gt__" ):
                    __gt__ = _stderr_default.__gt__

                if hasattr( _stderr_default, "__hash__" ):
                    __hash__ = _stderr_default.__hash__

                if hasattr( _stderr_default, "__init__" ):
                    __init__ = _stderr_default.__init__

                if hasattr( _stderr_default, "__init_subclass__" ):
                    __init_subclass__ = _stderr_default.__init_subclass__

                if hasattr( _stderr_default, "__instancecheck__" ):
                    __instancecheck__ = _stderr_default.__instancecheck__

                if hasattr( _stderr_default, "__itemsize__" ):
                    __itemsize__ = _stderr_default.__itemsize__

                if hasattr( _stderr_default, "__le__" ):
                    __le__ = _stderr_default.__le__

                if hasattr( _stderr_default, "__lt__" ):
                    __lt__ = _stderr_default.__lt__

                if hasattr( _stderr_default, "__module__" ):
                    __module__ = _stderr_default.__module__

                if hasattr( _stderr_default, "__mro__" ):
                    __mro__ = _stderr_default.__mro__

                if hasattr( _stderr_default, "__name__" ):
                    __name__ = _stderr_default.__name__

                if hasattr( _stderr_default, "__ne__" ):
                    __ne__ = _stderr_default.__ne__

                if hasattr( _stderr_default, "__new__" ):
                    __new__ = _stderr_default.__new__

                if hasattr( _stderr_default, "__prepare__" ):
                    __prepare__ = _stderr_default.__prepare__

                if hasattr( _stderr_default, "__qualname__" ):
                    __qualname__ = _stderr_default.__qualname__

                if hasattr( _stderr_default, "__reduce__" ):
                    __reduce__ = _stderr_default.__reduce__

                if hasattr( _stderr_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stderr_default.__reduce_ex__

                if hasattr( _stderr_default, "__repr__" ):
                    __repr__ = _stderr_default.__repr__

                if hasattr( _stderr_default, "__setattr__" ):
                    __setattr__ = _stderr_default.__setattr__

                if hasattr( _stderr_default, "__sizeof__" ):
                    __sizeof__ = _stderr_default.__sizeof__

                if hasattr( _stderr_default, "__str__" ):
                    __str__ = _stderr_default.__str__

                if hasattr( _stderr_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stderr_default.__subclasscheck__

                if hasattr( _stderr_default, "__subclasses__" ):
                    __subclasses__ = _stderr_default.__subclasses__

                if hasattr( _stderr_default, "__subclasshook__" ):
                    __subclasshook__ = _stderr_default.__subclasshook__

                if hasattr( _stderr_default, "__text_signature__" ):
                    __text_signature__ = _stderr_default.__text_signature__

                if hasattr( _stderr_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stderr_default.__weakrefoffset__

                if hasattr( _stderr_default, "mro" ):
                    mro = _stderr_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stderr_default )` constructor, so we can 
                        instantiate any kind of `sys.stderr` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stderr_default, attribute ):

                            base_class_attribute = super( _stderr_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stderr_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stderr.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stderr_write

                    try:
                        return _stderr_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stderr_default_class_type, _stderr_default ).__getattribute__( item )

            _stderr_singleton = StdErrReplamentHidden()
            sys.stderr = _stderr_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stderr` writer from `sys.stderr` and allow the next call to `lock()` create
            a new writer for the stderr.
        """

        if cls.is_active:
            global _sys_stderr_write_hidden

            cls.is_active = False
            _sys_stderr_write_hidden = _stderr_default.write



class StdOutReplament(object):
    """
        How to redirect stdout and stderr to logger in Python
        /programming/19425736/how-to-redirect-stdout-and-stderr-to-logger-in-python

        Set a Read-Only Attribute in Python?
        /programming/24497316/set-a-read-only-attribute-in-python
    """
    is_active = False

    @classmethod
    def lock(cls, logger):
        """
            Attach this singleton logger to the `sys.stdout` permanently.
        """
        global _stdout_singleton
        global _stdout_default
        global _stdout_default_class_type

        # On Sublime Text, `sys.__stdout__` is set to None, because they already replaced `sys.stdout`
        # by some `_LogWriter()` class, then just save the current one over there.
        if not sys.__stdout__:
            sys.__stdout__ = sys.stdout

        try:
            _stdout_default
            _stdout_default_class_type

        except NameError:
            _stdout_default = sys.stdout
            _stdout_default_class_type = type( _stdout_default )

        # Recreate the sys.stdout logger when it was reset by `unlock()`
        if not cls.is_active:
            cls.is_active = True
            _stdout_write = _stdout_default.write

            logger_call = logger.debug
            clean_formatter = logger.clean_formatter

            global _sys_stdout_write
            global _sys_stdout_write_hidden

            if sys.version_info <= (3,2):
                logger.file_handler.terminator = '\n'

            # Always recreate/override the internal write function used by `_sys_stdout_write`
            def _sys_stdout_write_hidden(*args, **kwargs):
                """
                    Suppress newline in Python logging module
                    /programming/7168790/suppress-newline-in-python-logging-module
                """

                try:
                    _stdout_write( *args, **kwargs )
                    file_handler = logger.file_handler

                    formatter = file_handler.formatter
                    terminator = file_handler.terminator

                    file_handler.formatter = clean_formatter
                    file_handler.terminator = ""

                    kwargs['extra'] = {'_duplicated_from_file': True}
                    logger_call( *args, **kwargs )

                    file_handler.formatter = formatter
                    file_handler.terminator = terminator

                except Exception:
                    logger.exception( "Could not write to the file_handler: %s(%s)", file_handler, logger )
                    cls.unlock()

            # Only create one `_sys_stdout_write` function pointer ever
            try:
                _sys_stdout_write

            except NameError:

                def _sys_stdout_write(*args, **kwargs):
                    """
                        Hides the actual function pointer. This allow the external function pointer to
                        be cached while the internal written can be exchanged between the standard
                        `sys.stdout.write` and our custom wrapper around it.
                    """
                    _sys_stdout_write_hidden( *args, **kwargs )

        try:
            # Only create one singleton instance ever
            _stdout_singleton

        except NameError:

            class StdOutReplamentHidden(_stdout_default_class_type):
                """
                    Which special methods bypasses __getattribute__ in Python?
                    /programming/12872695/which-special-methods-bypasses-getattribute-in-python
                """

                if hasattr( _stdout_default, "__abstractmethods__" ):
                    __abstractmethods__ = _stdout_default.__abstractmethods__

                if hasattr( _stdout_default, "__base__" ):
                    __base__ = _stdout_default.__base__

                if hasattr( _stdout_default, "__bases__" ):
                    __bases__ = _stdout_default.__bases__

                if hasattr( _stdout_default, "__basicsize__" ):
                    __basicsize__ = _stdout_default.__basicsize__

                if hasattr( _stdout_default, "__call__" ):
                    __call__ = _stdout_default.__call__

                if hasattr( _stdout_default, "__class__" ):
                    __class__ = _stdout_default.__class__

                if hasattr( _stdout_default, "__delattr__" ):
                    __delattr__ = _stdout_default.__delattr__

                if hasattr( _stdout_default, "__dict__" ):
                    __dict__ = _stdout_default.__dict__

                if hasattr( _stdout_default, "__dictoffset__" ):
                    __dictoffset__ = _stdout_default.__dictoffset__

                if hasattr( _stdout_default, "__dir__" ):
                    __dir__ = _stdout_default.__dir__

                if hasattr( _stdout_default, "__doc__" ):
                    __doc__ = _stdout_default.__doc__

                if hasattr( _stdout_default, "__eq__" ):
                    __eq__ = _stdout_default.__eq__

                if hasattr( _stdout_default, "__flags__" ):
                    __flags__ = _stdout_default.__flags__

                if hasattr( _stdout_default, "__format__" ):
                    __format__ = _stdout_default.__format__

                if hasattr( _stdout_default, "__ge__" ):
                    __ge__ = _stdout_default.__ge__

                if hasattr( _stdout_default, "__getattribute__" ):
                    __getattribute__ = _stdout_default.__getattribute__

                if hasattr( _stdout_default, "__gt__" ):
                    __gt__ = _stdout_default.__gt__

                if hasattr( _stdout_default, "__hash__" ):
                    __hash__ = _stdout_default.__hash__

                if hasattr( _stdout_default, "__init__" ):
                    __init__ = _stdout_default.__init__

                if hasattr( _stdout_default, "__init_subclass__" ):
                    __init_subclass__ = _stdout_default.__init_subclass__

                if hasattr( _stdout_default, "__instancecheck__" ):
                    __instancecheck__ = _stdout_default.__instancecheck__

                if hasattr( _stdout_default, "__itemsize__" ):
                    __itemsize__ = _stdout_default.__itemsize__

                if hasattr( _stdout_default, "__le__" ):
                    __le__ = _stdout_default.__le__

                if hasattr( _stdout_default, "__lt__" ):
                    __lt__ = _stdout_default.__lt__

                if hasattr( _stdout_default, "__module__" ):
                    __module__ = _stdout_default.__module__

                if hasattr( _stdout_default, "__mro__" ):
                    __mro__ = _stdout_default.__mro__

                if hasattr( _stdout_default, "__name__" ):
                    __name__ = _stdout_default.__name__

                if hasattr( _stdout_default, "__ne__" ):
                    __ne__ = _stdout_default.__ne__

                if hasattr( _stdout_default, "__new__" ):
                    __new__ = _stdout_default.__new__

                if hasattr( _stdout_default, "__prepare__" ):
                    __prepare__ = _stdout_default.__prepare__

                if hasattr( _stdout_default, "__qualname__" ):
                    __qualname__ = _stdout_default.__qualname__

                if hasattr( _stdout_default, "__reduce__" ):
                    __reduce__ = _stdout_default.__reduce__

                if hasattr( _stdout_default, "__reduce_ex__" ):
                    __reduce_ex__ = _stdout_default.__reduce_ex__

                if hasattr( _stdout_default, "__repr__" ):
                    __repr__ = _stdout_default.__repr__

                if hasattr( _stdout_default, "__setattr__" ):
                    __setattr__ = _stdout_default.__setattr__

                if hasattr( _stdout_default, "__sizeof__" ):
                    __sizeof__ = _stdout_default.__sizeof__

                if hasattr( _stdout_default, "__str__" ):
                    __str__ = _stdout_default.__str__

                if hasattr( _stdout_default, "__subclasscheck__" ):
                    __subclasscheck__ = _stdout_default.__subclasscheck__

                if hasattr( _stdout_default, "__subclasses__" ):
                    __subclasses__ = _stdout_default.__subclasses__

                if hasattr( _stdout_default, "__subclasshook__" ):
                    __subclasshook__ = _stdout_default.__subclasshook__

                if hasattr( _stdout_default, "__text_signature__" ):
                    __text_signature__ = _stdout_default.__text_signature__

                if hasattr( _stdout_default, "__weakrefoffset__" ):
                    __weakrefoffset__ = _stdout_default.__weakrefoffset__

                if hasattr( _stdout_default, "mro" ):
                    mro = _stdout_default.mro

                def __init__(self):
                    """
                        Override any super class `type( _stdout_default )` constructor, so we can 
                        instantiate any kind of `sys.stdout` replacement object, in case it was already 
                        replaced by something else like on Sublime Text with `_LogWriter()`.

                        Assures all attributes were statically replaced just above. This should happen in case
                        some new attribute is added to the python language.

                        This also ignores the only two methods which are not equal, `__init__()` and `__getattribute__()`.
                    """
                    different_methods = ("__init__", "__getattribute__")
                    attributes_to_check = set( dir( object ) + dir( type ) )

                    for attribute in attributes_to_check:

                        if attribute not in different_methods \
                                and hasattr( _stdout_default, attribute ):

                            base_class_attribute = super( _stdout_default_class_type, self ).__getattribute__( attribute )
                            target_class_attribute = _stdout_default.__getattribute__( attribute )

                            if base_class_attribute != target_class_attribute:
                                sys.stdout.write( "    The base class attribute `%s` is different from the target class:\n%s\n%s\n\n" % (
                                        attribute, base_class_attribute, target_class_attribute ) )

                def __getattribute__(self, item):

                    if item == 'write':
                        return _sys_stdout_write

                    try:
                        return _stdout_default.__getattribute__( item )

                    except AttributeError:
                        return super( _stdout_default_class_type, _stdout_default ).__getattribute__( item )

            _stdout_singleton = StdOutReplamentHidden()
            sys.stdout = _stdout_singleton

        return cls

    @classmethod
    def unlock(cls):
        """
            Detach this `stdout` writer from `sys.stdout` and allow the next call to `lock()` create
            a new writer for the stdout.
        """

        if cls.is_active:
            global _sys_stdout_write_hidden

            cls.is_active = False
            _sys_stdout_write_hidden = _stdout_default.write

Bunu kullanmak için, çıktı metnini göndermek için kullanmak istediğiniz kaydediciyi arayabilir StdErrReplament::lock(logger)ve geçirebilirsiniz StdOutReplament::lock(logger). Örneğin:

import os
import sys
import logging

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

file_handler = logging.FileHandler( log_file_path, 'a' )
file_handler.formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )

log.file_handler = file_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

Bu kodu çalıştırdığınızda, ekranda şunu göreceksiniz:

görüntü açıklamasını buraya girin

Ve dosya içeriğinde:

görüntü açıklamasını buraya girin

log.debugAramaların içeriğini ekranda da görmek isterseniz, kaydedicinize bir akış işleyici eklemeniz gerekecektir. Bu durumda şöyle olur:

import os
import sys
import logging

class ContextFilter(logging.Filter):
    """ This filter avoids duplicated information to be displayed to the StreamHandler log. """
    def filter(self, record):
        return not "_duplicated_from_file" in record.__dict__

current_folder = os.path.dirname( os.path.realpath( __file__ ) )
log_file_path = os.path.join( current_folder, "my_log_file.txt" )

stream_handler = logging.StreamHandler()
file_handler = logging.FileHandler( log_file_path, 'a' )

formatter = logging.Formatter( "%(asctime)s %(name)s %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S" )
file_handler.formatter = formatter
stream_handler.formatter = formatter
stream_handler.addFilter( ContextFilter() )

log = logging.getLogger( __name__ )
log.setLevel( "DEBUG" )
log.addHandler( file_handler )
log.addHandler( stream_handler )

log.file_handler = file_handler
log.stream_handler = stream_handler
log.clean_formatter = logging.Formatter( "", "" )

StdOutReplament.lock( log )
StdErrReplament.lock( log )

log.debug( "I am doing usual logging debug..." )
sys.stderr.write( "Tests 1...\n" )
sys.stdout.write( "Tests 2...\n" )

Çalıştırıldığında şöyle çıktı:

görüntü açıklamasını buraya girin

Hala bunu dosyaya kaydederken my_log_file.txt:

görüntü açıklamasını buraya girin

Bunu ile devre dışı bıraktığınızda StdErrReplament:unlock(), stderrakışın yalnızca standart davranışını geri yükleyecektir , çünkü ekli günlükleyici hiçbir zaman ayrılamaz çünkü başka biri eski sürümüne referans alabilir. Bu yüzden asla ölmeyecek küresel bir bekar. Bu nedenle, bu modülü impveya başka bir şeyle yeniden yükleme durumunda sys.stderr, zaten enjekte edildiği ve dahili olarak kaydedildiği için akımı asla geri almayacaktır .


6
bir akışı kopyalamak için inanılmaz düzeyde yanlışlıkla karmaşıklık.
Attila Lendvai
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.