azpaddypy 0.3.4__py3-none-any.whl → 0.3.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.
azpaddypy/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ from .mgmt import *
2
+ from .resources import *
@@ -0,0 +1,12 @@
1
+ """Azure resources package for azpaddypy.
2
+
3
+ This package contains modules for interacting with various Azure resources
4
+ including Key Vault, Storage, and other Azure services.
5
+ """
6
+
7
+ from .keyvault import AzureKeyVault, create_azure_keyvault
8
+
9
+ __all__ = [
10
+ "AzureKeyVault",
11
+ "create_azure_keyvault",
12
+ ]
@@ -0,0 +1,530 @@
1
+ from typing import Optional, Dict, Any, Union, List
2
+ from azure.keyvault.secrets import SecretClient
3
+ from azure.keyvault.keys import KeyClient
4
+ from azure.keyvault.certificates import CertificateClient
5
+ from azure.core.exceptions import ResourceNotFoundError, ClientAuthenticationError
6
+ from azure.core.credentials import TokenCredential
7
+ from ..mgmt.logging import AzureLogger
8
+ from ..mgmt.identity import AzureIdentity
9
+
10
+
11
+ class AzureKeyVault:
12
+ """Azure Key Vault management with comprehensive secret, key, and certificate operations.
13
+
14
+ Provides standardized Azure Key Vault operations using Azure SDK clients
15
+ with integrated logging, error handling, and OpenTelemetry tracing support.
16
+ Supports operations for secrets, keys, and certificates with proper
17
+ authentication and authorization handling.
18
+
19
+ Attributes:
20
+ vault_url: Azure Key Vault URL
21
+ service_name: Service identifier for logging and tracing
22
+ service_version: Service version for context
23
+ logger: AzureLogger instance for structured logging
24
+ credential: Azure credential for authentication
25
+ secret_client: Azure Key Vault SecretClient instance
26
+ key_client: Azure Key Vault KeyClient instance
27
+ certificate_client: Azure Key Vault CertificateClient instance
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ vault_url: str,
33
+ credential: Optional[TokenCredential] = None,
34
+ azure_identity: Optional[AzureIdentity] = None,
35
+ service_name: str = "azure_keyvault",
36
+ service_version: str = "1.0.0",
37
+ logger: Optional[AzureLogger] = None,
38
+ connection_string: Optional[str] = None,
39
+ enable_secrets: bool = True,
40
+ enable_keys: bool = True,
41
+ enable_certificates: bool = True,
42
+ ):
43
+ """Initialize Azure Key Vault with comprehensive configuration.
44
+
45
+ Args:
46
+ vault_url: Azure Key Vault URL (e.g., https://vault.vault.azure.net/)
47
+ credential: Azure credential for authentication
48
+ azure_identity: AzureIdentity instance for credential management
49
+ service_name: Service name for tracing context
50
+ service_version: Service version for metadata
51
+ logger: Optional AzureLogger instance
52
+ connection_string: Application Insights connection string
53
+ enable_secrets: Enable secret operations client
54
+ enable_keys: Enable key operations client
55
+ enable_certificates: Enable certificate operations client
56
+
57
+ Raises:
58
+ ValueError: If neither credential nor azure_identity is provided
59
+ Exception: If client initialization fails
60
+ """
61
+ self.vault_url = vault_url
62
+ self.service_name = service_name
63
+ self.service_version = service_version
64
+ self.enable_secrets = enable_secrets
65
+ self.enable_keys = enable_keys
66
+ self.enable_certificates = enable_certificates
67
+
68
+ # Initialize logger - use provided instance or create new one
69
+ if logger is not None:
70
+ self.logger = logger
71
+ else:
72
+ self.logger = AzureLogger(
73
+ service_name=service_name,
74
+ service_version=service_version,
75
+ connection_string=connection_string,
76
+ enable_console_logging=True,
77
+ )
78
+
79
+ # Setup credential
80
+ if azure_identity is not None:
81
+ self.credential = azure_identity.get_credential()
82
+ self.azure_identity = azure_identity
83
+ elif credential is not None:
84
+ self.credential = credential
85
+ self.azure_identity = None
86
+ else:
87
+ raise ValueError("Either 'credential' or 'azure_identity' must be provided")
88
+
89
+ # Initialize clients
90
+ self.secret_client = None
91
+ self.key_client = None
92
+ self.certificate_client = None
93
+
94
+ self._setup_clients()
95
+
96
+ self.logger.info(
97
+ f"Azure Key Vault initialized for service '{service_name}' v{service_version}",
98
+ extra={
99
+ "vault_url": vault_url,
100
+ "secrets_enabled": enable_secrets,
101
+ "keys_enabled": enable_keys,
102
+ "certificates_enabled": enable_certificates,
103
+ }
104
+ )
105
+
106
+ def _setup_clients(self):
107
+ """Initialize Key Vault clients based on enabled features.
108
+
109
+ Raises:
110
+ Exception: If client initialization fails
111
+ """
112
+ try:
113
+ if self.enable_secrets:
114
+ self.secret_client = SecretClient(
115
+ vault_url=self.vault_url,
116
+ credential=self.credential
117
+ )
118
+ self.logger.debug("SecretClient initialized successfully")
119
+
120
+ if self.enable_keys:
121
+ self.key_client = KeyClient(
122
+ vault_url=self.vault_url,
123
+ credential=self.credential
124
+ )
125
+ self.logger.debug("KeyClient initialized successfully")
126
+
127
+ if self.enable_certificates:
128
+ self.certificate_client = CertificateClient(
129
+ vault_url=self.vault_url,
130
+ credential=self.credential
131
+ )
132
+ self.logger.debug("CertificateClient initialized successfully")
133
+
134
+ except Exception as e:
135
+ self.logger.error(
136
+ f"Failed to initialize Key Vault clients: {e}",
137
+ exc_info=True
138
+ )
139
+ raise
140
+
141
+ # Secret Operations
142
+ def get_secret(
143
+ self,
144
+ secret_name: str,
145
+ version: Optional[str] = None,
146
+ **kwargs
147
+ ) -> Optional[str]:
148
+ """Retrieve a secret from Azure Key Vault.
149
+
150
+ Args:
151
+ secret_name: Name of the secret
152
+ version: Optional specific version of the secret
153
+ **kwargs: Additional parameters for the secret retrieval
154
+
155
+ Returns:
156
+ Secret value if found, None if not found
157
+
158
+ Raises:
159
+ RuntimeError: If secret client is not initialized
160
+ Exception: If secret retrieval fails for reasons other than not found
161
+ """
162
+ with self.logger.create_span(
163
+ "AzureKeyVault.get_secret",
164
+ attributes={
165
+ "service.name": self.service_name,
166
+ "operation.type": "secret_retrieval",
167
+ "keyvault.secret_name": secret_name,
168
+ "keyvault.version": version or "latest",
169
+ "keyvault.vault_url": self.vault_url
170
+ }
171
+ ):
172
+ if self.secret_client is None:
173
+ error_msg = "Secret client not initialized. Enable secrets during initialization."
174
+ self.logger.error(error_msg)
175
+ raise RuntimeError(error_msg)
176
+
177
+ try:
178
+ self.logger.debug(
179
+ "Retrieving secret from Key Vault",
180
+ extra={
181
+ "secret_name": secret_name,
182
+ "version": version,
183
+ "vault_url": self.vault_url
184
+ }
185
+ )
186
+
187
+ secret = self.secret_client.get_secret(secret_name, version=version, **kwargs)
188
+
189
+ self.logger.info(
190
+ "Secret retrieved successfully",
191
+ extra={
192
+ "secret_name": secret_name,
193
+ "version": secret.properties.version if secret.properties else None,
194
+ "content_type": secret.properties.content_type if secret.properties else None
195
+ }
196
+ )
197
+
198
+ return secret.value
199
+
200
+ except ResourceNotFoundError:
201
+ self.logger.warning(
202
+ f"Secret '{secret_name}' not found in Key Vault",
203
+ extra={"secret_name": secret_name, "version": version}
204
+ )
205
+ return None
206
+ except ClientAuthenticationError as e:
207
+ self.logger.error(
208
+ f"Authentication failed for secret '{secret_name}': {e}",
209
+ extra={"secret_name": secret_name},
210
+ exc_info=True
211
+ )
212
+ raise
213
+ except Exception as e:
214
+ self.logger.error(
215
+ f"Failed to retrieve secret '{secret_name}': {e}",
216
+ extra={"secret_name": secret_name, "version": version},
217
+ exc_info=True
218
+ )
219
+ raise
220
+
221
+ def set_secret(
222
+ self,
223
+ secret_name: str,
224
+ secret_value: str,
225
+ content_type: Optional[str] = None,
226
+ tags: Optional[Dict[str, str]] = None,
227
+ **kwargs
228
+ ) -> bool:
229
+ """Set a secret in Azure Key Vault.
230
+
231
+ Args:
232
+ secret_name: Name of the secret
233
+ secret_value: Value of the secret
234
+ content_type: Optional content type for the secret
235
+ tags: Optional tags for the secret
236
+ **kwargs: Additional parameters for secret creation
237
+
238
+ Returns:
239
+ True if secret was set successfully
240
+
241
+ Raises:
242
+ RuntimeError: If secret client is not initialized
243
+ Exception: If secret creation fails
244
+ """
245
+ with self.logger.create_span(
246
+ "AzureKeyVault.set_secret",
247
+ attributes={
248
+ "service.name": self.service_name,
249
+ "operation.type": "secret_creation",
250
+ "keyvault.secret_name": secret_name,
251
+ "keyvault.content_type": content_type or "text/plain",
252
+ "keyvault.vault_url": self.vault_url
253
+ }
254
+ ):
255
+ if self.secret_client is None:
256
+ error_msg = "Secret client not initialized. Enable secrets during initialization."
257
+ self.logger.error(error_msg)
258
+ raise RuntimeError(error_msg)
259
+
260
+ try:
261
+ self.logger.debug(
262
+ "Setting secret in Key Vault",
263
+ extra={
264
+ "secret_name": secret_name,
265
+ "content_type": content_type,
266
+ "has_tags": tags is not None,
267
+ "vault_url": self.vault_url
268
+ }
269
+ )
270
+
271
+ secret = self.secret_client.set_secret(
272
+ secret_name,
273
+ secret_value,
274
+ content_type=content_type,
275
+ tags=tags,
276
+ **kwargs
277
+ )
278
+
279
+ self.logger.info(
280
+ "Secret set successfully",
281
+ extra={
282
+ "secret_name": secret_name,
283
+ "version": secret.properties.version if secret.properties else None,
284
+ "content_type": secret.properties.content_type if secret.properties else None
285
+ }
286
+ )
287
+
288
+ return True
289
+
290
+ except Exception as e:
291
+ self.logger.error(
292
+ f"Failed to set secret '{secret_name}': {e}",
293
+ extra={"secret_name": secret_name, "content_type": content_type},
294
+ exc_info=True
295
+ )
296
+ raise
297
+
298
+ def delete_secret(self, secret_name: str, **kwargs) -> bool:
299
+ """Delete a secret from Azure Key Vault.
300
+
301
+ Args:
302
+ secret_name: Name of the secret to delete
303
+ **kwargs: Additional parameters for secret deletion
304
+
305
+ Returns:
306
+ True if secret was deleted successfully
307
+
308
+ Raises:
309
+ RuntimeError: If secret client is not initialized
310
+ Exception: If secret deletion fails
311
+ """
312
+ with self.logger.create_span(
313
+ "AzureKeyVault.delete_secret",
314
+ attributes={
315
+ "service.name": self.service_name,
316
+ "operation.type": "secret_deletion",
317
+ "keyvault.secret_name": secret_name,
318
+ "keyvault.vault_url": self.vault_url
319
+ }
320
+ ):
321
+ if self.secret_client is None:
322
+ error_msg = "Secret client not initialized. Enable secrets during initialization."
323
+ self.logger.error(error_msg)
324
+ raise RuntimeError(error_msg)
325
+
326
+ try:
327
+ self.logger.debug(
328
+ "Deleting secret from Key Vault",
329
+ extra={"secret_name": secret_name, "vault_url": self.vault_url}
330
+ )
331
+
332
+ self.secret_client.begin_delete_secret(secret_name, **kwargs)
333
+
334
+ self.logger.info(
335
+ "Secret deletion initiated successfully",
336
+ extra={"secret_name": secret_name}
337
+ )
338
+
339
+ return True
340
+
341
+ except ResourceNotFoundError:
342
+ self.logger.warning(
343
+ f"Secret '{secret_name}' not found for deletion",
344
+ extra={"secret_name": secret_name}
345
+ )
346
+ return False
347
+ except Exception as e:
348
+ self.logger.error(
349
+ f"Failed to delete secret '{secret_name}': {e}",
350
+ extra={"secret_name": secret_name},
351
+ exc_info=True
352
+ )
353
+ raise
354
+
355
+ def list_secrets(self, **kwargs) -> List[str]:
356
+ """List all secrets in the Key Vault.
357
+
358
+ Args:
359
+ **kwargs: Additional parameters for listing secrets
360
+
361
+ Returns:
362
+ List of secret names
363
+
364
+ Raises:
365
+ RuntimeError: If secret client is not initialized
366
+ Exception: If listing secrets fails
367
+ """
368
+ with self.logger.create_span(
369
+ "AzureKeyVault.list_secrets",
370
+ attributes={
371
+ "service.name": self.service_name,
372
+ "operation.type": "secret_listing",
373
+ "keyvault.vault_url": self.vault_url
374
+ }
375
+ ):
376
+ if self.secret_client is None:
377
+ error_msg = "Secret client not initialized. Enable secrets during initialization."
378
+ self.logger.error(error_msg)
379
+ raise RuntimeError(error_msg)
380
+
381
+ try:
382
+ self.logger.debug(
383
+ "Listing secrets from Key Vault",
384
+ extra={"vault_url": self.vault_url}
385
+ )
386
+
387
+ secret_names = []
388
+ for secret_property in self.secret_client.list_properties_of_secrets(**kwargs):
389
+ secret_names.append(secret_property.name)
390
+
391
+ self.logger.info(
392
+ "Secrets listed successfully",
393
+ extra={"secret_count": len(secret_names)}
394
+ )
395
+
396
+ return secret_names
397
+
398
+ except Exception as e:
399
+ self.logger.error(
400
+ f"Failed to list secrets: {e}",
401
+ exc_info=True
402
+ )
403
+ raise
404
+
405
+ def test_connection(self) -> bool:
406
+ """Test connection to Key Vault by attempting to list secrets.
407
+
408
+ Returns:
409
+ True if connection is successful, False otherwise
410
+ """
411
+ with self.logger.create_span(
412
+ "AzureKeyVault.test_connection",
413
+ attributes={
414
+ "service.name": self.service_name,
415
+ "operation.type": "connection_test",
416
+ "keyvault.vault_url": self.vault_url
417
+ }
418
+ ):
419
+ try:
420
+ self.logger.debug(
421
+ "Testing Key Vault connection",
422
+ extra={"vault_url": self.vault_url}
423
+ )
424
+
425
+ if self.secret_client is not None:
426
+ # Try to list secrets (limited to 1) to test connection
427
+ list(self.secret_client.list_properties_of_secrets(max_page_size=1))
428
+ elif self.key_client is not None:
429
+ # Try to list keys if secrets are disabled
430
+ list(self.key_client.list_properties_of_keys(max_page_size=1))
431
+ elif self.certificate_client is not None:
432
+ # Try to list certificates if keys are disabled
433
+ list(self.certificate_client.list_properties_of_certificates(max_page_size=1))
434
+ else:
435
+ self.logger.error("No clients available for connection testing")
436
+ return False
437
+
438
+ self.logger.info("Key Vault connection test successful")
439
+ return True
440
+
441
+ except Exception as e:
442
+ self.logger.warning(
443
+ f"Key Vault connection test failed: {e}",
444
+ extra={"vault_url": self.vault_url}
445
+ )
446
+ return False
447
+
448
+ def set_correlation_id(self, correlation_id: str):
449
+ """Set correlation ID for request/transaction tracking.
450
+
451
+ Args:
452
+ correlation_id: Unique identifier for transaction correlation
453
+ """
454
+ self.logger.set_correlation_id(correlation_id)
455
+
456
+ def get_correlation_id(self) -> Optional[str]:
457
+ """Get current correlation ID.
458
+
459
+ Returns:
460
+ Current correlation ID if set, otherwise None
461
+ """
462
+ return self.logger.get_correlation_id()
463
+
464
+
465
+ def create_azure_keyvault(
466
+ vault_url: str,
467
+ credential: Optional[TokenCredential] = None,
468
+ azure_identity: Optional[AzureIdentity] = None,
469
+ service_name: str = "azure_keyvault",
470
+ service_version: str = "1.0.0",
471
+ logger: Optional[AzureLogger] = None,
472
+ connection_string: Optional[str] = None,
473
+ enable_secrets: bool = True,
474
+ enable_keys: bool = True,
475
+ enable_certificates: bool = True,
476
+ ) -> AzureKeyVault:
477
+ """Factory function to create AzureKeyVault instance.
478
+
479
+ Provides a convenient way to create an AzureKeyVault instance with
480
+ common configuration patterns. If no credential or azure_identity
481
+ is provided, creates a default AzureIdentity instance.
482
+
483
+ Args:
484
+ vault_url: Azure Key Vault URL
485
+ credential: Azure credential for authentication
486
+ azure_identity: AzureIdentity instance for credential management
487
+ service_name: Service name for tracing context
488
+ service_version: Service version for metadata
489
+ logger: Optional AzureLogger instance
490
+ connection_string: Application Insights connection string
491
+ enable_secrets: Enable secret operations client
492
+ enable_keys: Enable key operations client
493
+ enable_certificates: Enable certificate operations client
494
+
495
+ Returns:
496
+ Configured AzureKeyVault instance
497
+
498
+ Example:
499
+ # Basic usage with default credential
500
+ kv = create_azure_keyvault("https://vault.vault.azure.net/")
501
+
502
+ # With custom service name and specific features
503
+ kv = create_azure_keyvault(
504
+ "https://vault.vault.azure.net/",
505
+ service_name="my_app",
506
+ enable_keys=False,
507
+ enable_certificates=False
508
+ )
509
+ """
510
+ if credential is None and azure_identity is None:
511
+ # Create default AzureIdentity instance
512
+ from ..mgmt.identity import create_azure_identity
513
+ azure_identity = create_azure_identity(
514
+ service_name=f"{service_name}_identity",
515
+ service_version=service_version,
516
+ connection_string=connection_string,
517
+ )
518
+
519
+ return AzureKeyVault(
520
+ vault_url=vault_url,
521
+ credential=credential,
522
+ azure_identity=azure_identity,
523
+ service_name=service_name,
524
+ service_version=service_version,
525
+ logger=logger,
526
+ connection_string=connection_string,
527
+ enable_secrets=enable_secrets,
528
+ enable_keys=enable_keys,
529
+ enable_certificates=enable_certificates,
530
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azpaddypy
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Comprehensive Python logger for Azure, integrating OpenTelemetry for advanced, structured, and distributed tracing.
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Operating System :: OS Independent
@@ -9,9 +9,8 @@ Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: azure-monitor-opentelemetry==1.6.10
11
11
  Requires-Dist: azure-functions==1.23.0
12
- Provides-Extra: dev
13
- Requires-Dist: azure-functions==1.23.0; extra == "dev"
14
- Requires-Dist: pytest==8.4.1; extra == "dev"
15
- Requires-Dist: pytest-asyncio==1.0.0; extra == "dev"
16
- Requires-Dist: anyio==4.9.0; extra == "dev"
12
+ Requires-Dist: azure-identity==1.23.0
13
+ Requires-Dist: azure-keyvault-secrets==4.10.0
14
+ Requires-Dist: azure-keyvault-keys==4.10.0
15
+ Requires-Dist: azure-keyvault-certificates==4.10.0
17
16
  Dynamic: license-file
@@ -0,0 +1,11 @@
1
+ azpaddypy/__init__.py,sha256=hrWNAh4OHZOvm3Pbhq5eUjO-pSRYn0h0W0J87tc-lNI,45
2
+ azpaddypy/mgmt/__init__.py,sha256=-jH8Ftx9C8qu4yF5dMVEapVZhNwG7m4QCUjyutesOoY,278
3
+ azpaddypy/mgmt/identity.py,sha256=mA_krQslMsK_sDob-z-QA0B9khK_JUO2way7xwPopR8,12001
4
+ azpaddypy/mgmt/logging.py,sha256=pivPsHeySF1Dyx6HKCSos7HBOYyJMVeRP25wYZu3Sno,35117
5
+ azpaddypy/resources/__init__.py,sha256=Bvt3VK4RqwoxYpoh6EbLXIR18RuFPKaLP6zLL-icyFk,314
6
+ azpaddypy/resources/keyvault.py,sha256=4J08vLqoLFd1_UUDBji2oG2fatZaPkgnRyT_Z6wHAOc,20312
7
+ azpaddypy-0.3.6.dist-info/licenses/LICENSE,sha256=hQ6t0g2QaewGCQICHqTckBFbMVakGmoyTAzDpmEYV4c,1089
8
+ azpaddypy-0.3.6.dist-info/METADATA,sha256=jTLf8ztnMvo9Mlzma_k3Ck9kR7XpiDGse6SiPca9zeE,665
9
+ azpaddypy-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ azpaddypy-0.3.6.dist-info/top_level.txt,sha256=hsDuboDhT61320ML8X479ezSTwT3rrlDWz1_Z45B2cs,10
11
+ azpaddypy-0.3.6.dist-info/RECORD,,
@@ -1,112 +0,0 @@
1
- import logging
2
- import json
3
- import time
4
- import azure.functions as func
5
- from azpaddypy.mgmt.logging import create_function_logger
6
-
7
- # Initialize the logger
8
- logger = create_function_logger(
9
- function_app_name="test-function-app", function_name="test-function"
10
- )
11
-
12
-
13
- @logger.trace_function(log_args=True, log_result=True)
14
- def process_request(req_body: dict) -> dict:
15
- """Process the request body and return a response"""
16
- # Simulate some processing time
17
- time.sleep(0.1)
18
-
19
- # Log the request processing
20
- logger.info(
21
- "Processing request",
22
- extra={
23
- "request_id": req_body.get("request_id", "unknown"),
24
- "action": req_body.get("action", "unknown"),
25
- },
26
- )
27
-
28
- return {
29
- "status": "success",
30
- "message": "Request processed successfully",
31
- "data": req_body,
32
- }
33
-
34
-
35
- def main(req: func.HttpRequest) -> func.HttpResponse:
36
- """Azure Function entry point"""
37
- try:
38
- # Start timing the request
39
- start_time = time.time()
40
-
41
- # Get request details
42
- method = req.method
43
- url = req.url
44
- headers = dict(req.headers)
45
-
46
- # Log the incoming request
47
- logger.log_request(
48
- method=method,
49
- url=url,
50
- status_code=200, # We'll update this if there's an error
51
- duration_ms=0, # We'll update this at the end
52
- extra={"headers": headers, "request_type": "http_trigger"},
53
- )
54
-
55
- # Create a span for the entire function execution
56
- with logger.create_span("function_execution") as span:
57
- # Add request metadata to the span
58
- span.set_attribute("http.method", method)
59
- span.set_attribute("http.url", url)
60
-
61
- # Parse request body
62
- try:
63
- req_body = req.get_json()
64
- except ValueError:
65
- req_body = {}
66
-
67
- # Log the request body
68
- logger.info("Received request body", extra={"body": req_body})
69
-
70
- # Process the request
71
- result = process_request(req_body)
72
-
73
- # Calculate request duration
74
- duration_ms = (time.time() - start_time) * 1000
75
-
76
- # Log successful completion
77
- logger.log_function_execution(
78
- function_name="main",
79
- duration_ms=duration_ms,
80
- success=True,
81
- extra={"method": method, "url": url},
82
- )
83
-
84
- # Return the response
85
- return func.HttpResponse(
86
- json.dumps(result), mimetype="application/json", status_code=200
87
- )
88
-
89
- except Exception as e:
90
- # Calculate request duration
91
- duration_ms = (time.time() - start_time) * 1000
92
-
93
- # Log the error
94
- logger.error(
95
- f"Error processing request: {str(e)}",
96
- extra={"method": method, "url": url, "error_type": type(e).__name__},
97
- )
98
-
99
- # Log failed execution
100
- logger.log_function_execution(
101
- function_name="main",
102
- duration_ms=duration_ms,
103
- success=False,
104
- extra={"error": str(e), "error_type": type(e).__name__},
105
- )
106
-
107
- # Return error response
108
- return func.HttpResponse(
109
- json.dumps({"status": "error", "message": str(e)}),
110
- mimetype="application/json",
111
- status_code=500,
112
- )
@@ -1,129 +0,0 @@
1
- import logging
2
- import json
3
- import time
4
- import asyncio
5
- import azure.functions as func
6
- from azpaddypy.mgmt.logging import create_function_logger
7
-
8
- app = func.FunctionApp()
9
-
10
- # Initialize the logger
11
- logger = create_function_logger(
12
- function_app_name="test-function-app", function_name="test-function"
13
- )
14
-
15
-
16
- @logger.trace_function(log_args=True, log_result=True)
17
- def process_request(req_body: dict) -> dict:
18
- """Process the request body and return a response"""
19
- # Simulate some processing time
20
- time.sleep(0.1)
21
-
22
- # Log the request processing
23
- logger.info(
24
- "Processing request",
25
- extra={
26
- "request_id": req_body.get("request_id", "unknown"),
27
- "action": req_body.get("action", "unknown"),
28
- },
29
- )
30
-
31
- return {
32
- "status": "success",
33
- "message": "Request processed successfully",
34
- "data": req_body,
35
- }
36
-
37
-
38
- @logger.trace_function(log_args=True, log_result=True)
39
- async def process_request_async(req_body: dict) -> dict:
40
- """Process the request body asynchronously and return a response"""
41
- # Simulate some async processing time
42
- await asyncio.sleep(0.1)
43
-
44
- # Log the request processing
45
- logger.info(
46
- "Processing async request",
47
- extra={
48
- "request_id": req_body.get("request_id", "unknown"),
49
- "action": req_body.get("action", "unknown"),
50
- "is_async": True,
51
- },
52
- )
53
-
54
- return {
55
- "status": "success",
56
- "message": "Async request processed successfully",
57
- "data": req_body,
58
- }
59
-
60
-
61
- @app.function_name(name="test-function")
62
- @app.route(route="test-function", auth_level=func.AuthLevel.ANONYMOUS)
63
- async def test_function(req: func.HttpRequest) -> func.HttpResponse:
64
- """Azure Function HTTP trigger that processes requests both synchronously and asynchronously"""
65
- start_time = time.time()
66
- method = req.method
67
- url = str(req.url)
68
-
69
- try:
70
- # Get request body
71
- req_body = req.get_json()
72
-
73
- # Process request based on the action
74
- action = req_body.get("action", "").lower()
75
-
76
- if action == "async":
77
- # Process request asynchronously
78
- result = await process_request_async(req_body)
79
- else:
80
- # Process request synchronously
81
- result = process_request(req_body)
82
-
83
- # Calculate request duration
84
- duration_ms = (time.time() - start_time) * 1000
85
-
86
- # Log successful request
87
- logger.log_request(
88
- method=method,
89
- url=url,
90
- status_code=200,
91
- duration_ms=duration_ms,
92
- extra={
93
- "request_id": req_body.get("request_id", "unknown"),
94
- "action": action,
95
- "is_async": action == "async",
96
- },
97
- )
98
-
99
- # Return success response
100
- return func.HttpResponse(
101
- json.dumps(result),
102
- mimetype="application/json",
103
- status_code=200,
104
- )
105
-
106
- except Exception as e:
107
- # Calculate request duration
108
- duration_ms = (time.time() - start_time) * 1000
109
-
110
- # Log the error
111
- logger.error(
112
- f"Error processing request: {str(e)}",
113
- extra={"method": method, "url": url, "error_type": type(e).__name__},
114
- )
115
-
116
- # Log failed execution
117
- logger.log_function_execution(
118
- function_name="test_function",
119
- duration_ms=duration_ms,
120
- success=False,
121
- extra={"error": str(e), "error_type": type(e).__name__},
122
- )
123
-
124
- # Return error response
125
- return func.HttpResponse(
126
- json.dumps({"status": "error", "message": str(e)}),
127
- mimetype="application/json",
128
- status_code=500,
129
- )
@@ -1,10 +0,0 @@
1
- azpaddypy/mgmt/__init__.py,sha256=-jH8Ftx9C8qu4yF5dMVEapVZhNwG7m4QCUjyutesOoY,278
2
- azpaddypy/mgmt/identity.py,sha256=mA_krQslMsK_sDob-z-QA0B9khK_JUO2way7xwPopR8,12001
3
- azpaddypy/mgmt/logging.py,sha256=pivPsHeySF1Dyx6HKCSos7HBOYyJMVeRP25wYZu3Sno,35117
4
- azpaddypy/test_function/__init__.py,sha256=0NjUl36wvUWV79GpRwBFkgkBaC6uDZsTdaSVOIHMFEU,3481
5
- azpaddypy/test_function/function_app.py,sha256=6nX54-iq0L1l_hZpD6E744-j79oLxdaldFyWDCpwH7c,3867
6
- azpaddypy-0.3.4.dist-info/licenses/LICENSE,sha256=hQ6t0g2QaewGCQICHqTckBFbMVakGmoyTAzDpmEYV4c,1089
7
- azpaddypy-0.3.4.dist-info/METADATA,sha256=QDqVtpO7pbf_VGJM4e3W6QHV0VtOB3gc6kH8pAoe7jQ,705
8
- azpaddypy-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- azpaddypy-0.3.4.dist-info/top_level.txt,sha256=hsDuboDhT61320ML8X479ezSTwT3rrlDWz1_Z45B2cs,10
10
- azpaddypy-0.3.4.dist-info/RECORD,,