blob: 0a8df36d9f1ca2d19b41fcdcbb1e8d8b3ed971c3 [file] [log] [blame]
#
# Copyright 2020 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import structlog
from pyvoltha.adapters.common.kvstore.twisted_etcd_store import TwistedEtcdStore
from pyvoltha.common.structlog_setup import setup_logging, update_logging, string_to_int
from twisted.internet.defer import inlineCallbacks, returnValue
COMPONENT_NAME = os.environ.get("COMPONENT_NAME")
GLOBAL_CONFIG_ROOT_NODE = "global"
DEFAULT_KV_STORE_CONFIG_PATH = "config"
KV_STORE_DATA_PATH_PREFIX = "service/voltha"
KV_STORE_PATH_SEPARATOR = "/"
CONFIG_TYPE = "loglevel"
DEFAULT_PACKAGE_NAME = "default"
GLOBAL_DEFAULT_LOGLEVEL = "WARN"
class LogController():
instance_id = None
active_log_level = None
def __init__(self, etcd_host, etcd_port):
self.log = structlog.get_logger()
self.etcd_host = etcd_host
self.etcd_port = etcd_port
self.etcd_client = TwistedEtcdStore(self.etcd_host, self.etcd_port, KV_STORE_DATA_PATH_PREFIX)
def make_config_path(self, key):
return (DEFAULT_KV_STORE_CONFIG_PATH + KV_STORE_PATH_SEPARATOR + key + KV_STORE_PATH_SEPARATOR + CONFIG_TYPE + KV_STORE_PATH_SEPARATOR + DEFAULT_PACKAGE_NAME)
@inlineCallbacks
def get_global_loglevel(self):
global_default_loglevel = ""
try:
level = yield self.etcd_client.get(self.global_config_path)
if level is not None:
level_int = string_to_int(str(level, 'utf-8'))
if level_int == 0:
self.log.warn("Unsupported loglevel at global config path", level)
else:
global_default_loglevel = level
self.log.debug("Retrieved global default loglevel", global_default_loglevel)
except KeyError:
self.log.warn("Failed to retrive default global loglevel")
returnValue(global_default_loglevel)
@inlineCallbacks
def get_component_loglevel(self, global_default_loglevel):
component_default_loglevel = global_default_loglevel
try:
level = yield self.etcd_client.get(self.component_config_path)
if level is not None:
level_int = string_to_int(str(level, 'utf-8'))
if level_int == 0:
self.log.warn("Unsupported loglevel at component config path", level)
else:
component_default_loglevel = level
self.log.debug("Retrieved component default loglevel", component_default_loglevel)
except KeyError:
self.log.warn("Failed to retrive default component loglevel")
if component_default_loglevel == "":
component_default_loglevel = GLOBAL_DEFAULT_LOGLEVEL.encode('utf-8')
returnValue(component_default_loglevel)
@inlineCallbacks
def start_watch_log_config_change(self, instance_id, initial_default_loglevel):
self.log.debug("Start watching for log config change")
LogController.instance_id = instance_id
if COMPONENT_NAME == None:
raise Exception("Unable to retrive pod component name from runtime env")
self.global_config_path = self.make_config_path(GLOBAL_CONFIG_ROOT_NODE)
self.component_config_path = self.make_config_path(COMPONENT_NAME)
self.set_default_loglevel(self.global_config_path, self.component_config_path, initial_default_loglevel.upper())
self.process_log_config_change()
yield self.etcd_client.watch(self.global_config_path, self.watch_callback)
yield self.etcd_client.watch(self.component_config_path, self.watch_callback)
def watch_callback(self, event):
self.process_log_config_change()
@inlineCallbacks
def process_log_config_change(self):
self.log.debug("Processing log config change")
global_default_level = yield self.get_global_loglevel()
level = yield self.get_component_loglevel(global_default_level)
level_int = string_to_int(str(level, 'utf-8'))
current_log_level = level_int
if LogController.active_log_level != current_log_level:
LogController.active_log_level = current_log_level
self.log.debug("Applying updated loglevel")
update_logging(LogController.instance_id, None, verbosity_adjust=level_int)
else:
self.log.debug("Loglevel not updated")
@inlineCallbacks
def set_default_loglevel(self, global_config_path, component_config_path, initial_default_loglevel):
if (yield self.etcd_client.get(global_config_path)) == None:
yield self.etcd_client.set(global_config_path, GLOBAL_DEFAULT_LOGLEVEL)
if (yield self.etcd_client.get(component_config_path)) == None:
yield self.etcd_client.set(component_config_path, initial_default_loglevel)