]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/log.py
ifupdown: ifupdownmain: create a copy of environment dictionary for addons scripts...
[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 self.syslog_handler = logging.handlers.SysLogHandler(address=address, facility=facility)
66 self.syslog_handler.setFormatter(logging.Formatter(format))
67
68 # console
69 format = '%(levelname)s: %(message)s'
70 self.console_handler = logging.StreamHandler(sys.stderr)
71 self.console_handler.setFormatter(logging.Formatter(format))
72
73 if self.LOGGER_NAME[-1] == 'd':
74 self.update_current_logger(syslog=True, verbose=True, debug=False)
75 else:
76 self.update_current_logger(syslog=False, verbose=False, debug=False)
77
78 def update_current_logger(self, syslog, verbose, debug):
79 self.syslog = syslog
80 self.root.setLevel(self.get_log_level(verbose=verbose, debug=debug))
81 self.root.handlers = [self.syslog_handler if self.syslog else self.console_handler]
82 self.flush()
83
84 def flush(self):
85 if self.socket:
86 result = dict()
87 stdout = self._flush_buffer('stdout', self.stdout_buffer, result)
88 stderr = self._flush_buffer('stderr', self.stderr_buffer, result)
89 if stdout or stderr:
90 try:
91 self.tx_data(json.dumps(result))
92 self.redirect_stdouput()
93 except select.error as e:
94 # haven't seen the case yet
95 self.socket = None
96 self.update_current_logger(syslog=True, verbose=True)
97 self.critical(str(e))
98 exit(84)
99 self.console_handler.flush()
100 self.syslog_handler.flush()
101
102 def tx_data(self, data, socket=None):
103 socket_obj = socket if socket else self.socket
104 ready = select.select([], [socket_obj], [])
105 if ready and ready[1] and ready[1][0] == socket_obj:
106 frmt = "=%ds" % len(data)
107 packed_msg = struct.pack(frmt, data)
108 packed_hdr = struct.pack('=I', len(packed_msg))
109 socket_obj.sendall(packed_hdr)
110 socket_obj.sendall(packed_msg)
111
112 def set_socket(self, socket):
113 self.socket = socket
114 self.redirect_stdouput()
115
116 def redirect_stdouput(self):
117 self.stdout_buffer = sys.stdout = StringIO()
118 self.stderr_buffer = self.console_handler.stream = sys.stderr = StringIO()
119
120 def error(self, msg, *args, **kwargs):
121 self.root_error(msg, *args, **kwargs)
122 self.flush()
123
124 def critical(self, msg, *args, **kwargs):
125 self.root_critical(msg, *args, **kwargs)
126 self.flush()
127
128 def warning(self, msg, *args, **kwargs):
129 self.root_warning(msg, *args, **kwargs)
130 self.flush()
131
132 def info(self, msg, *args, **kwargs):
133 self.root_info(msg, *args, **kwargs)
134 self.flush()
135
136 def debug(self, msg, *args, **kwargs):
137 self.root_debug(msg, *args, **kwargs)
138 self.flush()
139
140 def get_current_log_level(self):
141 return self.root.level
142
143 def is_syslog(self): return self.syslog
144
145 @staticmethod
146 def get_log_level(verbose=False, debug=False):
147 log_level = logging.WARNING
148 if debug:
149 log_level = logging.DEBUG
150 elif verbose:
151 log_level = logging.INFO
152 return log_level
153
154 @staticmethod
155 def _flush_buffer(stream, buff, dictionary):
156 if buff:
157 data = buff.getvalue()
158 if data:
159 dictionary[stream] = data
160 return True
161
162
163 log = Log()
164
165
166 """
167
168 #logging.basicConfig( format="%(filename)s: %(username)s says '%(message)s' in %(funcname)s" )
169
170 Logger.debug(msg, *args, **kwargs)
171 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.)
172
173 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.
174
175 """
176
177 """
178 USE FILTER TO IGNORE "EXITS" MESSAGES
179 Now that you know the basic plot, let me introduce one more character - the Filter.
180 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.
181 Both Loggers and Handlers can have multiple Filters. You can add Filters using addFilter and removeFilter methods.
182 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.
183 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.)"""