runbooks 0.9.1__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.
- runbooks/__init__.py +15 -6
- runbooks/cfat/__init__.py +3 -1
- runbooks/cloudops/__init__.py +3 -1
- runbooks/common/aws_utils.py +367 -0
- runbooks/common/enhanced_logging_example.py +239 -0
- runbooks/common/enhanced_logging_integration_example.py +257 -0
- runbooks/common/logging_integration_helper.py +344 -0
- runbooks/common/profile_utils.py +8 -6
- runbooks/common/rich_utils.py +347 -3
- runbooks/enterprise/logging.py +400 -38
- runbooks/finops/README.md +262 -406
- runbooks/finops/__init__.py +2 -1
- runbooks/finops/accuracy_cross_validator.py +12 -3
- runbooks/finops/commvault_ec2_analysis.py +415 -0
- runbooks/finops/cost_processor.py +718 -42
- runbooks/finops/dashboard_router.py +44 -22
- runbooks/finops/dashboard_runner.py +302 -39
- runbooks/finops/embedded_mcp_validator.py +358 -48
- runbooks/finops/finops_scenarios.py +771 -0
- runbooks/finops/multi_dashboard.py +30 -15
- runbooks/finops/single_dashboard.py +386 -58
- runbooks/finops/types.py +29 -4
- runbooks/inventory/__init__.py +2 -1
- runbooks/main.py +522 -29
- runbooks/operate/__init__.py +3 -1
- runbooks/remediation/__init__.py +3 -1
- runbooks/remediation/commons.py +55 -16
- runbooks/remediation/commvault_ec2_analysis.py +259 -0
- runbooks/remediation/rds_snapshot_list.py +267 -102
- runbooks/remediation/workspaces_list.py +182 -31
- runbooks/security/__init__.py +3 -1
- runbooks/sre/__init__.py +2 -1
- runbooks/utils/__init__.py +81 -6
- runbooks/utils/version_validator.py +241 -0
- runbooks/vpc/__init__.py +2 -1
- runbooks-0.9.4.dist-info/METADATA +563 -0
- {runbooks-0.9.1.dist-info → runbooks-0.9.4.dist-info}/RECORD +41 -38
- {runbooks-0.9.1.dist-info → runbooks-0.9.4.dist-info}/entry_points.txt +1 -0
- runbooks/inventory/cloudtrail.md +0 -727
- runbooks/inventory/discovery.md +0 -81
- runbooks/remediation/CLAUDE.md +0 -100
- runbooks/remediation/DOME9.md +0 -218
- runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +0 -506
- runbooks-0.9.1.dist-info/METADATA +0 -308
- {runbooks-0.9.1.dist-info → runbooks-0.9.4.dist-info}/WHEEL +0 -0
- {runbooks-0.9.1.dist-info → runbooks-0.9.4.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.9.1.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
|
-
|
63
|
-
|
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
|
-
#
|
66
|
+
# Fallback for legacy importlib.metadata usage during transition
|
66
67
|
try:
|
67
|
-
|
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
|
-
#
|
70
|
-
|
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
|
runbooks/cloudops/__init__.py
CHANGED
@@ -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()
|