-
Notifications
You must be signed in to change notification settings - Fork 7k
[Serve] log deployment config in controller logs #59222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces logging for deployment configurations in Ray Serve, which is a valuable addition for debugging. The new _log_deployment_configs function logs declarative, imperative, and merged configurations. My review focuses on improving the implementation of this new function for better maintainability and readability by reducing code duplication.
| def _log_deployment_configs( | ||
| app_name: str, | ||
| declarative_config: ServeApplicationSchema, | ||
| imperative_configs: Dict[str, DeploymentInfo], | ||
| merged_configs: Dict[str, DeploymentInfo], | ||
| ) -> None: | ||
| """Log the declarative, imperative, and merged deployment configs. | ||
| This function logs the deployment configuration after each update to help | ||
| with debugging and understanding how declarative (from YAML/config) and | ||
| imperative (from code) configurations are merged. | ||
| Args: | ||
| app_name: Name of the application. | ||
| declarative_config: The declarative config from the YAML/config file. | ||
| imperative_configs: The imperative configs from the application code. | ||
| merged_configs: The final merged configs after applying overrides. | ||
| """ | ||
| try: | ||
| # Format declarative config | ||
| declarative_dict = {} | ||
| if declarative_config.deployments: | ||
| for dep in declarative_config.deployments: | ||
| dep_dict = dep.dict(exclude_unset=True) if hasattr(dep, "dict") else {} | ||
| declarative_dict[dep.name] = dep_dict | ||
|
|
||
| # Format imperative configs | ||
| imperative_dict = {} | ||
| for dep_name, dep_info in imperative_configs.items(): | ||
| imperative_dict[dep_name] = { | ||
| "deployment_config": dep_info.deployment_config.dict(), | ||
| "replica_config": { | ||
| "ray_actor_options": dep_info.replica_config.ray_actor_options, | ||
| "placement_group_bundles": dep_info.replica_config.placement_group_bundles, | ||
| "placement_group_strategy": dep_info.replica_config.placement_group_strategy, | ||
| "max_replicas_per_node": dep_info.replica_config.max_replicas_per_node, | ||
| }, | ||
| "route_prefix": dep_info.route_prefix, | ||
| "version": dep_info.version, | ||
| } | ||
|
|
||
| # Format merged configs | ||
| merged_dict = {} | ||
| for dep_name, dep_info in merged_configs.items(): | ||
| merged_dict[dep_name] = { | ||
| "deployment_config": dep_info.deployment_config.dict(), | ||
| "replica_config": { | ||
| "ray_actor_options": dep_info.replica_config.ray_actor_options, | ||
| "placement_group_bundles": dep_info.replica_config.placement_group_bundles, | ||
| "placement_group_strategy": dep_info.replica_config.placement_group_strategy, | ||
| "max_replicas_per_node": dep_info.replica_config.max_replicas_per_node, | ||
| }, | ||
| "route_prefix": dep_info.route_prefix, | ||
| "version": dep_info.version, | ||
| } | ||
|
|
||
| # Log the configurations | ||
| logger.info( | ||
| f"Application '{app_name}' deployment configs after update:\n" | ||
| f"Declarative config (from YAML/config): {json.dumps(declarative_dict, indent=2, default=str)}\n" | ||
| f"Imperative config (from code): {json.dumps(imperative_dict, indent=2, default=str)}\n" | ||
| f"Merged config (final): {json.dumps(merged_dict, indent=2, default=str)}" | ||
| ) | ||
| except Exception as e: | ||
| # Don't fail the deployment if logging fails | ||
| logger.warning( | ||
| f"Failed to log deployment configs for application '{app_name}': {e}" | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _log_deployment_configs function can be refactored for better readability and to reduce code duplication.
- The logic for formatting
imperative_configsandmerged_configsis identical. This can be extracted into a nested helper function,_format_deployment_infos, to avoid repetition. - The creation of
declarative_dictcan be simplified using a dictionary comprehension. Thehasattr(dep, "dict")check is also redundant becausedeclarative_config.deploymentsis a list ofDeploymentSchemapydantic models, which are guaranteed to have a.dict()method.
Here is the suggested refactoring:
def _log_deployment_configs(
app_name: str,
declarative_config: ServeApplicationSchema,
imperative_configs: Dict[str, DeploymentInfo],
merged_configs: Dict[str, DeploymentInfo],
) -> None:
"""Log the declarative, imperative, and merged deployment configs.
This function logs the deployment configuration after each update to help
with debugging and understanding how declarative (from YAML/config) and
imperative (from code) configurations are merged.
Args:
app_name: Name of the application.
declarative_config: The declarative config from the YAML/config file.
imperative_configs: The imperative configs from the application code.
merged_configs: The final merged configs after applying overrides.
"""
try:
def _format_deployment_infos(infos: Dict[str, DeploymentInfo]) -> Dict[str, Dict]:
"""Helper to format DeploymentInfo objects for logging."""
return {
dep_name: {
"deployment_config": dep_info.deployment_config.dict(),
"replica_config": {
"ray_actor_options": dep_info.replica_config.ray_actor_options,
"placement_group_bundles": dep_info.replica_config.placement_group_bundles,
"placement_group_strategy": dep_info.replica_config.placement_group_strategy,
"max_replicas_per_node": dep_info.replica_config.max_replicas_per_node,
},
"route_prefix": dep_info.route_prefix,
"version": dep_info.version,
}
for dep_name, dep_info in infos.items()
}
# Format declarative config
declarative_dict = {
dep.name: dep.dict(exclude_unset=True)
for dep in declarative_config.deployments
} if declarative_config.deployments else {}
# Format imperative and merged configs
imperative_dict = _format_deployment_infos(imperative_configs)
merged_dict = _format_deployment_infos(merged_configs)
# Log the configurations
logger.info(
f"Application '{app_name}' deployment configs after update:\n"
f"Declarative config (from YAML/config): {json.dumps(declarative_dict, indent=2, default=str)}\n"
f"Imperative config (from code): {json.dumps(imperative_dict, indent=2, default=str)}\n"
f"Merged config (final): {json.dumps(merged_dict, indent=2, default=str)}"
)
except Exception as e:
# Don't fail the deployment if logging fails
logger.warning(
f"Failed to log deployment configs for application '{app_name}': {e}"
)
abrarsheikh
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes seem overly complex for a simple task like logging config. Is there a architectural reason why logging just the deployment config is difficult?
Thanks for the feedback. Indeed it looks complex, but this is caused by architectural limitations. |
Description
[Serve] log deployment config in controller logs
Related issues
#59167
Additional information