onaplogging: Docstrings, refactor, type hinting
[logging-analytics.git] / pylog / onaplogging / logWatchDog.py
1 # Copyright 2018 ke liang <lokyse@163.com>.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import os
16 import traceback
17
18 from logging import config
19 from typing import Dict, Optional, Any
20 from deprecated import deprecated
21 from warnings import warn
22
23 from watchdog.observers import Observer
24 from watchdog.events import FileSystemEventHandler, FileSystemEvent
25
26 from onaplogging.utils.tools import yaml_to_dict
27
28
29 __all__ = ['patch_loggingYaml']  # rename after the deprecated name changed
30
31
32 class FileEventHandlers(FileSystemEventHandler):
33     """Handler of the events in the file system.
34
35     Use it to keep and eye on files in the file system.
36
37     Extends:
38         watchdog.events.FileSystemEventHandler
39     Properties:
40         filepath        : The path to the file to be monitored.
41         current_config  : Defaults to None.
42     Args:
43         filepath        : The path to the file to be monitored.
44     """
45
46     @property
47     def filepath(self):
48         # type: () -> str
49         return self._filepath
50
51     @property
52     def current_config(self):
53         # type: () -> str
54         return self.currentConfig  # deprecated, replace with _current_config
55
56     @filepath.setter
57     def filepath(self, value):
58         # type: (str) -> str
59         self._filepath = value
60
61     @current_config.setter
62     def current_config(self, value):
63         # type: (Dict) -> Dict
64         self.currentConfig = value
65
66     def __init__(self, filepath):  # type: (str)
67         warn("Attribute currentConfig will be replaced with property"
68                "current_config. Use current_config instead.")
69
70         FileSystemEventHandler.__init__(self)
71
72         self.filepath = filepath
73         self.current_config = None
74
75     def on_modified(self, event):
76         # type: (FileSystemEvent) -> None
77         """Configuration file actualizer.
78
79         When an event occurs in the file system  the hadnler's filepath
80         is taken to update the configuration file. If the actualization
81         of  the  config file  fails it  will  keep the old  config file.
82
83         Args:
84             event       : Represents an event on the file system.
85         Raises:
86             Exception   : If the actualization of the config file fails.
87         """
88         try:
89             if event.src_path == self.filepath:
90
91                 new_config = yaml_to_dict(self.filepath)
92                 print("Reloading logging configuration file %s "
93                         % event.src_path)
94
95                 config.dictConfig(new_config)
96                 self.current_config = new_config
97
98         except Exception:
99             traceback.print_exc()
100             print("Reuse the old configuration to avoid this"
101                   "exception terminate program")
102
103             if self.current_config:
104                 config.dictConfig(self.current_config)
105
106
107 def _yamlConfig(filepath=None, watchDog=None):
108     # type: (Optional[str], Optional[Any]) -> None
109     """YAML configuration file loader.
110
111     Use it to monitor a file status in a directory.  The watchdog can monitor
112     a YAML file status looking for modifications. If the watchdog is provided
113     start  observing  the  directory. The new configuration  file is saved as
114     current for the later reuse.
115
116     Args:
117         filepath    : The path to the file to be monitored.   Defaults to None.
118         watchDog    : Monitors a YAML file identifier status. Defaults to None.
119
120     Raises:
121         OSError     : If the requested file in the filepath is not a file.
122         Exception   : If watchdog observer setup  or YAML coversion fails.
123     """
124
125     is_file = os.path.isfile(filepath)
126
127     if is_file is False:
128         raise OSError("%s is not a file" % (filepath))
129
130     dirpath = os.path.dirname(filepath)
131     event_handler = None
132
133     try:
134         dictConfig = yaml_to_dict(filepath)
135         # Dev note: Will send a notify then we could reload logging config
136         if watchDog:
137             observer = Observer()
138             event_handler = FileEventHandlers(filepath)
139             observer.schedule(event_handler=event_handler,
140                               path=dirpath,
141                               recursive=False)
142             observer.setDaemon(True)
143             observer.start()
144
145         config.dictConfig(dictConfig)
146
147         if event_handler:
148             event_handler.currentConfig = dictConfig
149
150     except Exception:
151         traceback.print_exc()
152
153
154 def patch_logging_yaml():
155     # type: () -> None
156     """YAML configuration patch.
157
158     Adds the YAML configuration file loader
159     to logging.config module during runtime.
160     """
161     config.yamlConfig = _yamlConfig
162
163
164 @deprecated(reason="Will be removed. Call patch_logging_yaml() instead.")
165 def patch_loggingYaml():
166     """See patch_logging_yaml()"""
167     patch_logging_yaml()