Unverified Commit 974201c8 authored by ishandhanani's avatar ishandhanani Committed by GitHub
Browse files

refactor: move logging config to runtime (#863)

parent 566068dc
......@@ -23,9 +23,9 @@ from typing import Any, Dict, List, Optional
from circus.client import CircusClient
from circus.exc import CallError
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
configure_server_logging()
configure_dynamo_logging()
logger = logging.getLogger(__name__)
......
......@@ -25,9 +25,9 @@ import filelock
from dynamo.planner.circusd import CircusController
from dynamo.planner.planner_connector import PlannerConnector
from dynamo.runtime import DistributedRuntime
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
configure_server_logging()
configure_dynamo_logging()
logger = logging.getLogger(__name__)
......
......@@ -35,14 +35,14 @@ from bentoml.exceptions import BentoMLException, CLIException, CloudRESTApiClien
from rich.console import Console
from simple_di import Provide, inject
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
from .utils import resolve_service_config
# Configure logging to suppress INFO HTTP logs
logging.getLogger("httpx").setLevel(logging.WARNING) # HTTP client library logs
logging.getLogger("httpcore").setLevel(logging.WARNING) # HTTP core library logs
configure_server_logging()
configure_dynamo_logging()
logger = logging.getLogger(__name__)
......
......@@ -91,9 +91,8 @@ def serve(
Starts a local server for the specified Dynamo pipeline.
"""
# Warning: internal
from dynamo.runtime.logging import configure_dynamo_logging
from dynamo.sdk.lib.loader import find_and_load_service
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.sdk.lib.service import LinkedServices
# Extract extra arguments not captured by typer
......@@ -119,7 +118,7 @@ def serve(
console.print(f"DYNAMO_SERVICE_CONFIG={json.dumps(service_configs)}")
raise typer.Exit()
configure_server_logging()
configure_dynamo_logging()
if service_configs:
logger.info(f"Running dynamo serve with service configs {service_configs}")
......
......@@ -146,7 +146,7 @@ def main(
from bentoml._internal.container import BentoMLContainer
from bentoml._internal.context import server_context
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
# Setup signal handlers for graceful shutdown
setup_signal_handlers()
......@@ -172,7 +172,7 @@ def main(
if service_name and service_name != service.name:
service = service.find_dependent_by_name(service_name)
configure_server_logging(service_name=service_name, worker_id=worker_id)
configure_dynamo_logging(service_name=service_name, worker_id=worker_id)
if runner_map:
BentoMLContainer.remote_runner_mapping.set(
t.cast(t.Dict[str, str], json.loads(runner_map))
......
......@@ -131,13 +131,13 @@ def serve_dynamo_graph(
service_name: str = "",
enable_local_planner: bool = False,
) -> CircusRunner:
from dynamo.runtime.logging import configure_dynamo_logging
from dynamo.sdk.cli.circus import create_arbiter, create_circus_watcher
from dynamo.sdk.lib.loader import find_and_load_service
from dynamo.sdk.lib.logging import configure_server_logging
from .allocator import ResourceAllocator
configure_server_logging(service_name=service_name)
configure_dynamo_logging(service_name=service_name)
bento_id: str = ""
namespace: str = ""
......
......@@ -31,9 +31,9 @@ import click
import yaml
from click import Command, Context
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
configure_server_logging()
configure_dynamo_logging()
logger = logging.getLogger(__name__)
......
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# 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 json
import logging
import logging.config
import os
import tempfile
from dynamo.runtime.logging import configure_logger as configure_dynamo_logger
def configure_server_logging(
service_name: str | None = None, worker_id: int | None = None
):
"""
A single place to configure logging for Dynamo.
"""
# First, remove any existing handlers to avoid duplication
root_logger = logging.getLogger()
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# Configure the logger with Dynamo's handler
configure_dynamo_logger(service_name, worker_id)
# map the DYN_LOG variable to a logging level
dyn_var = os.environ.get("DYN_LOG", "info")
dyn_level = log_level_mapping(dyn_var)
configure_vllm_logging(dyn_level)
# loggers that should be configured to ERROR
error_loggers = ["bentoml", "tag"]
for logger_name in error_loggers:
logger = logging.getLogger(logger_name)
logger.handlers = []
logger.setLevel(logging.ERROR)
logger.propagate = True
def log_level_mapping(level: str) -> int:
"""
The DYN_LOG variable is set using "debug" or "trace" or "info.
This function maps those to the appropriate logging level and defaults to INFO
if the variable is not set or a bad value.
"""
if level == "debug":
return logging.DEBUG
elif level == "info":
return logging.INFO
elif level == "warn" or level == "warning":
return logging.WARNING
elif level == "error":
return logging.ERROR
elif level == "critical":
return logging.CRITICAL
elif level == "trace":
return logging.INFO
else:
return logging.INFO
def configure_vllm_logging(dyn_level: int):
"""
vLLM requires a logging config file to be set in the environment.
This function creates a temporary file with the VLLM logging config and sets the
VLLM_LOGGING_CONFIG_PATH environment variable to the path of the file.
"""
os.environ["VLLM_CONFIGURE_LOGGING"] = "1"
vllm_level = logging.getLevelName(dyn_level)
# Create a temporary config file for VLLM
vllm_config = {
"formatters": {"simple": {"format": "%(message)s"}},
"handlers": {
"dynamo": {
"class": "dynamo.runtime.logging.LogHandler",
"formatter": "simple",
"level": vllm_level,
}
},
"loggers": {
"vllm": {"handlers": ["dynamo"], "level": vllm_level, "propagate": False}
},
"version": 1,
"disable_existing_loggers": False,
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(vllm_config, f)
os.environ["VLLM_LOGGING_CONFIG_PATH"] = f.name
......@@ -19,9 +19,9 @@ from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from dynamo.runtime.logging import configure_dynamo_logging
from dynamo.sdk import DYNAMO_IMAGE, depends, dynamo_endpoint, service
from dynamo.sdk.lib.config import ServiceConfig
from dynamo.sdk.lib.logging import configure_server_logging
logger = logging.getLogger(__name__)
......@@ -119,7 +119,7 @@ class Frontend:
def __init__(self) -> None:
# Configure logging
configure_server_logging(service_name="Frontend")
configure_dynamo_logging(service_name="Frontend")
logger.info("Starting frontend")
config = ServiceConfig.get_instance()
......
......@@ -32,9 +32,9 @@ from utils.prefill_queue import PrefillQueue
from dynamo.llm import KvMetricsAggregator
from dynamo.planner import LocalConnector
from dynamo.runtime import DistributedRuntime, dynamo_worker
from dynamo.sdk.lib.logging import configure_server_logging
from dynamo.runtime.logging import configure_dynamo_logging
configure_server_logging()
configure_dynamo_logging()
logger = logging.getLogger(__name__)
# will not decrease decode worker number within 3 adjustment interval after a new decode worker
......
......@@ -13,7 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import logging
import os
import tempfile
from dynamo._core import log_message
......@@ -69,3 +72,86 @@ def construct_formatter_prefix(service_name: str | None, worker_id: int | None)
tmp += f":{worker_id}"
return tmp.strip()
def configure_dynamo_logging(
service_name: str | None = None, worker_id: int | None = None
):
"""
A single place to configure logging for Dynamo.
"""
# First, remove any existing handlers to avoid duplication
root_logger = logging.getLogger()
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# Configure the logger with Dynamo's handler
configure_logger(service_name, worker_id)
# map the DYN_LOG variable to a logging level
dyn_var = os.environ.get("DYN_LOG", "info")
dyn_level = log_level_mapping(dyn_var)
configure_vllm_logging(dyn_level)
# loggers that should be configured to ERROR
error_loggers = ["bentoml", "tag"]
for logger_name in error_loggers:
logger = logging.getLogger(logger_name)
logger.handlers = []
logger.setLevel(logging.ERROR)
logger.propagate = True
def log_level_mapping(level: str) -> int:
"""
The DYN_LOG variable is set using "debug" or "trace" or "info.
This function maps those to the appropriate logging level and defaults to INFO
if the variable is not set or a bad value.
"""
if level == "debug":
return logging.DEBUG
elif level == "info":
return logging.INFO
elif level == "warn" or level == "warning":
return logging.WARNING
elif level == "error":
return logging.ERROR
elif level == "critical":
return logging.CRITICAL
elif level == "trace":
return logging.INFO
else:
return logging.INFO
def configure_vllm_logging(dyn_level: int):
"""
vLLM requires a logging config file to be set in the environment.
This function creates a temporary file with the VLLM logging config and sets the
VLLM_LOGGING_CONFIG_PATH environment variable to the path of the file.
"""
os.environ["VLLM_CONFIGURE_LOGGING"] = "1"
vllm_level = logging.getLevelName(dyn_level)
# Create a temporary config file for VLLM
vllm_config = {
"formatters": {"simple": {"format": "%(message)s"}},
"handlers": {
"dynamo": {
"class": "dynamo.runtime.logging.LogHandler",
"formatter": "simple",
"level": vllm_level,
}
},
"loggers": {
"vllm": {"handlers": ["dynamo"], "level": vllm_level, "propagate": False}
},
"version": 1,
"disable_existing_loggers": False,
}
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
json.dump(vllm_config, f)
os.environ["VLLM_LOGGING_CONFIG_PATH"] = f.name
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment