runbooks 0.9.2__py3-none-any.whl → 0.9.4__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 (46) hide show
  1. runbooks/__init__.py +15 -6
  2. runbooks/cfat/__init__.py +3 -1
  3. runbooks/cloudops/__init__.py +3 -1
  4. runbooks/common/aws_utils.py +367 -0
  5. runbooks/common/enhanced_logging_example.py +239 -0
  6. runbooks/common/enhanced_logging_integration_example.py +257 -0
  7. runbooks/common/logging_integration_helper.py +344 -0
  8. runbooks/common/profile_utils.py +8 -6
  9. runbooks/common/rich_utils.py +347 -3
  10. runbooks/enterprise/logging.py +400 -38
  11. runbooks/finops/README.md +262 -406
  12. runbooks/finops/__init__.py +2 -1
  13. runbooks/finops/accuracy_cross_validator.py +12 -3
  14. runbooks/finops/commvault_ec2_analysis.py +415 -0
  15. runbooks/finops/cost_processor.py +718 -42
  16. runbooks/finops/dashboard_router.py +44 -22
  17. runbooks/finops/dashboard_runner.py +302 -39
  18. runbooks/finops/embedded_mcp_validator.py +358 -48
  19. runbooks/finops/finops_scenarios.py +771 -0
  20. runbooks/finops/multi_dashboard.py +30 -15
  21. runbooks/finops/single_dashboard.py +386 -58
  22. runbooks/finops/types.py +29 -4
  23. runbooks/inventory/__init__.py +2 -1
  24. runbooks/main.py +522 -29
  25. runbooks/operate/__init__.py +3 -1
  26. runbooks/remediation/__init__.py +3 -1
  27. runbooks/remediation/commons.py +55 -16
  28. runbooks/remediation/commvault_ec2_analysis.py +259 -0
  29. runbooks/remediation/rds_snapshot_list.py +267 -102
  30. runbooks/remediation/workspaces_list.py +182 -31
  31. runbooks/security/__init__.py +3 -1
  32. runbooks/sre/__init__.py +2 -1
  33. runbooks/utils/__init__.py +81 -6
  34. runbooks/utils/version_validator.py +241 -0
  35. runbooks/vpc/__init__.py +2 -1
  36. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/METADATA +98 -60
  37. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/RECORD +41 -38
  38. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/entry_points.txt +1 -0
  39. runbooks/inventory/cloudtrail.md +0 -727
  40. runbooks/inventory/discovery.md +0 -81
  41. runbooks/remediation/CLAUDE.md +0 -100
  42. runbooks/remediation/DOME9.md +0 -218
  43. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +0 -506
  44. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/WHEEL +0 -0
  45. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/licenses/LICENSE +0 -0
  46. {runbooks-0.9.2.dist-info → runbooks-0.9.4.dist-info}/top_level.txt +0 -0
runbooks/__init__.py CHANGED
@@ -59,15 +59,24 @@ s3_ops = S3Operations()
59
59
  - **FinOps Practitioners**: Cost optimization and resource governance
