loghandler.py 3.8 KB
Newer Older
Anders Blomdell's avatar
Anders Blomdell committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/python3

import threading
import asyncio
import os
import sys

LOG_MESSAGE = 0
LOG_ERROR = 1
LOG_WARNING = 2
LOG_DEBUG = 3

class LOG:
    
    class singleton(object):
        loop = asyncio.new_event_loop()
        lock = threading.Lock()
        def __init__(self):
            t = threading.Thread(daemon=True, target=self.loop.run_forever)
            t.start()
            
        def __call__(self):
            return self
    singleton = singleton()
    loop = singleton().loop
    lock = singleton().lock

    def __init__(self, level=None, prefix=None, parent=None):
        if level != None:
            self.level = level
        elif parent:
            self.level = parent.level
        else:
            level = LOG_WARNING
        self.prefix = ''.join(filter(None, [parent and parent.prefix,
                                            prefix]))

    def MESSAGE(self, *args, level=LOG_MESSAGE):
        if self.level >= level:
            with self.lock:
                buf = ' '.join(map(str, args))
                while '\n' in buf:
                    line, buf = buf.split('\n', 1)
                    if self.prefix != None:
                        print(self.prefix, end='', file=sys.stderr)
Anders Blomdell's avatar
Anders Blomdell committed
46
                    print(line, file=sys.stderr, flush=True)
Anders Blomdell's avatar
Anders Blomdell committed
47
48
49
                if len(buf):
                    if self.prefix != None:
                        print(self.prefix, end='', file=sys.stderr)
Anders Blomdell's avatar
Anders Blomdell committed
50
                    print(buf, file=sys.stderr, flush=True)
Anders Blomdell's avatar
Anders Blomdell committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        
    def ERROR(self, *args):
        self.MESSAGE(*args, level=LOG_ERROR)

    def WARNING(self, *args):
        self.MESSAGE(*args, level=LOG_WARNING)

    def DEBUG(self, *args):
        self.MESSAGE(*args, level=LOG_DEBUG)

    def makefile(self, level=LOG_WARNING, encoding=None):
        loop = self.singleton().loop
        class Reader:

            def __init__(self, log, fd):
                self.log = log
                self.fd = fd
                self.buf = bytearray()
                self.mutex = threading.Lock()
                def decode(b):
                    if encoding == None:
                        return b
                    else:
                        return b.decode(encoding)
                self.decode = decode
                    

78
            def __call__(self):
Anders Blomdell's avatar
Anders Blomdell committed
79
                with self.mutex:
80
81
                    buf = os.read(self.fd, 4096)
                    self.buf.extend(buf)
Anders Blomdell's avatar
Anders Blomdell committed
82
                    while True:
83
84
85
86
87
88
89
90
91
92
93
                        i = self.buf.find(b'\n')
                        if i < 0: break
                        self.log.MESSAGE(self.decode(self.buf[0:i]),
                                         level=level)
                        self.buf = self.buf[i+1:]

            def flush(self):
                with self.mutex:
                    while True:
         
                        buf = os.read(self.fd, 4096)
Anders Blomdell's avatar
Anders Blomdell committed
94
95
96
97
98
99
100
101
                        if len(buf) == 0: break
                        self.buf.extend(buf)
                        while True:
                            i = self.buf.find(b'\n')
                            if i < 0: break
                            self.log.MESSAGE(self.decode(self.buf[0:i]),
                                             level=level)
                            self.buf = self.buf[i+1:]
102
103
                if len(self.buf) > 0:
                    self.log.MESSAGE(self.decode(self.buf), level=level)
Anders Blomdell's avatar
Anders Blomdell committed
104
105
106
107
108
109
110
111
112
113
114
115
116
    
        class MakeFile:
            def __init__(self, log):
                self.rpipe, self.wpipe = os.pipe()
                self.reader = Reader(log, self.rpipe)
                loop.add_reader(self.rpipe, self.reader)

            def fileno(self):
                return self.wpipe

            def __del__(self):
                loop.remove_reader(self.rpipe)
                os.close(self.wpipe)
117
118
                self.reader.flush()
                
Anders Blomdell's avatar
Anders Blomdell committed
119
        return MakeFile(self)