diff --git a/Logger/LokiLogger.py b/Logger/LokiLogger.py index de4ce96..5360a36 100644 --- a/Logger/LokiLogger.py +++ b/Logger/LokiLogger.py @@ -26,14 +26,14 @@ class LokiLogger: def __init__( self, endpoint: str = "http://127.0.0.1:3101/loki/api/v1/push", - default_labels: Optional[Dict[str, str]] = None, + add_labels: Optional[Dict[str, str]] = None, timeout: float = 5.0, min_level: str = "DEBUG", service_name: Optional[str] = None, ): """ :param endpoint: URL completa del endpoint de Loki / Alloy. - :param default_labels: etiquetas estáticas comunes (ej: {"env": "dev"}) + :param add_labels: etiquetas adicionales comunes (ej: {"env": "dev"}) :param timeout: timeout en segundos para la petición HTTP :param min_level: nivel mínimo para enviar logs :param service_name: nombre del servicio (usado como 'service_name' label) @@ -47,9 +47,8 @@ class LokiLogger: raise ValueError(f"min_level debe estar en {self.ALLOWED_LEVELS}") self.min_level = min_level - # 🔹 Labels base (solo service_name + default_labels) - self.default_labels = dict(default_labels or {}) - self.default_labels["service_name"] = self.service_name + # 🔹 Labels adicionales del constructor + self.add_labels = dict(add_labels or {}) self._level_order = { "TRACE": 0, @@ -74,8 +73,8 @@ class LokiLogger: self, level: str, message: str, - labels: Optional[Dict[str, str]] = None, - metadata: Optional[Dict[str, Any]] = None, # mantenemos metadata por compatibilidad + add_labels: Optional[Dict[str, str]] = None, + add_fields: Optional[Dict[str, Any]] = None, ) -> None: """Envía un log a Loki con los campos mínimos (timestamp + message).""" level = level.upper() @@ -88,24 +87,31 @@ class LokiLogger: if not self._should_log(level): return - # 🔸 Construimos labels: base + detected_level + custom + # 🔸 Construimos labels: solo 2 labels por defecto final_labels = { "service_name": self.service_name, "detected_level": level, } - # Agrega default_labels (si existen) - final_labels.update(self.default_labels) + # Agrega labels adicionales del constructor + if self.add_labels: + final_labels.update(self.add_labels) - # Agrega labels personalizados - if labels: - final_labels.update(labels) + # Agrega labels adicionales del método + if add_labels: + final_labels.update(add_labels) - # 🧾 El log line solo lleva timestamp y message - log_line = json.dumps({ + # 🧾 El log line lleva timestamp + message + campos adicionales + log_line_data = { "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "message": message, - }) + } + + # Agrega campos adicionales si se pasan + if add_fields: + log_line_data.update(add_fields) + + log_line = json.dumps(log_line_data) body = { "streams": [ @@ -120,38 +126,38 @@ class LokiLogger: print(f"Failed to send log to Loki: {e}", flush=True) # Métodos estándar por nivel - def trace(self, message, labels=None, metadata=None): - self.log("TRACE", message, labels, metadata) + def trace(self, message, add_labels=None, add_fields=None): + self.log("TRACE", message, add_labels, add_fields) - def debug(self, message, labels=None, metadata=None): - self.log("DEBUG", message, labels, metadata) + def debug(self, message, add_labels=None, add_fields=None): + self.log("DEBUG", message, add_labels, add_fields) - def info(self, message, labels=None, metadata=None): - self.log("INFO", message, labels, metadata) + def info(self, message, add_labels=None, add_fields=None): + self.log("INFO", message, add_labels, add_fields) - def warn(self, message, labels=None, metadata=None): - self.log("WARN", message, labels, metadata) + def warn(self, message, add_labels=None, add_fields=None): + self.log("WARN", message, add_labels, add_fields) - def warning(self, message, labels=None, metadata=None): - self.log("WARN", message, labels, metadata) + def warning(self, message, add_labels=None, add_fields=None): + self.log("WARN", message, add_labels, add_fields) - def error(self, message, labels=None, metadata=None): - self.log("ERROR", message, labels, metadata) + def error(self, message, add_labels=None, add_fields=None): + self.log("ERROR", message, add_labels, add_fields) - def exception(self, exc: Exception, labels=None, metadata=None): + def exception(self, exc: Exception, add_labels=None, add_fields=None): """Registra una excepción con traceback incluido.""" tb = traceback.format_exc() message = str(exc) - self.log("ERROR", f"{message}\n{tb}", labels=labels) + self.log("ERROR", f"{message}\n{tb}", add_labels=add_labels, add_fields=add_fields) - def fatal(self, message, labels=None, metadata=None): - self.log("FATAL", message, labels, metadata) + def fatal(self, message, add_labels=None, add_fields=None): + self.log("FATAL", message, add_labels, add_fields) - def critical(self, message, labels=None, metadata=None): - self.log("CRITICAL", message, labels, metadata) + def critical(self, message, add_labels=None, add_fields=None): + self.log("CRITICAL", message, add_labels, add_fields) - def unknown(self, message, labels=None, metadata=None): - self.log("UNKNOWN", message, labels, metadata) + def unknown(self, message, add_labels=None, add_fields=None): + self.log("UNKNOWN", message, add_labels, add_fields) # 🧩 Decorador para capturar excepciones def catch_exceptions(self, reraise: bool = False): diff --git a/config/loki/loki.yaml b/config/loki/loki.yaml index 286f75b..b8beaf7 100644 --- a/config/loki/loki.yaml +++ b/config/loki/loki.yaml @@ -36,6 +36,9 @@ schema_config: ruler: alertmanager_url: http://localhost:9093 +limits_config: + discover_log_levels: false + # By default, Loki will send anonymous, but uniquely-identifiable usage and configuration # analytics to Grafana Labs. These statistics are sent to https://stats.grafana.org/ # diff --git a/main.py b/main.py index 3c7b649..65602c6 100644 --- a/main.py +++ b/main.py @@ -4,21 +4,31 @@ from Logger.LokiLogger import LokiLogger def prueba_log(): logger = LokiLogger( - default_labels={"job": "prueba_ejemplo", - # "env": "production" - }, + service_name="suite_logs_example_3", + # add_labels={ + # "job": "log_testing", + # "env": "development", + # "component": "main" + # }, min_level="DEBUG" ) - - - logger.trace("Inicio del proceso de ETL", service="etl") - logger.debug("Carga de datos completada", service="etl") - logger.info("Pipeline ejecutado correctamente", service="etl") - logger.warn("Latencia superior a lo esperado", service="etl", metadata={"latency_ms": 850}) - logger.error("Error al conectar con base de datos", service="etl", metadata={"db_host": "postgres"}) - logger.fatal("Fallo crítico en nodo principal", service="etl") - logger.critical("Memoria insuficiente para procesamiento", service="etl") - logger.unknown("Log sin nivel detectado", service="etl") + + try: + logger.info("Iniciando pruebas del sistema de logging") + + logger.trace("Inicio del proceso de ETL") + logger.debug("Carga de datos completada") + logger.info("Pipeline ejecutado correctamente") + logger.warn("Latencia superior a lo esperado") + logger.error("Error al conectar con base de datos") + logger.fatal("Fallo crítico en nodo principal") + logger.critical("Memoria insuficiente para procesamiento") + logger.unknown("Log sin nivel detectado") + + logger.info("Pruebas de logging completadas exitosamente") + + except Exception as e: + logger.exception(e) if __name__ == "__main__":