60
60
  """
61
61
 
62
- from importlib.metadata import PackageNotFoundError
63
- from importlib.metadata import version as _pkg_version
62
+ # Centralized Version Management - Single Source of Truth
63
+ # All modules MUST import __version__ from this location
64
+ __version__ = "0.9.3"
64
65
 
65
- # Keep package version in sync with distribution metadata
66
+ # Fallback for legacy importlib.metadata usage during transition
66
67
  try:
67
- __version__ = _pkg_version("runbooks")
68
+ from importlib.metadata import version as _pkg_version
69
+ _metadata_version = _pkg_version("runbooks")
70
+ if _metadata_version != __version__:
71
+ import warnings
72
+ warnings.warn(
73
+ f"Version mismatch detected: pyproject.toml has {_metadata_version}, "
74
+ f"but centralized version is {__version__}. Please sync pyproject.toml.",
75
+ UserWarning
76
+ )
68
77
  except Exception:
69
- # Fallback if metadata is unavailable during editable installs
70
- __version__ = "1.0.0"
78
+ # Expected during development or when package metadata is unavailable
79
+ pass
71
80
 
72
81
  # Core module exports
73
82
  from runbooks.config import RunbooksConfig, load_config, save_config
runbooks/cfat/__init__.py CHANGED
@@ -56,8 +56,10 @@ from runbooks.cfat.models import (
56
56
  )
57
57
  from runbooks.cfat.runner import AssessmentRunner
58
58
 
59
+ # Import centralized version from main runbooks package
60
+ from runbooks import __version__
61
+
59
62
  # Version info
60
- __version__ = "0.7.8"
61
63
  __author__ = "CloudOps Runbooks Team"
62
64
 
63
65
  # Public API exports
@@ -51,7 +51,9 @@ from .models import (
51
51
  ResourceImpact
52
52
  )
53
53
 
54
- __version__ = "0.9.1"
54
+ # Import centralized version from main runbooks package
55
+ from runbooks import __version__
56
+
55
57
  __author__ = "CloudOps Enterprise Team"
56
58
 
57
59
  __all__ = [
@@ -0,0 +1,367 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ AWS Security & Authentication Utilities for CloudOps Runbooks Platform
4
+
5
+ This module provides enterprise-grade AWS authentication security enhancements
6
+ following FAANG security-as-code principles:
7
+
8
+ Features:
9
+ - Profile name sanitization to prevent account ID exposure
10
+ - Proactive token refresh with retry logic
11
+ - Enhanced error handling with security-aware messaging
12
+ - Audit trail maintenance while protecting sensitive identifiers
13
+ - Token expiration prediction and silent refresh
14
+
15
+ Author: DevSecOps Security Engineer - CloudOps Runbooks Team
16
+ Version: 0.9.1
17
+ Security Focus: Enterprise AWS Account Protection
18
+ """
19
+
20
+ import re
21
+ import time
22
+ from datetime import datetime, timedelta
23
+ from typing import Dict, List, Optional, Tuple
24
+
25
+ import boto3
26
+ from botocore.exceptions import ClientError, NoCredentialsError, TokenRetrievalError
27
+
28
+ from runbooks.common.rich_utils import console
29
+
30
+
31
+ class AWSProfileSanitizer:
32
+ """
33
+ Enterprise-grade AWS profile name sanitization for security logging.
34
+
35
+ Prevents AWS account ID exposure in logs while maintaining audit trail integrity.
36
+ Following FAANG security-as-code principles for sensitive identifier protection.
37
+ """
38
+
39
+ # Pattern to detect AWS account IDs in profile names
40
+ ACCOUNT_ID_PATTERN = re.compile(r'\b\d{12}\b')
41
+
42
+ # Pattern to detect enterprise profile patterns
43
+ ENTERPRISE_PROFILE_PATTERN = re.compile(r'(ams|aws)-.*-ReadOnlyAccess-(\d{12})')
44
+
45
+ @classmethod
46
+ def sanitize_profile_name(cls, profile_name: str, mask_style: str = "***masked***") -> str:
47
+ """
48
+ Sanitize AWS profile name by masking account IDs for secure logging.
49
+
50
+ Replaces 12-digit AWS account IDs with masked values to prevent account enumeration
51
+ while preserving profile identification capabilities for audit purposes.
52
+
53
+ Args:
54
+ profile_name: Original AWS profile name
55
+ mask_style: Masking pattern for account IDs (default: ***masked***)
56
+
57
+ Returns:
58
+ Sanitized profile name with masked account IDs
59
+
60
+ Example:
61
+ 'ams-admin-Billing-ReadOnlyAccess-909135376185' → 'ams-admin-Billing-ReadOnlyAccess-***masked***'
62
+ """
63
+ if not profile_name:
64
+ return profile_name
65
+
66
+ # Check for enterprise pattern first (more specific)
67
+ if cls.ENTERPRISE_PROFILE_PATTERN.match(profile_name):
68
+ return cls.ENTERPRISE_PROFILE_PATTERN.sub(r'\1-masked-ReadOnlyAccess-***masked***', profile_name)
69
+
70
+ # General account ID masking
71
+ return cls.ACCOUNT_ID_PATTERN.sub(mask_style, profile_name)
72
+
73
+ @classmethod
74
+ def sanitize_profile_list(cls, profiles: List[str]) -> List[str]:
75
+ """
76
+ Sanitize a list of AWS profile names for secure logging.
77
+
78
+ Args:
79
+ profiles: List of AWS profile names
80
+
81
+ Returns:
82
+ List of sanitized profile names
83
+ """
84
+ return [cls.sanitize_profile_name(profile) for profile in profiles]
85
+
86
+ @classmethod
87
+ def create_secure_log_context(cls, profile: str, operation: str) -> Dict[str, str]:
88
+ """
89
+ Create secure logging context with sanitized profile information.
90
+
91
+ Args:
92
+ profile: AWS profile name
93
+ operation: Operation being performed
94
+
95
+ Returns:
96
+ Dictionary with sanitized context for secure logging
97
+ """
98
+ return {
99
+ "operation": operation,
100
+ "profile_sanitized": cls.sanitize_profile_name(profile),
101
+ "profile_type": cls._classify_profile_type(profile),
102
+ "timestamp": datetime.utcnow().isoformat()
103
+ }
104
+
105
+ @classmethod
106
+ def _classify_profile_type(cls, profile_name: str) -> str:
107
+ """Classify profile type for enhanced logging context."""
108
+ profile_lower = profile_name.lower()
109
+
110
+ if "billing" in profile_lower:
111
+ return "billing"
112
+ elif "management" in profile_lower:
113
+ return "management"
114
+ elif "ops" in profile_lower or "operational" in profile_lower:
115
+ return "operational"
116
+ elif "admin" in profile_lower:
117
+ return "administrative"
118
+ else:
119
+ return "standard"
120
+
121
+
122
+ class AWSTokenManager:
123
+ """
124
+ Proactive AWS token management with security-focused error handling.
125
+
126
+ Implements proactive token refresh, retry logic, and enhanced error messaging
127
+ to reduce authentication timing exposure and improve operational security.
128
+ """
129
+
130
+ # Token refresh thresholds
131
+ TOKEN_REFRESH_THRESHOLD_MINUTES = 15 # Refresh if expires within 15 minutes
132
+ MAX_RETRY_ATTEMPTS = 3
133
+ RETRY_BACKOFF_BASE = 2 # Exponential backoff base (seconds)
134
+
135
+ def __init__(self, profile_name: str):
136
+ """Initialize token manager for specific AWS profile."""
137
+ self.profile_name = profile_name
138
+ self.sanitized_profile = AWSProfileSanitizer.sanitize_profile_name(profile_name)
139
+ self._session = None
140
+ self._last_refresh_check = None
141
+
142
+ def get_secure_session(self, force_refresh: bool = False) -> boto3.Session:
143
+ """
144
+ Get AWS session with proactive token refresh and security enhancements.
145
+
146
+ Implements:
147
+ - Proactive token expiration checking
148
+ - Silent token refresh before expiration
149
+ - Exponential backoff retry logic
150
+ - Security-aware error messages
151
+
152
+ Args:
153
+ force_refresh: Force token refresh regardless of expiration status
154
+
155
+ Returns:
156
+ Boto3 session with valid credentials
157
+
158
+ Raises:
159
+ SecurityError: For authentication security issues
160
+ TokenRefreshError: For token refresh failures
161
+ """
162
+ current_time = datetime.utcnow()
163
+
164
+ # Check if proactive refresh is needed
165
+ if (force_refresh or
166
+ self._session is None or
167
+ self._needs_token_refresh(current_time)):
168
+
169
+ self._session = self._refresh_session_with_retry()
170
+ self._last_refresh_check = current_time
171
+
172
+ # Log secure refresh event
173
+ console.log(
174
+ f"[dim green]✅ Token refresh completed for profile: {self.sanitized_profile}[/]"
175
+ )
176
+
177
+ return self._session
178
+
179
+ def _needs_token_refresh(self, current_time: datetime) -> bool:
180
+ """Check if proactive token refresh is needed."""
181
+ if self._last_refresh_check is None:
182
+ return True
183
+
184
+ # Check every 5 minutes to avoid excessive API calls
185
+ if (current_time - self._last_refresh_check) < timedelta(minutes=5):
186
+ return False
187
+
188
+ try:
189
+ # Test session validity with STS call
190
+ if self._session:
191
+ sts_client = self._session.client('sts')
192
+ sts_client.get_caller_identity()
193
+ return False # Session still valid
194
+ except ClientError as e:
195
+ error_code = e.response.get('Error', {}).get('Code', '')
196
+ if error_code in ['ExpiredToken', 'InvalidToken', 'TokenRefreshRequired']:
197
+ return True
198
+
199
+ return False
200
+
201
+ def _refresh_session_with_retry(self) -> boto3.Session:
202
+ """Refresh session with exponential backoff retry logic."""
203
+ last_exception = None
204
+
205
+ for attempt in range(self.MAX_RETRY_ATTEMPTS):
206
+ try:
207
+ # Create new session
208
+ session = boto3.Session(profile_name=self.profile_name)
209
+
210
+ # Validate session with STS call
211
+ sts_client = session.client('sts')
212
+ caller_identity = sts_client.get_caller_identity()
213
+
214
+ # Log successful refresh (with sanitized profile)
215
+ console.log(
216
+ f"[dim cyan]🔄 Session validated for {self.sanitized_profile} "
217
+ f"(attempt {attempt + 1}/{self.MAX_RETRY_ATTEMPTS})[/]"
218
+ )
219
+
220
+ return session
221
+
222
+ except (ClientError, NoCredentialsError, TokenRetrievalError) as e:
223
+ last_exception = e
224
+
225
+ if attempt < self.MAX_RETRY_ATTEMPTS - 1:
226
+ # Wait with exponential backoff
227
+ wait_time = self.RETRY_BACKOFF_BASE ** attempt
228
+ console.log(
229
+ f"[yellow]⏳ Token refresh attempt {attempt + 1} failed, "
230
+ f"retrying in {wait_time}s for {self.sanitized_profile}[/]"
231
+ )
232
+ time.sleep(wait_time)
233
+ else:
234
+ # Final attempt failed, provide enhanced guidance
235
+ self._handle_token_refresh_failure(last_exception)
236
+
237
+ # If we get here, all attempts failed
238
+ raise TokenRefreshError(
239
+ f"Failed to refresh AWS session for profile {self.sanitized_profile} "
240
+ f"after {self.MAX_RETRY_ATTEMPTS} attempts"
241
+ )
242
+
243
+ def _handle_token_refresh_failure(self, error: Exception) -> None:
244
+ """Provide enhanced guidance for token refresh failures."""
245
+ error_str = str(error)
246
+
247
+ # Determine error type for appropriate guidance
248
+ if "ExpiredToken" in error_str or "InvalidToken" in error_str:
249
+ console.log(
250
+ f"[red]🔐 Token expired for {self.sanitized_profile}[/]\n"
251
+ "[yellow]💡 Resolution steps:[/]\n"
252
+ f" 1. Run: [bold]aws sso login --profile {self.profile_name}[/bold]\n"
253
+ " 2. Complete browser authentication\n"
254
+ " 3. Retry your operation\n"
255
+ " 4. Consider extending SSO session duration in AWS settings"
256
+ )
257
+ elif "NoCredentialsError" in error_str:
258
+ console.log(
259
+ f"[red]🔐 No credentials found for {self.sanitized_profile}[/]\n"
260
+ "[yellow]💡 Resolution steps:[/]\n"
261
+ f" 1. Verify profile exists: [bold]aws configure list-profiles[/bold]\n"
262
+ f" 2. Configure profile: [bold]aws configure sso --profile {self.profile_name}[/bold]\n"
263
+ " 3. Complete SSO configuration\n"
264
+ " 4. Retry your operation"
265
+ )
266
+ else:
267
+ console.log(
268
+ f"[red]🔐 Authentication error for {self.sanitized_profile}: {str(error)[:100]}[/]\n"
269
+ "[yellow]💡 General resolution steps:[/]\n"
270
+ f" 1. Check AWS CLI configuration\n"
271
+ f" 2. Verify IAM permissions\n"
272
+ f" 3. Try: [bold]aws sts get-caller-identity --profile {self.profile_name}[/bold]"
273
+ )
274
+
275
+
276
+ class SecurityError(Exception):
277
+ """Raised for AWS security-related errors."""
278
+ pass
279
+
280
+
281
+ class TokenRefreshError(Exception):
282
+ """Raised for AWS token refresh failures."""
283
+ pass
284
+
285
+
286
+ def create_secure_aws_session(profile_name: str, operation_context: str = "aws_operation") -> boto3.Session:
287
+ """
288
+ Create secure AWS session with enterprise security enhancements.
289
+
290
+ This is the primary entry point for secure AWS session creation across
291
+ all CloudOps modules. Implements:
292
+
293
+ - Profile name sanitization for secure logging
294
+ - Proactive token refresh
295
+ - Enhanced error handling
296
+ - Security audit trail
297
+
298
+ Args:
299
+ profile_name: AWS profile name
300
+ operation_context: Description of the operation for audit logging
301
+
302
+ Returns:
303
+ Secure boto3 session with valid credentials
304
+
305
+ Raises:
306
+ SecurityError: For security-related authentication issues
307
+ TokenRefreshError: For token refresh failures
308
+
309
+ Example:
310
+ session = create_secure_aws_session("ams-admin-Billing-ReadOnlyAccess-909135376185", "cost_analysis")
311
+ """
312
+ # Create secure logging context
313
+ log_context = AWSProfileSanitizer.create_secure_log_context(profile_name, operation_context)
314
+
315
+ console.log(
316
+ f"[dim cyan]🔐 Initiating secure AWS session for {log_context['profile_sanitized']} "
317
+ f"({log_context['profile_type']} profile)[/]"
318
+ )
319
+
320
+ try:
321
+ # Initialize token manager and get secure session
322
+ token_manager = AWSTokenManager(profile_name)
323
+ session = token_manager.get_secure_session()
324
+
325
+ console.log(
326
+ f"[dim green]✅ Secure session established for {log_context['profile_sanitized']}[/]"
327
+ )
328
+
329
+ return session
330
+
331
+ except Exception as e:
332
+ console.log(
333
+ f"[red]❌ Failed to create secure session for {log_context['profile_sanitized']}: {str(e)[:100]}[/]"
334
+ )
335
+ raise
336
+
337
+
338
+ def sanitize_aws_error_message(error_message: str) -> str:
339
+ """
340
+ Sanitize AWS error messages to remove sensitive account information.
341
+
342
+ Args:
343
+ error_message: Original AWS error message
344
+
345
+ Returns:
346
+ Sanitized error message with account IDs masked
347
+ """
348
+ return AWSProfileSanitizer.ACCOUNT_ID_PATTERN.sub("***masked***", error_message)
349
+
350
+
351
+ def get_profile_classification(profile_name: str) -> Dict[str, str]:
352
+ """
353
+ Get security classification information for AWS profile.
354
+
355
+ Args:
356
+ profile_name: AWS profile name
357
+
358
+ Returns:
359
+ Dictionary with profile security classification
360
+ """
361
+ sanitizer = AWSProfileSanitizer()
362
+ return {
363
+ "original": profile_name,
364
+ "sanitized": sanitizer.sanitize_profile_name(profile_name),
365
+ "type": sanitizer._classify_profile_type(profile_name),
366
+ "risk_level": "high" if "admin" in profile_name.lower() else "medium"
367
+ }
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Enhanced Logging Integration Example for CloudOps Runbooks
4
+
5
+ This module demonstrates how to integrate the enhanced multi-level logging system
6
+ with Rich CLI formatting across different user types and scenarios.
7
+ """
8
+
9
+ import time
10
+ from typing import Dict, Any, Optional
11
+
12
+ # Import enhanced logging capabilities
13
+ from runbooks.enterprise.logging import get_context_logger, EnterpriseRichLogger
14
+
15
+
16
+ class EnhancedLoggingExample:
17
+ """Demonstrates enhanced logging integration patterns for different user types."""
18
+
19
+ def __init__(self, log_level: str = "INFO", json_output: bool = False):
20
+ """
21
+ Initialize with enhanced logger.
22
+
23
+ Args:
24
+ log_level: Logging level (DEBUG, INFO, WARNING, ERROR)
25
+ json_output: Enable structured JSON output
26
+ """
27
+ self.logger = get_context_logger(level=log_level, json_output=json_output)
28
+ self.log_level = log_level.upper()
29
+
30
+ def demonstrate_debug_logging(self):
31
+ """Demonstrate DEBUG level logging for tech users (SRE/DevOps)."""
32
+ print("\n=== DEBUG Level Logging (Tech Users) ===")
33
+
34
+ # AWS API tracing example
35
+ start_time = time.time()
36
+
37
+ # Simulate AWS API call
38
+ time.sleep(0.1)
39
+ duration = time.time() - start_time
40
+
41
+ self.logger.debug_tech(
42
+ "EC2 instances discovered in region us-east-1",
43
+ aws_api={
44
+ "service": "ec2",
45
+ "operation": "describe_instances",
46
+ "region": "us-east-1"
47
+ },
48
+ duration=duration,
49
+ request_id="12345-abcde-67890",
50
+ resource_count=42
51
+ )
52
+
53
+ # Performance metrics
54
+ self.logger.debug_tech(
55
+ "Cost analysis query executed",
56
+ aws_api={
57
+ "service": "ce",
58
+ "operation": "get_cost_and_usage"
59
+ },
60
+ duration=2.435,
61
+ data_points=1500,
62
+ cache_hit=False
63
+ )
64
+
65
+ def demonstrate_info_logging(self):
66
+ """Demonstrate INFO level logging for standard users."""
67
+ print("\n=== INFO Level Logging (Standard Users) ===")
68
+
69
+ # Standard operation status
70
+ self.logger.info_standard("Starting cost analysis for account 123456789012")
71
+
72
+ # Progress indication
73
+ self.logger.info_standard("Processing 15 AWS services across 3 regions")
74
+
75
+ # Completion status
76
+ self.logger.info_standard(
77
+ "Cost analysis completed successfully",
78
+ execution_time="12.3s",
79
+ resources_analyzed=245
80
+ )
81
+
82
+ def demonstrate_warning_logging(self):
83
+ """Demonstrate WARNING level logging for business users."""
84
+ print("\n=== WARNING Level Logging (Business Users) ===")
85
+
86
+ # Cost alerts with recommendations
87
+ self.logger.warning_business(
88
+ "High storage costs detected in S3",
89
+ recommendation="Consider implementing lifecycle policies for objects older than 90 days",
90
+ cost_impact=2847.50,
91
+ affected_buckets=12
92
+ )
93
+
94
+ # Resource optimization alerts
95
+ self.logger.warning_business(
96
+ "Underutilized EC2 instances found",
97
+ recommendation="Review instance sizing for potential rightsizing opportunities",
98
+ cost_impact=1250.00,
99
+ instance_count=8
100
+ )
101
+
102
+ # Budget threshold warnings
103
+ self.logger.warning_business(
104
+ "Monthly budget threshold exceeded",
105
+ recommendation="Review cost allocation and consider implementing cost controls",
106
+ cost_impact=450.75,
107
+ budget_utilization="105%"
108
+ )
109
+
110
+ def demonstrate_error_logging(self):
111
+ """Demonstrate ERROR level logging for all users."""
112
+ print("\n=== ERROR Level Logging (All Users) ===")
113
+
114
+ # AWS authentication errors
115
+ self.logger.error_all(
116
+ "Unable to access Cost Explorer API",
117
+ solution="Run 'aws sso login' to refresh your authentication tokens",
118
+ aws_error="ExpiredToken: Token has expired",
119
+ profile="billing-profile"
120
+ )
121
+
122
+ # Configuration errors
123
+ self.logger.error_all(
124
+ "Invalid AWS profile configuration",
125
+ solution="Check your ~/.aws/config file or contact your AWS administrator",
126
+ aws_error="ProfileNotFound: The config profile (invalid-profile) could not be found"
127
+ )
128
+
129
+ # Service access errors
130
+ self.logger.error_all(
131
+ "Insufficient permissions for Organizations API",
132
+ solution="Request 'organizations:ListAccounts' permission or use a different profile",
133
+ aws_error="AccessDenied: User is not authorized to perform organizations:ListAccounts"
134
+ )
135
+
136
+ def demonstrate_structured_logging(self):
137
+ """Demonstrate structured JSON logging for programmatic use."""
138
+ print("\n=== Structured JSON Logging (Programmatic Use) ===")
139
+
140
+ # Create JSON logger
141
+ json_logger = get_context_logger(level=self.log_level, json_output=True)
142
+
143
+ # JSON structured logs
144
+ json_logger.info_standard(
145
+ "Cost analysis completed",
146
+ total_cost=15432.75,
147
+ account_count=5,
148
+ service_breakdown={
149
+ "EC2": 8750.25,
150
+ "S3": 3200.50,
151
+ "RDS": 2482.00,
152
+ "Lambda": 1000.00
153
+ }
154
+ )
155
+
156
+ json_logger.warning_business(
157
+ "Budget variance detected",
158
+ recommendation="Implement cost controls",
159
+ cost_impact=2500.00,
160
+ variance_percentage=15.2
161
+ )
162
+
163
+ def demonstrate_contextual_logging(self):
164
+ """Demonstrate context-aware logging based on execution environment."""
165
+ print("\n=== Context-Aware Logging ===")
166
+
167
+ # Logging adapts to CLI vs Jupyter vs CI/CD environments
168
+ self.logger.info_standard("Environment-aware logging initialized")
169
+
170
+ # Performance logging with context
171
+ with self.logger_performance_context("cost_analysis_operation") as perf:
172
+ # Simulate work
173
+ time.sleep(0.2)
174
+ perf.add_metric("accounts_processed", 12)
175
+ perf.add_metric("cost_data_points", 1500)
176
+
177
+
178
+ def demo_user_type_scenarios():
179
+ """
180
+ Demonstrate logging for different user types and scenarios.
181
+ """
182
+ print("Enhanced Multi-Level Logging System Demo")
183
+ print("=" * 50)
184
+
185
+ # Tech users (DEBUG level)
186
+ print("\n🔧 TECH USER SCENARIO (--log-level DEBUG)")
187
+ tech_demo = EnhancedLoggingExample(log_level="DEBUG")
188
+ tech_demo.demonstrate_debug_logging()
189
+
190
+ # Standard users (INFO level)
191
+ print("\n👥 STANDARD USER SCENARIO (--log-level INFO)")
192
+ standard_demo = EnhancedLoggingExample(log_level="INFO")
193
+ standard_demo.demonstrate_info_logging()
194
+
195
+ # Business users (WARNING level)
196
+ print("\n💼 BUSINESS USER SCENARIO (--log-level WARNING)")
197
+ business_demo = EnhancedLoggingExample(log_level="WARNING")
198
+ business_demo.demonstrate_warning_logging()
199
+
200
+ # Error scenarios (ERROR level)
201
+ print("\n🚨 ERROR SCENARIOS (--log-level ERROR)")
202
+ error_demo = EnhancedLoggingExample(log_level="ERROR")
203
+ error_demo.demonstrate_error_logging()
204
+
205
+ # JSON output for automation
206
+ print("\n📄 STRUCTURED JSON OUTPUT (--json-output)")
207
+ json_demo = EnhancedLoggingExample(log_level="INFO", json_output=True)
208
+ json_demo.demonstrate_structured_logging()
209
+
210
+
211
+ # Performance context manager for demonstration
212
+ class MockPerformanceContext:
213
+ """Mock performance context for demonstration."""
214
+
215
+ def __init__(self, operation_name: str):
216
+ self.operation_name = operation_name
217
+ self.metrics = {}
218
+ self.start_time = time.time()
219
+
220
+ def __enter__(self):
221
+ return self
222
+
223
+ def __exit__(self, exc_type, exc_val, exc_tb):
224
+ duration = time.time() - self.start_time
225
+ print(f"Performance: {self.operation_name} completed in {duration:.3f}s")
226
+ if self.metrics:
227
+ print(f"Metrics: {self.metrics}")
228
+
229
+ def add_metric(self, key: str, value: Any):
230
+ self.metrics[key] = value
231
+
232
+
233
+ # Add performance context to logger class for demo
234
+ EnhancedLoggingExample.logger_performance_context = lambda self, name: MockPerformanceContext(name)
235
+
236
+
237
+ if __name__ == "__main__":
238
+ """Run the enhanced logging demonstration."""
239
+ demo_user_type_scenarios()