runbooks 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -59,7 +59,7 @@ __all__ = [
59
59
  "configure_enterprise_logging",
60
60
  # Security
61
61
  "get_enhanced_logger",
62
- "assess_vpc_security_posture",
62
+ "assess_vpc_security_posture",
63
63
  "validate_compliance_requirements",
64
64
  "evaluate_security_baseline",
65
65
  "classify_security_risk",
@@ -17,36 +17,37 @@ from typing import Any, Dict, List, Optional, Union, Literal
17
17
 
18
18
  try:
19
19
  from loguru import logger as loguru_logger
20
+
20
21
  _HAS_LOGURU = True
21
22
  except ImportError:
22
23
  # Create a mock loguru-like object for compatibility
23
24
  import logging
24
-
25
+
25
26
  class MockLoguru:
26
27
  def __init__(self):
27
28
  self.logger = logging.getLogger(__name__)
28
-
29
+
29
30
  def remove(self, *args, **kwargs):
30
31
  pass # No-op for standard logging
31
-
32
+
32
33
  def add(self, *args, **kwargs):
33
34
  pass # No-op for standard logging
34
-
35
+
35
36
  def bind(self, **kwargs):
36
37
  return self # Return self for chaining
37
-
38
+
38
39
  def info(self, message, *args, **kwargs):
39
40
  self.logger.info(message, *args)
40
-
41
+
41
42
  def warning(self, message, *args, **kwargs):
42
43
  self.logger.warning(message, *args)
43
-
44
+
44
45
  def error(self, message, *args, **kwargs):
45
46
  self.logger.error(message, *args)
46
-
47
+
47
48
  def debug(self, message, *args, **kwargs):
48
49
  self.logger.debug(message, *args)
49
-
50
+
50
51
  loguru_logger = MockLoguru()
51
52
  _HAS_LOGURU = False
52
53
 
@@ -58,6 +59,7 @@ try:
58
59
  from rich.panel import Panel
59
60
  from rich.text import Text
60
61
  from rich import box
62
+
61
63
  _HAS_RICH = True
62
64
  except ImportError:
63
65
  _HAS_RICH = False
@@ -72,7 +74,7 @@ class EnterpriseRichLogger:
72
74
  def __init__(
73
75
  self,
74
76
  name: str = "runbooks",
75
- level: str = "INFO",
77
+ level: str = "INFO",
76
78
  log_dir: Optional[Path] = None,
77
79
  enable_console: bool = True,
78
80
  enable_file: bool = True,
@@ -100,17 +102,17 @@ class EnterpriseRichLogger:
100
102
  self.log_dir = log_dir or Path.home() / ".runbooks" / "logs"
101
103
  self.correlation_id = correlation_id or self._generate_correlation_id()
102
104
  self.json_output = json_output
103
-
105
+
104
106
  # Initialize Rich console for beautiful output
105
107
  self.console = rich_console or (Console() if _HAS_RICH else None)
106
-
108
+
107
109
  # User-type specific styling
108
110
  self.level_styles = {
109
111
  "DEBUG": {"style": "dim white", "icon": "🔧", "label": "TECH"},
110
112
  "INFO": {"style": "cyan", "icon": "ℹ️", "label": "INFO"},
111
113
  "WARNING": {"style": "yellow bold", "icon": "⚠️", "label": "BIZ"},
112
114
  "ERROR": {"style": "red bold", "icon": "❌", "label": "ALL"},
113
- "CRITICAL": {"style": "red bold reverse", "icon": "🚨", "label": "ALL"}
115
+ "CRITICAL": {"style": "red bold reverse", "icon": "🚨", "label": "ALL"},
114
116
  }
115
117
 
116
118
  # Ensure log directory exists
@@ -222,18 +224,20 @@ class EnterpriseRichLogger:
222
224
  """Determine if message should be logged based on current log level."""
223
225
  level_hierarchy = {"DEBUG": 0, "INFO": 1, "WARNING": 2, "ERROR": 3, "CRITICAL": 4}
224
226
  return level_hierarchy.get(level, 0) >= level_hierarchy.get(self.level, 1)
225
-
226
- def _rich_log(self, level: str, message: str, details: Optional[Dict[str, Any]] = None, progress: Optional[Progress] = None) -> None:
227
+
228
+ def _rich_log(
229
+ self, level: str, message: str, details: Optional[Dict[str, Any]] = None, progress: Optional[Progress] = None
230
+ ) -> None:
227
231
  """Enhanced logging with Rich CLI formatting and user-type specific content."""
228
232
  if not self._should_log(level):
229
233
  return
230
-
234
+
231
235
  # Get level-specific styling
232
236
  level_config = self.level_styles.get(level, self.level_styles["INFO"])
233
237
  icon = level_config["icon"]
234
238
  style = level_config["style"]
235
239
  label = level_config["label"]
236
-
240
+
237
241
  # Handle JSON output for programmatic use
238
242
  if self.json_output:
239
243
  log_entry = {
@@ -241,35 +245,39 @@ class EnterpriseRichLogger:
241
245
  "level": level,
242
246
  "correlation_id": self.correlation_id,
243
247
  "message": message,
244
- "details": details or {}
248
+ "details": details or {},
245
249
  }
246
250
  if self.console:
247
251
  self.console.print_json(data=log_entry)
248
252
  else:
249
253
  print(json.dumps(log_entry))
250
254
  return
251
-
255
+
252
256
  # Rich console output with user-type specific formatting
253
257
  if self.console and _HAS_RICH:
254
258
  timestamp = datetime.now().strftime("%H:%M:%S")
255
-
259
+
256
260
  # User-type specific message formatting
257
261
  if level == "DEBUG":
258
262
  # Tech users - Full details with timing and API info
259
263
  self.console.print(f"[dim]{timestamp}[/] {icon} [bold]{label}[/] {message}")
260
264
  if details and "aws_api" in details and details["aws_api"]:
261
265
  api_details = details["aws_api"]
262
- self.console.print(f" └─ [dim]API: {api_details.get('service', 'unknown')} / {api_details.get('operation', 'unknown')}[/]")
266
+ self.console.print(
267
+ f" └─ [dim]API: {api_details.get('service', 'unknown')} / {api_details.get('operation', 'unknown')}[/]"
268
+ )
263
269
  if details.get("request_id"):
264
270
  self.console.print(f" └─ [dim]Request ID: {details['request_id']}[/]")
265
271
  if details.get("duration"):
266
- duration_color = "green" if details["duration"] < 1.0 else "yellow" if details["duration"] < 5.0 else "red"
272
+ duration_color = (
273
+ "green" if details["duration"] < 1.0 else "yellow" if details["duration"] < 5.0 else "red"
274
+ )
267
275
  self.console.print(f" └─ [dim]Duration: [{duration_color}]{details['duration']:.3f}s[/dim][/]")
268
276
  if details.get("memory_usage"):
269
277
  memory_mb = details["memory_usage"] / 1024 / 1024
270
278
  memory_color = "green" if memory_mb < 50 else "yellow" if memory_mb < 200 else "red"
271
279
  self.console.print(f" └─ [dim]Memory: [{memory_color}]{memory_mb:.1f}MB[/dim][/]")
272
-
280
+
273
281
  elif level == "INFO":
274
282
  # Standard users - Clean status with progress bars
275
283
  if progress:
@@ -282,27 +290,37 @@ class EnterpriseRichLogger:
282
290
  status_color = "green" if details["operation_status"] == "completed" else "yellow"
283
291
  info_text += f" [{status_color}][{details['operation_status']}][/]"
284
292
  self.console.print(info_text)
285
-
293
+
286
294
  elif level == "WARNING":
287
295
  # Business users - Recommendations and alerts
288
296
  self.console.print(f"{icon} [bold]{label}[/] [{style}]{message}[/]")
289
297
  if details.get("recommendation"):
290
298
  self.console.print(f" 💡 [bright_cyan]Recommendation:[/] {details['recommendation']}")
291
299
  if details.get("cost_impact"):
292
- impact_color = "red" if details["cost_impact"] > 1000 else "yellow" if details["cost_impact"] > 100 else "green"
300
+ impact_color = (
301
+ "red"
302
+ if details["cost_impact"] > 1000
303
+ else "yellow"
304
+ if details["cost_impact"] > 100
305
+ else "green"
306
+ )
293
307
  self.console.print(f" 💰 [{impact_color}]Cost Impact:[/] ${details['cost_impact']:,.2f}/month")
294
308
  if details.get("savings_opportunity"):
295
- self.console.print(f" 💎 [bright_green]Savings Opportunity:[/] ${details['savings_opportunity']:,.2f}/month")
309
+ self.console.print(
310
+ f" 💎 [bright_green]Savings Opportunity:[/] ${details['savings_opportunity']:,.2f}/month"
311
+ )
296
312
  if details.get("business_impact"):
297
313
  self.console.print(f" 📊 [bright_blue]Business Impact:[/] {details['business_impact']}")
298
-
314
+
299
315
  elif level in ["ERROR", "CRITICAL"]:
300
316
  # All users - Clear errors with solutions
301
317
  self.console.print(f"{icon} [bold]{label}[/] [{style}]{message}[/]")
302
318
  if details.get("solution"):
303
319
  self.console.print(f" 🔧 [bright_blue]Solution:[/] {details['solution']}")
304
320
  if details.get("suggested_command"):
305
- self.console.print(f" ⚡ [bright_yellow]Try this command:[/] [cyan]{details['suggested_command']}[/]")
321
+ self.console.print(
322
+ f" ⚡ [bright_yellow]Try this command:[/] [cyan]{details['suggested_command']}[/]"
323
+ )
306
324
  if details.get("aws_error"):
307
325
  self.console.print(f" 📋 [dim]AWS Error:[/] {details['aws_error']}")
308
326
  if details.get("troubleshooting_steps"):
@@ -312,56 +330,79 @@ class EnterpriseRichLogger:
312
330
  else:
313
331
  # Fallback to standard logging
314
332
  print(f"[{level}] {message}")
315
-
333
+
316
334
  # Always log to file systems
317
335
  if _HAS_LOGURU:
318
336
  loguru_logger.bind(correlation_id=self.correlation_id, **(details or {})).info(f"[{level}] {message}")
319
337
  else:
320
338
  logging.getLogger(self.name).info(f"[{level}] {message}")
321
339
 
322
- def debug_tech(self, message: str, aws_api: Optional[Dict[str, str]] = None, duration: Optional[float] = None,
323
- memory_usage: Optional[int] = None, request_id: Optional[str] = None, **kwargs) -> None:
340
+ def debug_tech(
341
+ self,
342
+ message: str,
343
+ aws_api: Optional[Dict[str, str]] = None,
344
+ duration: Optional[float] = None,
345
+ memory_usage: Optional[int] = None,
346
+ request_id: Optional[str] = None,
347
+ **kwargs,
348
+ ) -> None:
324
349
  """Log debug message for tech users (SRE/DevOps) with full API details."""
325
350
  details = {
326
- "aws_api": aws_api or {},
327
- "duration": duration,
351
+ "aws_api": aws_api or {},
352
+ "duration": duration,
328
353
  "memory_usage": memory_usage,
329
354
  "request_id": request_id,
330
- **kwargs
355
+ **kwargs,
331
356
  }
332
357
  self._rich_log("DEBUG", message, details)
333
358
 
334
- def info_standard(self, message: str, progress: Optional[Progress] = None, resource_count: Optional[int] = None,
335
- operation_status: Optional[str] = None, **kwargs) -> None:
359
+ def info_standard(
360
+ self,
361
+ message: str,
362
+ progress: Optional[Progress] = None,
363
+ resource_count: Optional[int] = None,
364
+ operation_status: Optional[str] = None,
365
+ **kwargs,
366
+ ) -> None:
336
367
  """Log info message for standard users with progress indicators."""
337
- details = {
338
- "resource_count": resource_count,
339
- "operation_status": operation_status,
340
- **kwargs
341
- }
368
+ details = {"resource_count": resource_count, "operation_status": operation_status, **kwargs}
342
369
  self._rich_log("INFO", message, details, progress)
343
370
 
344
- def warning_business(self, message: str, recommendation: Optional[str] = None, cost_impact: Optional[float] = None,
345
- savings_opportunity: Optional[float] = None, business_impact: Optional[str] = None, **kwargs) -> None:
371
+ def warning_business(
372
+ self,
373
+ message: str,
374
+ recommendation: Optional[str] = None,
375
+ cost_impact: Optional[float] = None,
376
+ savings_opportunity: Optional[float] = None,
377
+ business_impact: Optional[str] = None,
378
+ **kwargs,
379
+ ) -> None:
346
380
  """Log warning message for business users with recommendations and cost impact."""
347
381
  details = {
348
- "recommendation": recommendation,
382
+ "recommendation": recommendation,
349
383
  "cost_impact": cost_impact,
350
384
  "savings_opportunity": savings_opportunity,
351
385
  "business_impact": business_impact,
352
- **kwargs
386
+ **kwargs,
353
387
  }
354
388
  self._rich_log("WARNING", message, details)
355
389
 
356
- def error_all(self, message: str, solution: Optional[str] = None, aws_error: Optional[str] = None,
357
- suggested_command: Optional[str] = None, troubleshooting_steps: Optional[List[str]] = None, **kwargs) -> None:
390
+ def error_all(
391
+ self,
392
+ message: str,
393
+ solution: Optional[str] = None,
394
+ aws_error: Optional[str] = None,
395
+ suggested_command: Optional[str] = None,
396
+ troubleshooting_steps: Optional[List[str]] = None,
397
+ **kwargs,
398
+ ) -> None:
358
399
  """Log error message for all users with clear solutions."""
359
400
  details = {
360
- "solution": solution,
401
+ "solution": solution,
361
402
  "aws_error": aws_error,
362
403
  "suggested_command": suggested_command,
363
404
  "troubleshooting_steps": troubleshooting_steps or [],
364
- **kwargs
405
+ **kwargs,
365
406
  }
366
407
  self._rich_log("ERROR", message, details)
367
408
 
@@ -387,80 +428,87 @@ class EnterpriseRichLogger:
387
428
  self._rich_log("CRITICAL", message, kwargs)
388
429
 
389
430
  # Convenience methods for common operations
390
- def log_aws_operation(self, operation: str, service: str, duration: Optional[float] = None,
391
- success: bool = True, resource_count: Optional[int] = None, **kwargs) -> None:
431
+ def log_aws_operation(
432
+ self,
433
+ operation: str,
434
+ service: str,
435
+ duration: Optional[float] = None,
436
+ success: bool = True,
437
+ resource_count: Optional[int] = None,
438
+ **kwargs,
439
+ ) -> None:
392
440
  """Log AWS operation with appropriate level based on success and duration."""
393
441
  if not success:
394
442
  self.error_all(
395
443
  f"AWS {service} {operation} failed",
396
444
  solution=f"Check AWS permissions for {service}:{operation}",
397
445
  aws_error=kwargs.get("error"),
398
- suggested_command=f"aws {service} {operation.replace('_', '-')} --help"
446
+ suggested_command=f"aws {service} {operation.replace('_', '-')} --help",
399
447
  )
400
448
  elif self.level == "DEBUG":
401
449
  self.debug_tech(
402
450
  f"AWS {service} {operation} completed",
403
451
  aws_api={"service": service, "operation": operation},
404
452
  duration=duration,
405
- **kwargs
453
+ **kwargs,
406
454
  )
407
455
  else:
408
456
  status = "completed" if success else "failed"
409
457
  self.info_standard(
410
458
  f"{service.upper()} {operation.replace('_', ' ')} {status}",
411
459
  resource_count=resource_count,
412
- operation_status=status
460
+ operation_status=status,
413
461
  )
414
462
 
415
- def log_cost_analysis(self, operation: str, cost_impact: Optional[float] = None,
416
- savings_opportunity: Optional[float] = None, recommendation: Optional[str] = None) -> None:
463
+ def log_cost_analysis(
464
+ self,
465
+ operation: str,
466
+ cost_impact: Optional[float] = None,
467
+ savings_opportunity: Optional[float] = None,
468
+ recommendation: Optional[str] = None,
469
+ ) -> None:
417
470
  """Log cost analysis with business-focused messaging."""
418
471
  if cost_impact and cost_impact > 100: # Significant cost impact
419
472
  self.warning_business(
420
473
  f"Cost analysis: {operation}",
421
474
  cost_impact=cost_impact,
422
475
  savings_opportunity=savings_opportunity,
423
- recommendation=recommendation or f"Review {operation} for optimization opportunities"
476
+ recommendation=recommendation or f"Review {operation} for optimization opportunities",
424
477
  )
425
478
  else:
426
479
  self.info_standard(f"Cost analysis completed: {operation}")
427
480
 
428
- def log_performance_metric(self, operation: str, duration: float, threshold: float = 5.0,
429
- memory_usage: Optional[int] = None) -> None:
481
+ def log_performance_metric(
482
+ self, operation: str, duration: float, threshold: float = 5.0, memory_usage: Optional[int] = None
483
+ ) -> None:
430
484
  """Log performance metrics with appropriate warnings."""
431
485
  if duration > threshold:
432
486
  self.warning_business(
433
487
  f"Performance alert: {operation} took {duration:.2f}s",
434
488
  recommendation=f"Consider optimizing {operation} - target: <{threshold}s",
435
- business_impact="May affect user experience during peak hours"
489
+ business_impact="May affect user experience during peak hours",
436
490
  )
437
491
  elif self.level == "DEBUG":
438
- self.debug_tech(
439
- f"Performance: {operation}",
440
- duration=duration,
441
- memory_usage=memory_usage
442
- )
492
+ self.debug_tech(f"Performance: {operation}", duration=duration, memory_usage=memory_usage)
443
493
  else:
444
494
  self.info_standard(f"{operation} completed", operation_status="completed")
445
495
 
446
- def log_security_finding(self, finding: str, severity: str = "medium",
447
- remediation_steps: Optional[List[str]] = None) -> None:
496
+ def log_security_finding(
497
+ self, finding: str, severity: str = "medium", remediation_steps: Optional[List[str]] = None
498
+ ) -> None:
448
499
  """Log security finding with appropriate level and remediation."""
449
500
  if severity.lower() in ["high", "critical"]:
450
501
  self.error_all(
451
502
  f"Security finding: {finding}",
452
503
  solution=f"Immediate action required for {severity} severity finding",
453
- troubleshooting_steps=remediation_steps or [
454
- "Review security policies",
455
- "Apply security patches",
456
- "Contact security team if needed"
457
- ]
504
+ troubleshooting_steps=remediation_steps
505
+ or ["Review security policies", "Apply security patches", "Contact security team if needed"],
458
506
  )
459
507
  elif severity.lower() == "medium":
460
508
  self.warning_business(
461
509
  f"Security alert: {finding}",
462
510
  recommendation="Schedule remediation within next maintenance window",
463
- business_impact="Potential security risk if not addressed"
511
+ business_impact="Potential security risk if not addressed",
464
512
  )
465
513
  else:
466
514
  self.info_standard(f"Security scan: {finding} ({severity} severity)")
@@ -469,13 +517,14 @@ class EnterpriseRichLogger:
469
517
  def operation_context(self, operation_name: str, **context_details):
470
518
  """Context manager for logging operation start/end with performance tracking."""
471
519
  import time
520
+
472
521
  start_time = time.time()
473
-
522
+
474
523
  if self.level == "DEBUG":
475
524
  self.debug_tech(f"Starting {operation_name}", **context_details)
476
525
  else:
477
526
  self.info_standard(f"Starting {operation_name}")
478
-
527
+
479
528
  success = True
480
529
  try:
481
530
  yield self
@@ -484,23 +533,16 @@ class EnterpriseRichLogger:
484
533
  self.error_all(
485
534
  f"Operation failed: {operation_name}",
486
535
  solution="Check logs above for detailed error information",
487
- aws_error=str(e)
536
+ aws_error=str(e),
488
537
  )
489
538
  raise
490
539
  finally:
491
540
  duration = time.time() - start_time
492
541
  if success:
493
542
  if self.level == "DEBUG":
494
- self.debug_tech(
495
- f"Completed {operation_name}",
496
- duration=duration,
497
- **context_details
498
- )
543
+ self.debug_tech(f"Completed {operation_name}", duration=duration, **context_details)
499
544
  else:
500
- self.info_standard(
501
- f"Completed {operation_name}",
502
- operation_status="completed"
503
- )
545
+ self.info_standard(f"Completed {operation_name}", operation_status="completed")
504
546
  else:
505
547
  self.error_all(f"Failed {operation_name} after {duration:.2f}s")
506
548
 
@@ -668,6 +710,7 @@ def configure_enterprise_logging(
668
710
  json_output=json_output,
669
711
  )
670
712
 
713
+
671
714
  # Backward compatibility alias
672
715
  EnterpriseLogger = EnterpriseRichLogger
673
716
 
@@ -761,44 +804,41 @@ def get_logger() -> EnterpriseRichLogger:
761
804
  def get_context_logger(level: str = "INFO", json_output: bool = False) -> EnterpriseRichLogger:
762
805
  """
763
806
  Get context-aware enterprise logger with Rich CLI integration.
764
-
807
+
765
808
  This is the recommended way to get a logger instance with proper
766
809
  Rich CLI integration and user-type specific formatting.
767
-
810
+
768
811
  Args:
769
812
  level: Log level (DEBUG, INFO, WARNING, ERROR)
770
813
  json_output: Enable structured JSON output
771
-
814
+
772
815
  Returns:
773
816
  Configured enterprise logger with Rich CLI support
774
817
  """
775
818
  try:
776
819
  from runbooks.common.rich_utils import get_context_aware_console
820
+
777
821
  rich_console = get_context_aware_console()
778
822
  except ImportError:
779
823
  rich_console = Console() if _HAS_RICH else None
780
-
781
- return configure_enterprise_logging(
782
- level=level,
783
- rich_console=rich_console,
784
- json_output=json_output
785
- )
824
+
825
+ return configure_enterprise_logging(level=level, rich_console=rich_console, json_output=json_output)
786
826
 
787
827
 
788
828
  def get_module_logger(module_name: str, level: str = "INFO", json_output: bool = False) -> EnterpriseRichLogger:
789
829
  """
790
830
  Get a module-specific enhanced logger with automatic correlation ID and module identification.
791
-
831
+
792
832
  This is the recommended method for modules to get their logger instance.
793
-
833
+
794
834
  Args:
795
835
  module_name: Name of the module (e.g., 'finops', 'inventory', 'security')
796
836
  level: Log level (DEBUG, INFO, WARNING, ERROR)
797
837
  json_output: Enable structured JSON output
798
-
838
+
799
839
  Returns:
800
840
  Configured enterprise logger with module-specific identification
801
-
841
+
802
842
  Example:
803
843
  >>> from runbooks.enterprise.logging import get_module_logger
804
844
  >>> logger = get_module_logger("finops", level="INFO")
@@ -807,23 +847,21 @@ def get_module_logger(module_name: str, level: str = "INFO", json_output: bool =
807
847
  """
808
848
  try:
809
849
  from runbooks.common.rich_utils import get_context_aware_console
850
+
810
851
  rich_console = get_context_aware_console()
811
852
  except ImportError:
812
853
  rich_console = Console() if _HAS_RICH else None
813
-
854
+
814
855
  logger = EnterpriseRichLogger(
815
- name=f"runbooks.{module_name}",
816
- level=level,
817
- rich_console=rich_console,
818
- json_output=json_output
856
+ name=f"runbooks.{module_name}", level=level, rich_console=rich_console, json_output=json_output
819
857
  )
820
-
858
+
821
859
  # Add module-specific initialization message
822
860
  if level == "DEBUG":
823
861
  logger.debug_tech(
824
862
  f"Module logger initialized for {module_name}",
825
863
  aws_api={"service": "logging", "operation": "module_init"},
826
- duration=0.001
864
+ duration=0.001,
827
865
  )
828
-
866
+
829
867
  return logger