Files
superset2/superset/stats_logger.py
2025-09-15 12:42:49 -07:00

132 lines
4.5 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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 logging
from typing import Any, Optional
from colorama import Fore, Style
logger = logging.getLogger(__name__)
class BaseStatsLogger:
"""Base class for logging realtime events"""
def __init__(self, prefix: str = "superset") -> None:
self.prefix = prefix
def key(self, key: str) -> str:
if self.prefix:
return self.prefix + key
return key
def incr(self, key: str) -> None:
"""Increment a counter"""
raise NotImplementedError()
def decr(self, key: str) -> None:
"""Decrement a counter"""
raise NotImplementedError()
def timing(self, key: str, value: float) -> None:
raise NotImplementedError()
def gauge(self, key: str, value: float) -> None:
"""Setup a gauge"""
raise NotImplementedError()
class DummyStatsLogger(BaseStatsLogger):
def incr(self, key: str) -> None:
logger.debug("%s[stats_logger] (incr) %s%s", Fore.CYAN, key, Style.RESET_ALL)
def decr(self, key: str) -> None:
logger.debug("%s[stats_logger] (decr) %s%s", Fore.CYAN, key, Style.RESET_ALL)
def timing(self, key: str, value: float) -> None:
logger.debug(
"%s[stats_logger] (timing) %s | %s %s",
Fore.CYAN,
key,
value,
Style.RESET_ALL,
)
def gauge(self, key: str, value: float) -> None:
logger.debug(
"%s[stats_logger] (gauge) %s%s%s", Fore.CYAN, key, value, Style.RESET_ALL
)
try:
from statsd import StatsClient
class StatsdStatsLogger(BaseStatsLogger):
def __init__( # pylint: disable=super-init-not-called
self,
host: str = "localhost",
port: int = 8125,
prefix: str = "superset",
statsd_client: Optional[StatsClient] = None,
) -> None:
"""
Initializes from either params or a supplied, pre-constructed statsd client.
If statsd_client argument is given, all other arguments are ignored and the
supplied client will be used to emit metrics.
"""
if statsd_client:
self.client = statsd_client
else:
self.client = StatsClient(host=host, port=port, prefix=prefix)
def incr(self, key: str) -> None:
self.client.incr(key)
def decr(self, key: str) -> None:
self.client.decr(key)
def timing(self, key: str, value: float) -> None:
self.client.timing(key, value)
def gauge(self, key: str, value: float) -> None:
self.client.gauge(key, value)
except Exception as e: # pylint: disable=broad-except # noqa: S110
# e can only be accessed in the catch and not later during class instantiation.
# We have to save it to a separate variable.
_saved_exception = e
class StatsdStatsLogger(BaseStatsLogger): # type:ignore[no-redef] # the redefinition only happens when the original definition failed
def __init__( # pylint: disable=super-init-not-called
self,
host: str = "localhost",
port: int = 8125,
prefix: str = "superset",
statsd_client: Any = None,
) -> None:
"""
Initializes from either params or a supplied, pre-constructed statsd client.
If statsd_client argument is given, all other arguments are ignored and the
supplied client will be used to emit metrics.
If an exception is raised while creating the StatsdStatsLogger class, for
example because the statsd package is not installed, it will be re-raised
on instantiation of the StatsdStatsLogger.
"""
raise _saved_exception