1 # Copyright (C) 2016, 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License as
5 # published by the Free Software Foundation; version 2.
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 # General Public License for more details.
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to the Free Software
14 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 # https://www.gnu.org/licenses/gpl-2.0-standalone.html
20 # Julien Fortin, julien@cumulusnetworks.com
28 import logging
.handlers
30 root_logger
= logging
.getLogger()
34 LOGGER_NAME
= "ifupdown2"
35 LOGGER_NAME_DAEMON
= "ifupdown2d"
37 DEFAULT_TCP_LOGGING_PORT
= 42422
38 DEFAULT_LOGGING_LEVEL_DAEMON
= logging
.INFO
39 DEFAULT_LOGGING_LEVEL_NORMAL
= logging
.WARNING
45 if not LogManager
.__instance
:
47 LogManager
.__instance
= LogManager()
48 except Exception as e
:
49 sys
.stderr
.write("warning: ifupdown2.Log: %s\n" % str(e
))
51 return LogManager
.__instance
55 Setup root logger and console handler (stderr). To enable daemon, client
56 or standalone logging please call the proper function, see:
57 "start_(daemon|client|standlone)_logging"
59 if LogManager
.__instance
:
60 raise RuntimeError("Log: invalid access. Please use Log.getInstance()")
62 LogManager
.__instance
= self
64 self
.__fmt
= "%(levelname)s: %(message)s"
66 self
.__debug
_fmt
= "%(asctime)s: %(threadName)s: %(name)s: " \
67 "%(filename)s:%(lineno)d:%(funcName)s(): " \
68 "%(levelname)s: %(message)s"
70 self
.__root
_logger
= logging
.getLogger()
71 self
.__root
_logger
.name
= self
.LOGGER_NAME
73 self
.__socket
_handler
= None
74 self
.__syslog
_handler
= None
75 self
.__console
_handler
= None
79 # by default we attach a console handler that logs on stderr
80 # the daemon can manually remove this handler on startup
81 self
.__console
_handler
= logging
.StreamHandler(sys
.stderr
)
82 self
.__console
_handler
.setFormatter(logging
.Formatter(self
.__fmt
))
83 self
.__root
_logger
.addHandler(self
.__console
_handler
)
85 if os
.path
.exists("/dev/log"):
87 self
.__syslog
_handler
= logging
.handlers
.SysLogHandler(
89 facility
=logging
.handlers
.SysLogHandler
.LOG_DAEMON
91 self
.__syslog
_handler
.setFormatter(logging
.Formatter(self
.__fmt
))
92 except Exception as e
:
93 sys
.stderr
.write("warning: syslog: %s\n" % str(e
))
94 self
.__syslog
_handler
= None
96 logging
.addLevelName(logging
.CRITICAL
, "critical")
97 logging
.addLevelName(logging
.WARNING
, "warning")
98 logging
.addLevelName(logging
.ERROR
, "error")
99 logging
.addLevelName(logging
.DEBUG
, "debug")
100 logging
.addLevelName(logging
.INFO
, "info")
102 def set_level(self
, default
, error
=False, warning
=False, info
=False, debug
=False):
104 Set root handler logging level
112 log_level
= logging
.DEBUG
114 log_level
= logging
.INFO
116 log_level
= logging
.WARNING
118 log_level
= logging
.ERROR
122 for handler
in self
.__root
_logger
.handlers
:
123 handler
.setLevel(log_level
)
124 self
.__root
_logger
.setLevel(log_level
)
126 def enable_console(self
):
127 """ Add console handler to root logger """
128 self
.__root
_logger
.addHandler(self
.__console
_handler
)
130 def disable_console(self
):
131 """ Remove console handler from root logger """
132 self
.__root
_logger
.removeHandler(self
.__console
_handler
)
134 def enable_syslog(self
):
135 """ Add syslog handler to root logger """
136 if self
.__syslog
_handler
:
137 self
.__root
_logger
.addHandler(self
.__syslog
_handler
)
139 def disable_syslog(self
):
140 """ Remove syslog handler from root logger """
141 if self
.__syslog
_handler
:
142 self
.__root
_logger
.removeHandler(self
.__syslog
_handler
)
144 def close_log_stream(self
):
145 """ Close socket to disconnect client.
146 We first have to perform this little hack: it seems like the socket is
147 not opened until data (LogRecord) are transmitted. In our most basic use
148 case (client sends "ifup -a") the daemon doesn't send back any LogRecord
149 but we can't predict that in the client. The client is already in a
150 blocking-select waiting for data on it's socket handler
151 (StreamRequestHandler). For this special case we need to manually call
152 "createSocket" to open the channel to the client so that we can properly
153 close it. That way the client can exit cleanly.
155 self
.__root
_logger
.removeHandler(self
.__socket
_handler
)
156 self
.__socket
_handler
.acquire()
157 self
.__socket
_handler
.retryTime
= None
159 if not self
.__socket
_handler
.sock
:
160 self
.__socket
_handler
.createSocket()
162 self
.__socket
_handler
.close()
163 self
.__socket
_handler
.release()
165 def start_stream(self
):
166 self
.__root
_logger
.addHandler(self
.__socket
_handler
)
168 def set_daemon_logging_level(self
, args
):
169 self
.set_level(self
.DEFAULT_LOGGING_LEVEL_DAEMON
, info
=args
.verbose
, debug
=args
.debug
)
171 def set_request_logging_level(self
, args
):
172 if not hasattr(args
, "syslog") or not args
.syslog
:
173 self
.disable_syslog()
175 self
.__root
_logger
.removeHandler(self
.__socket
_handler
)
176 self
.set_level(self
.DEFAULT_LOGGING_LEVEL_NORMAL
, info
=args
.verbose
, debug
=args
.debug
)
178 def start_client_logging(self
, args
):
179 """ Setup root logger name and client log level
180 syslog is handled by the daemon directly
182 self
.__root
_logger
.name
= self
.LOGGER_NAME
184 if hasattr(args
, "syslog") and args
.syslog
:
186 self
.disable_console()
188 self
.set_level(self
.DEFAULT_LOGGING_LEVEL_NORMAL
, info
=args
.verbose
, debug
=args
.debug
)
190 def start_standalone_logging(self
, args
):
191 self
.__root
_logger
.name
= self
.LOGGER_NAME
193 if hasattr(args
, "syslog") and args
.syslog
:
195 self
.disable_console()
197 self
.__root
_logger
.removeHandler(self
.__console
_handler
)
199 self
.set_level(self
.DEFAULT_LOGGING_LEVEL_NORMAL
, info
=args
.verbose
, debug
=args
.debug
)
201 def start_daemon_logging(self
, args
):
203 Daemon mode initialize a socket handler to transmit logging to the
204 client, we can also do syslog logging and/or console logging (probably
205 just for debugging purpose)
209 self
.__root
_logger
.name
= self
.LOGGER_NAME_DAEMON
214 # Create SocketHandler for daemon-client communication
215 self
.__socket
_handler
= logging
.handlers
.SocketHandler(
217 port
=self
.DEFAULT_TCP_LOGGING_PORT
219 self
.__root
_logger
.addHandler(self
.__socket
_handler
)
222 self
.disable_console()
224 self
.set_daemon_logging_level(args
)
226 def write(self
, msg
):
227 root_logger
.info(msg
)