]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/log.py
log: use stderr if syslog initialization fails
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / log.py
1 #!/usr/bin/env python
2 #
3 # Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Julien Fortin, julien@cumulusnetworks.com
5 #
6
7 import sys
8 import json
9 import struct
10 import select
11 import logging
12 import logging.handlers
13
14 from cStringIO import StringIO
15
16
17 class Log:
18 LOGGER_NAME = sys.argv[0].split('/')[-1]
19
20 def __init__(self):
21 """
22 - On start the daemon will log on syslog.
23 - For each client commands we might need to adjust the target
24 (stderr/stdout):
25 if -v --verbose or -d --debug are provided we override
26 sys.stdout and sys.stderr with string buffers to be able to send
27 back the content of these buffer on the UNIX socket back to the
28 client.
29 if -l or --syslog we make sure to use syslog.
30 """
31
32 self.stdout_buffer = None
33 self.stderr_buffer = None
34
35 self.root = logging.getLogger()
36 self.root.name = Log.LOGGER_NAME
37 self.root.setLevel(logging.INFO)
38
39 self.root_info = self.root.info
40 self.root_debug = self.root.debug
41 self.root_error = self.root.error
42 self.root_warning = self.root.warning
43 self.root_critical = self.root.critical
44
45 self.root.info = self.info
46 self.root.debug = self.debug
47 self.root.error = self.error
48 self.root.warning = self.warning
49 self.root.critical = self.critical
50
51 logging.addLevelName(logging.CRITICAL, 'critical')
52 logging.addLevelName(logging.WARNING, 'warning')
53 logging.addLevelName(logging.ERROR, 'error')
54 logging.addLevelName(logging.DEBUG, 'debug')
55 logging.addLevelName(logging.INFO, 'info')
56
57 self.syslog = True
58 self.socket = None
59
60 # syslog
61 facility = logging.handlers.SysLogHandler.LOG_DAEMON
62 address = '/dev/log'
63 format = '%(name)s: %(levelname)s: %(message)s'
64
65 try:
66 self.syslog_handler = logging.handlers.SysLogHandler(address=address, facility=facility)
67 self.syslog_handler.setFormatter(logging.Formatter(format))
68 except Exception as e:
69 sys.stderr.write("warning: syslogs: %s\n" % str(e))
70 self.syslog_handler = None
71
72 # console
73 format = '%(levelname)s: %(message)s'
74 self.console_handler = logging.StreamHandler(sys.stderr)
75 self.console_handler.setFormatter(logging.Formatter(format))
76
77 if self.syslog_handler and self.LOGGER_NAME[-1] == 'd':
78 self.update_current_logger(syslog=True, verbose=True, debug=False)
79 else:
80 self.update_current_logger(syslog=False, verbose=False, debug=False)
81
82 def update_current_logger(self, syslog, verbose, debug):
83 self.syslog = syslog
84 self.root.setLevel(self.get_log_level(verbose=verbose, debug=debug))
85 self.root.handlers = [self.syslog_handler if self.syslog and self.syslog_handler else self.console_handler]
86 self.flush()
87
88 def flush(self):
89 if self.socket:
90 result = dict()
91 stdout = self._flush_buffer('stdout', self.stdout_buffer, result)
92 stderr = self._flush_buffer('stderr', self.stderr_buffer, result)
93 if stdout or stderr:
94 try:
95 self.tx_data(json.dumps(result))
96 self.redirect_stdouput()
97 except select.error as e:
98 # haven't seen the case yet
99 self.socket = None
100 self.update_current_logger(syslog=True, verbose=True)
101 self.critical(str(e))
102 exit(84)
103 self.console_handler.flush()
104
105 if self.syslog_handler:
106 self.syslog_handler.flush()
107
108 def tx_data(self, data, socket=None):
109 socket_obj = socket if socket else self.socket
110 ready = select.select([], [socket_obj], [])
111 if ready and ready[1] and ready[1][0] == socket_obj:
112 frmt = "=%ds" % len(data)
113 packed_msg = struct.pack(frmt, data)
114 packed_hdr = struct.pack('=I', len(packed_msg))
115 socket_obj.sendall(packed_hdr)
116 socket_obj.sendall(packed_msg)
117
118 def set_socket(self, socket):
119 self.socket = socket
120 self.redirect_stdouput()
121
122 def redirect_stdouput(self):
123 self.stdout_buffer = sys.stdout = StringIO()
124 self.stderr_buffer = self.console_handler.stream = sys.stderr = StringIO()
125
126 def error(self, msg, *args, **kwargs):
127 self.root_error(msg, *args, **kwargs)
128 self.flush()
129
130 def critical(self, msg, *args, **kwargs):
131 self.root_critical(msg, *args, **kwargs)
132 self.flush()
133
134 def warning(self, msg, *args, **kwargs):
135 self.root_warning(msg, *args, **kwargs)
136 self.flush()
137
138 def info(self, msg, *args, **kwargs):
139 self.root_info(msg, *args, **kwargs)
140 self.flush()
141
142 def debug(self, msg, *args, **kwargs):
143 self.root_debug(msg, *args, **kwargs)
144 self.flush()
145
146 def get_current_log_level(self):
147 return self.root.level
148
149 def is_syslog(self): return self.syslog
150
151 @staticmethod
152 def get_log_level(verbose=False, debug=False):
153 log_level = logging.WARNING
154 if debug:
155 log_level = logging.DEBUG
156 elif verbose:
157 log_level = logging.INFO
158 return log_level
159
160 @staticmethod
161 def _flush_buffer(stream, buff, dictionary):
162 if buff:
163 data = buff.getvalue()
164 if data:
165 dictionary[stream] = data
166 return True
167
168
169 log = Log()
170
171
172 """
173
174 #logging.basicConfig( format="%(filename)s: %(username)s says '%(message)s' in %(funcname)s" )
175
176 Logger.debug(msg, *args, **kwargs)
177 Logs a message with level DEBUG on this logger. The msg is the message format string, and the 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.)
178
179 There are two keyword arguments in kwargs which are inspected: exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message. If an exception tuple (in the format returned by sys.exc_info()) is provided, it is used; otherwise, sys.exc_info() is called to get the exception information.
180
181 """
182
183 """
184 USE FILTER TO IGNORE "EXITS" MESSAGES
185 Now that you know the basic plot, let me introduce one more character - the Filter.
186 Filter as the name suggests, allows you to filter a message before you log it. Yes, messages are filtered based on the level setting, but adding a Filter gives you more fine grained control of messages you log.
187 Both Loggers and Handlers can have multiple Filters. You can add Filters using addFilter and removeFilter methods.
188 When a Logger/Handler receives a message, it consults all of its filters. If the filter(record) method on any of the Filters attached returns False (or 0) the message is dropped.
189 The official documentation, though detailed, is actually pretty confusing about the role of Filters. This is a pity; because Filters can be handy when you want to drop a message based on a regular expression, error code, contextual information and pretty much anything else. The default Filter is pretty much useless (and the doc string is very confusing too). Just inherit from the default filter and override the filter method according to what you want to filter out. (Be sure to download the source for logging module and check out the unit tests which have some good examples. See the references at the end of this post.)"""