runbooks 1.1.3__py3-none-any.whl → 1.1.5__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.
- runbooks/__init__.py +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/WEIGHT_CONFIG_README.md +1 -1
- runbooks/cfat/assessment/compliance.py +8 -8
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cfat/models.py +6 -2
- runbooks/cfat/tests/__init__.py +6 -1
- runbooks/cli/__init__.py +13 -0
- runbooks/cli/commands/cfat.py +274 -0
- runbooks/cli/commands/finops.py +1164 -0
- runbooks/cli/commands/inventory.py +379 -0
- runbooks/cli/commands/operate.py +239 -0
- runbooks/cli/commands/security.py +248 -0
- runbooks/cli/commands/validation.py +825 -0
- runbooks/cli/commands/vpc.py +310 -0
- runbooks/cli/registry.py +107 -0
- runbooks/cloudops/__init__.py +23 -30
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +549 -547
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +226 -227
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +179 -215
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +11 -0
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +341 -0
- runbooks/common/aws_utils.py +75 -80
- runbooks/common/business_logic.py +127 -105
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
- runbooks/common/cross_account_manager.py +198 -205
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +235 -0
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +478 -495
- runbooks/common/mcp_integration.py +63 -74
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +176 -194
- runbooks/common/patterns.py +204 -0
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +248 -39
- runbooks/common/rich_utils.py +643 -92
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +29 -33
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +488 -622
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +40 -37
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +230 -292
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +338 -175
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1513 -482
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +25 -29
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +77 -78
- runbooks/finops/scenarios.py +1278 -439
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/tests/test_finops_dashboard.py +3 -3
- runbooks/finops/tests/test_reference_images_validation.py +2 -2
- runbooks/finops/tests/test_single_account_features.py +17 -17
- runbooks/finops/tests/validate_test_suite.py +1 -1
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +263 -269
- runbooks/finops/vpc_cleanup_exporter.py +191 -146
- runbooks/finops/vpc_cleanup_optimizer.py +593 -575
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/hitl/enhanced_workflow_engine.py +1 -1
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/README.md +3 -3
- runbooks/inventory/Tests/common_test_data.py +30 -30
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +28 -11
- runbooks/inventory/collectors/aws_networking.py +111 -101
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/discovery.md +2 -2
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/find_ec2_security_groups.py +1 -1
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/mcp_inventory_validator.py +549 -465
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +56 -52
- runbooks/inventory/rich_inventory_display.py +33 -32
- runbooks/inventory/unified_validation_engine.py +278 -251
- runbooks/inventory/vpc_analyzer.py +733 -696
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +382 -378
- runbooks/inventory/vpc_flow_analyzer.py +3 -3
- runbooks/main.py +152 -9147
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/metrics/dora_metrics_engine.py +2 -2
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/mcp_integration.py +1 -1
- runbooks/operate/networking_cost_heatmap.py +33 -10
- runbooks/operate/privatelink_operations.py +1 -1
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_endpoints.py +1 -1
- runbooks/operate/vpc_operations.py +648 -618
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +71 -67
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +91 -65
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +49 -44
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/integration_test_enterprise_security.py +5 -3
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/run_script.py +1 -1
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/mcp_reliability_engine.py +6 -6
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +51 -48
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +754 -708
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +447 -451
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +190 -162
- runbooks/vpc/mcp_no_eni_validator.py +681 -640
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1302 -1129
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.5.dist-info/METADATA +328 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -956
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.3.dist-info/METADATA +0 -799
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
runbooks/enterprise/__init__.py
CHANGED
runbooks/enterprise/logging.py
CHANGED
@@ -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(
|
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(
|
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 =
|
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 =
|
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(
|
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(
|
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(
|
323
|
-
|
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(
|
335
|
-
|
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(
|
345
|
-
|
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(
|
357
|
-
|
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(
|
391
|
-
|
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(
|
416
|
-
|
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(
|
429
|
-
|
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(
|
447
|
-
|
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
|
454
|
-
|
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
|