blob: 0af2c22cc036029590805732e08d7fe9c8d0d03e [file] [log] [blame]
Marek Szwalkiewiczbe4c4642020-01-30 13:49:18 +00001"""Copyright 2019 Deutsche Telekom.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14"""
15import logging
16import os
17from configparser import ConfigParser, SectionProxy
18from distutils.util import strtobool
19from logging import Logger
20from pathlib import Path, PurePath
21from typing import NoReturn
22
23from onaplogging import monkey
24from onaplogging.mdcformatter import MDCFormatter # noqa
25
26monkey.patch_loggingYaml()
27
28
29DEFAUL_CONFIGURATION_FILE: str = str(PurePath(Path().absolute(), "../configuration.ini"))
30SUPPLIED_CONFIGURATION_FILE: str = os.environ.get("CONFIGURATION")
31CONFIGURATION_FILE: str = str(os.path.expanduser(Path(SUPPLIED_CONFIGURATION_FILE or DEFAUL_CONFIGURATION_FILE)))
32
33
34class ArtifactManagerConfiguration:
35 """ServerConfiguration class loads configuration from config INI files."""
36
37 def __init__(self, config_file_path: str) -> NoReturn:
38 """Initialize configuration class instance.
39
40 Configuration is loaded from file provided as a parameter. Environment variables are loaded also.
41 Logger for object is created with the name same as the class name.
42 :param config_file_path: Path to configuration file.
43 """
44 self.config_file_path = config_file_path
45 self.config = ConfigParser(os.environ)
46 self.config.read(config_file_path, encoding="utf-8")
47
48 @property
49 def configuration_directory(self) -> str:
50 """Get directory path to a directory with configuration ini file.
51
52 This is used to handle relative file paths in config file.
53 """
54 return os.path.dirname(self.config_file_path)
55
56 def get_section(self, section_name: str) -> SectionProxy:
57 """Get the section from configuration file.
58
59 :param section_name: Name of the section to get
60 :raises: KeyError
61 :return: SectionProxy object for given section name
62 """
63 return self.config[section_name]
64
65 def __getitem__(self, key: str) -> SectionProxy:
66 """Get the section from configuration file.
67
68 This method just calls the get_section method but allows us to use it as key lookup
69
70 :param section_name: Name of the section to get
71 :raises: KeyError
72 :return: SectionProxy object for given section name
73 """
74 return self.get_section(key)
75
76 def get_property(self, section_name: str, property_name: str) -> str:
77 """Get the property value from *section_name* section.
78
79 :param section_name: Name of the section config file section on which property is set
80 :param property_name: Name of the property to get
81 :raises: configparser.NoOptionError
82 :return: String value of the property
83 """
84 return self.config.get(section_name, property_name)
85
86 def artifact_manager_property(self, property_name: str) -> str:
87 """Get the property value from *artifactManagerServer* section.
88
89 :param property_name: Name of the property to get
90 :raises: configparser.NoOptionError
91 :return: String value of the property
92 """
93 return self.config.get("artifactManagerServer", property_name)
94
95
96config = ArtifactManagerConfiguration(CONFIGURATION_FILE)
97
98
99def prepare_logger(log_file_path: str, development_mode: bool, config: ArtifactManagerConfiguration) -> callable:
100 """Base MDC logger configuration.
101
102 Level depends on the *development_mode* flag: DEBUG if development_mode is set or INFO otherwise.
103 Console handler is created from MDC settings from onappylog library.
104
105 :param log_file_path: Path to the log file, where logs are going to be saved.
106 :param development_mode: Boolean type value which means if logger should be setup in development mode or not
107 :param config: Configuration class so we can fetch app settings (paths) to logger.
108 :return: callable
109 """
110 logging_level: int = logging.DEBUG if development_mode else logging.INFO
111 logging.basicConfig(filename=log_file_path, level=logging_level)
112 logging.config.yamlConfig(
113 filepath=Path(config.configuration_directory, config["artifactManagerServer"]["logConfig"])
114 )
115
116 console: logging.StreamHandler = logging.StreamHandler()
117 console.setLevel(logging_level)
118 formatter: logging.Formatter = MDCFormatter(
119 fmt="%(asctime)s:[%(name)s] %(created)f %(module)s %(funcName)s %(pathname)s %(process)d %(levelno)s :[ %(threadName)s %(thread)d]: [%(mdc)s]: [%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s", # noqa
120 mdcfmt="{RequestID} {InvocationID} {ServiceName} {PartnerName} {BeginTimestamp} {EndTimestamp} {ElapsedTime} {StatusCode} {TargetEntity} {TargetServiceName} {Server}", # noqa
121 # Important: We cannot use %f here because this datetime format is used by time library, not datetime.
122 datefmt="%Y-%m-%dT%H:%M:%S%z",
123 )
124 console.setFormatter(formatter)
125
126 def get_logger(name: str) -> Logger:
127 """Get a new logger with predefined MDC handler."""
128 logger: Logger = logging.getLogger(name)
129 logger.addHandler(console)
130 return logger
131
132 return get_logger
133
134
135get_logger = prepare_logger(
136 config.artifact_manager_property("logFile"),
137 strtobool(config["artifactManagerServer"].get("debug", "false")),
138 config,
139)