azpaddypy 0.3.2__py3-none-any.whl → 0.3.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.
azpaddypy/mgmt/logging.py CHANGED
@@ -3,6 +3,7 @@ import os
3
3
  import functools
4
4
  import time
5
5
  import asyncio
6
+ import uuid
6
7
  from typing import Optional, Dict, Any, Union, Callable
7
8
  from datetime import datetime
8
9
  from azure.monitor.opentelemetry import configure_azure_monitor
@@ -19,12 +20,23 @@ class AzureLogger:
19
20
  tracking, baggage propagation, and automated function tracing for Azure
20
21
  applications with seamless local development support.
21
22
 
23
+ CLOUD ROLE NAME INTEGRATION:
24
+ The service_name parameter automatically sets the cloud role name for
25
+ Application Insights. When multiple services emit telemetry to the same
26
+ Application Insights resource, each service will appear as a separate
27
+ node on the Application Map, enabling proper service topology visualization.
28
+
29
+ CORRELATION ID AUTOMATION:
30
+ The trace_function decorator automatically generates UUID4 correlation IDs
31
+ when none are manually set, ensuring consistent distributed tracing across
32
+ all function calls without requiring manual configuration.
33
+
22
34
  Supports all standard logging levels (debug, info, warning, error, exception,
23
35
  critical) with enhanced context including trace IDs, correlation IDs, and
24
36
  baggage propagation.
25
37
 
26
38
  Attributes:
27
- service_name: Service identifier for telemetry
39
+ service_name: Service identifier for telemetry and cloud role name
28
40
  service_version: Service version for context
29
41
  connection_string: Application Insights connection string
30
42
  logger: Python logger instance
@@ -40,17 +52,24 @@ class AzureLogger:
40
52
  enable_console_logging: bool = True,
41
53
  custom_resource_attributes: Optional[Dict[str, str]] = None,
42
54
  instrumentation_options: Optional[Dict[str, Any]] = None,
55
+ cloud_role_name: Optional[str] = None,
43
56
  ):
44
57
  """Initialize Azure Logger with OpenTelemetry tracing.
45
58
 
59
+ The service_name parameter automatically sets the cloud role name for
60
+ Application Insights Application Map visualization. When multiple services
61
+ emit telemetry to the same Application Insights resource, each service
62
+ will appear as a separate node on the Application Map.
63
+
46
64
  Args:
47
- service_name: Service identifier for telemetry
65
+ service_name: Service identifier for telemetry and cloud role name
48
66
  service_version: Service version for metadata
49
67
  connection_string: Application Insights connection string
50
68
  log_level: Python logging level (default: INFO)
51
69
  enable_console_logging: Enable console output for local development
52
70
  custom_resource_attributes: Additional OpenTelemetry resource attributes
53
71
  instrumentation_options: Azure Monitor instrumentation options
72
+ cloud_role_name: Override cloud role name (defaults to service_name)
54
73
  """
55
74
  self.service_name = service_name
56
75
  self.service_version = service_version
@@ -58,9 +77,14 @@ class AzureLogger:
58
77
  "APPLICATIONINSIGHTS_CONNECTION_STRING"
59
78
  )
60
79
 
80
+ # Use explicit cloud role name or default to service name
81
+ effective_cloud_role_name = cloud_role_name or service_name
82
+ self.cloud_role_name = effective_cloud_role_name
83
+
61
84
  # Configure resource attributes
85
+ # NOTE: service.name automatically maps to cloud role name in Application Insights
62
86
  resource_attributes = {
63
- "service.name": service_name,
87
+ "service.name": effective_cloud_role_name,
64
88
  "service.version": service_version,
65
89
  "service.instance.id": os.getenv("WEBSITE_INSTANCE_ID", "local"),
66
90
  }
@@ -100,7 +124,8 @@ class AzureLogger:
100
124
  self._correlation_id = None
101
125
 
102
126
  self.info(
103
- f"Azure Logger initialized for service '{service_name}' v{service_version}"
127
+ f"Azure Logger initialized for service '{service_name}' v{service_version} "
128
+ f"(cloud role: '{effective_cloud_role_name}')"
104
129
  )
105
130
 
106
131
  def _setup_console_handler(self):
@@ -115,16 +140,27 @@ class AzureLogger:
115
140
  def set_correlation_id(self, correlation_id: str):
116
141
  """Set correlation ID for request/transaction tracking.
117
142
 
143
+ Manually sets the correlation ID that will be used for all subsequent
144
+ tracing operations. This value takes precedence over auto-generated
145
+ correlation IDs in the trace_function decorator.
146
+
118
147
  Args:
119
148
  correlation_id: Unique identifier for transaction correlation
149
+
150
+ Note:
151
+ If not set manually, the trace_function decorator will automatically
152
+ generate a UUID4 correlation_id on first use.
120
153
  """
121
154
  self._correlation_id = correlation_id
122
155
 
123
156
  def get_correlation_id(self) -> Optional[str]:
124
157
  """Get current correlation ID.
125
158
 
159
+ Returns the currently active correlation ID, whether manually set
160
+ or automatically generated by the trace_function decorator.
161
+
126
162
  Returns:
127
- Current correlation ID if set, otherwise None
163
+ Current correlation ID if set (manual or auto-generated), otherwise None
128
164
  """
129
165
  return self._correlation_id
130
166
 
@@ -167,8 +203,16 @@ class AzureLogger:
167
203
 
168
204
  Returns:
169
205
  Enhanced dictionary with service context, correlation ID, trace
170
- context, and baggage items
206
+ context, and baggage items, with built-in LogRecord attributes filtered out
171
207
  """
208
+ # Define built-in LogRecord attributes that should not be overwritten
209
+ reserved_attributes = {
210
+ 'name', 'msg', 'args', 'levelname', 'levelno', 'pathname', 'filename',
211
+ 'module', 'exc_info', 'exc_text', 'stack_info', 'lineno', 'funcName',
212
+ 'created', 'msecs', 'relativeCreated', 'thread', 'threadName',
213
+ 'processName', 'process', 'getMessage'
214
+ }
215
+
172
216
  enhanced_extra = {
173
217
  "service_name": self.service_name,
174
218
  "service_version": self.service_version,
@@ -185,13 +229,34 @@ class AzureLogger:
185
229
  enhanced_extra["trace_id"] = format(span_context.trace_id, "032x")
186
230
  enhanced_extra["span_id"] = format(span_context.span_id, "016x")
187
231
 
188
- # Add baggage items
232
+ # Add baggage items, filtering out any reserved attribute names
189
233
  baggage_items = self.get_all_baggage()
190
234
  if baggage_items:
191
- enhanced_extra["baggage"] = baggage_items
235
+ # Filter baggage items to avoid conflicts with LogRecord attributes
236
+ filtered_baggage = {k: v for k, v in baggage_items.items() if k not in reserved_attributes}
237
+ if filtered_baggage:
238
+ enhanced_extra["baggage"] = filtered_baggage
239
+
240
+ # Log warning if any baggage keys were filtered out
241
+ filtered_baggage_keys = set(baggage_items.keys()) - set(filtered_baggage.keys())
242
+ if filtered_baggage_keys:
243
+ # Use the base logger directly to avoid infinite recursion
244
+ self.logger.warning(
245
+ f"Filtered out reserved LogRecord attributes from baggage: {filtered_baggage_keys}"
246
+ )
192
247
 
193
248
  if isinstance(extra, dict):
194
- enhanced_extra.update(extra)
249
+ # Filter out any keys that would conflict with built-in LogRecord attributes
250
+ filtered_extra = {k: v for k, v in extra.items() if k not in reserved_attributes}
251
+ enhanced_extra.update(filtered_extra)
252
+
253
+ # Log warning if any keys were filtered out
254
+ filtered_keys = set(extra.keys()) - set(filtered_extra.keys())
255
+ if filtered_keys:
256
+ # Use the base logger directly to avoid infinite recursion
257
+ self.logger.warning(
258
+ f"Filtered out reserved LogRecord attributes from extra data: {filtered_keys}"
259
+ )
195
260
 
196
261
  return enhanced_extra
197
262
 
@@ -481,16 +546,38 @@ class AzureLogger:
481
546
  log_args: bool = True,
482
547
  log_result: bool = False
483
548
  ) -> Callable:
484
- """Decorator for automatic function execution tracing.
549
+ """Decorator for automatic function execution tracing with correlation ID support.
485
550
 
486
551
  Supports both synchronous and asynchronous functions with comprehensive
487
- logging and OpenTelemetry span creation.
552
+ logging and OpenTelemetry span creation. Automatically generates and manages
553
+ correlation IDs for distributed tracing.
554
+
555
+ CORRELATION ID AUTOMATION:
556
+ - If no correlation_id is set, automatically generates a UUID4 correlation_id
557
+ - If correlation_id is manually set, preserves the existing value
558
+ - Correlation_id is automatically added to all spans and log entries
559
+ - Ensures consistent tracing across function calls
488
560
 
489
561
  Args:
490
562
  function_name: Custom span name (defaults to function name)
491
563
  log_execution: Whether to log execution metrics
492
564
  log_args: Whether to log function arguments
493
565
  log_result: Whether to log function result
566
+
567
+ Returns:
568
+ Decorated function with automatic tracing and correlation ID support
569
+
570
+ Example:
571
+ # Automatic correlation_id generation
572
+ @logger.trace_function()
573
+ def my_function():
574
+ return "result"
575
+
576
+ # Manual correlation_id (preserved)
577
+ logger.set_correlation_id("manual-id")
578
+ @logger.trace_function()
579
+ def my_function():
580
+ return "result"
494
581
  """
495
582
 
496
583
  def decorator(func):
@@ -498,6 +585,11 @@ class AzureLogger:
498
585
  async def async_wrapper(*args, **kwargs):
499
586
  span_name = function_name or f"{func.__module__}.{func.__name__}"
500
587
  with self.tracer.start_as_current_span(span_name) as span:
588
+ # Auto-generate correlation_id if not set - ensures consistent tracing
589
+ # Manual correlation_ids take precedence over auto-generated ones
590
+ if not self._correlation_id:
591
+ self._correlation_id = str(uuid.uuid4())
592
+
501
593
  self._setup_span_for_function_trace(
502
594
  span, func, True, log_args, args, kwargs, log_result, log_execution
503
595
  )
@@ -531,6 +623,11 @@ class AzureLogger:
531
623
  def sync_wrapper(*args, **kwargs):
532
624
  span_name = function_name or f"{func.__module__}.{func.__name__}"
533
625
  with self.tracer.start_as_current_span(span_name) as span:
626
+ # Auto-generate correlation_id if not set - ensures consistent tracing
627
+ # Manual correlation_ids take precedence over auto-generated ones
628
+ if not self._correlation_id:
629
+ self._correlation_id = str(uuid.uuid4())
630
+
534
631
  self._setup_span_for_function_trace(
535
632
  span, func, False, log_args, args, kwargs, log_result, log_execution
536
633
  )
@@ -675,19 +772,23 @@ def create_app_logger(
675
772
  enable_console_logging: bool = True,
676
773
  custom_resource_attributes: Optional[Dict[str, str]] = None,
677
774
  instrumentation_options: Optional[Dict[str, Any]] = None,
775
+ cloud_role_name: Optional[str] = None,
678
776
  ) -> AzureLogger:
679
777
  """Create cached AzureLogger instance for applications.
680
778
 
681
779
  Returns existing logger if one with same configuration exists.
780
+ The service_name automatically becomes the cloud role name in Application Insights
781
+ unless explicitly overridden with cloud_role_name parameter.
682
782
 
683
783
  Args:
684
- service_name: Service identifier for telemetry
784
+ service_name: Service identifier for telemetry and cloud role name
685
785
  service_version: Service version for metadata
686
786
  connection_string: Application Insights connection string
687
787
  log_level: Python logging level
688
788
  enable_console_logging: Enable console output
689
789
  custom_resource_attributes: Additional OpenTelemetry resource attributes
690
790
  instrumentation_options: Azure Monitor instrumentation options
791
+ cloud_role_name: Override cloud role name (defaults to service_name)
691
792
 
692
793
  Returns:
693
794
  Configured AzureLogger instance
@@ -709,6 +810,7 @@ def create_app_logger(
709
810
  log_level,
710
811
  enable_console_logging,
711
812
  attr_items,
813
+ cloud_role_name,
712
814
  )
713
815
 
714
816
  if params_key in _loggers:
@@ -721,6 +823,8 @@ def create_app_logger(
721
823
  log_level=log_level,
722
824
  enable_console_logging=enable_console_logging,
723
825
  custom_resource_attributes=custom_resource_attributes,
826
+ instrumentation_options=instrumentation_options,
827
+ cloud_role_name=cloud_role_name,
724
828
  )
725
829
  _loggers[params_key] = logger
726
830
  return logger
@@ -732,15 +836,21 @@ def create_function_logger(
732
836
  service_version: str = "1.0.0",
733
837
  connection_string: Optional[str] = None,
734
838
  instrumentation_options: Optional[Dict[str, Any]] = None,
839
+ cloud_role_name: Optional[str] = None,
735
840
  ) -> AzureLogger:
736
841
  """Create AzureLogger optimized for Azure Functions.
737
842
 
843
+ Automatically creates cloud role name in the format '{function_app_name}.{function_name}'
844
+ unless explicitly overridden. This ensures each function appears as a separate
845
+ component in the Application Insights Application Map.
846
+
738
847
  Args:
739
848
  function_app_name: Azure Function App name
740
849
  function_name: Specific function name
741
850
  service_version: Service version for metadata
742
851
  connection_string: Application Insights connection string
743
852
  instrumentation_options: Azure Monitor instrumentation options
853
+ cloud_role_name: Override cloud role name (defaults to '{function_app_name}.{function_name}')
744
854
 
745
855
  Returns:
746
856
  Configured AzureLogger with Azure Functions context
@@ -751,10 +861,13 @@ def create_function_logger(
751
861
  "azure.resource.type": "function",
752
862
  }
753
863
 
864
+ default_service_name = f"{function_app_name}.{function_name}"
865
+
754
866
  return create_app_logger(
755
- service_name=f"{function_app_name}.{function_name}",
867
+ service_name=default_service_name,
756
868
  service_version=service_version,
757
869
  connection_string=connection_string,
758
870
  custom_resource_attributes=custom_attributes,
759
871
  instrumentation_options=instrumentation_options,
872
+ cloud_role_name=cloud_role_name,
760
873
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azpaddypy
3
- Version: 0.3.2
3
+ Version: 0.3.4
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
@@ -1,10 +1,10 @@
1
1
  azpaddypy/mgmt/__init__.py,sha256=-jH8Ftx9C8qu4yF5dMVEapVZhNwG7m4QCUjyutesOoY,278
2
2
  azpaddypy/mgmt/identity.py,sha256=mA_krQslMsK_sDob-z-QA0B9khK_JUO2way7xwPopR8,12001
3
- azpaddypy/mgmt/logging.py,sha256=5sAqUWwjX6nZMgqK8BurMoWhId4JJ21RZ9H29gXWqA4,28835
3
+ azpaddypy/mgmt/logging.py,sha256=pivPsHeySF1Dyx6HKCSos7HBOYyJMVeRP25wYZu3Sno,35117
4
4
  azpaddypy/test_function/__init__.py,sha256=0NjUl36wvUWV79GpRwBFkgkBaC6uDZsTdaSVOIHMFEU,3481
5
5
  azpaddypy/test_function/function_app.py,sha256=6nX54-iq0L1l_hZpD6E744-j79oLxdaldFyWDCpwH7c,3867
6
- azpaddypy-0.3.2.dist-info/licenses/LICENSE,sha256=hQ6t0g2QaewGCQICHqTckBFbMVakGmoyTAzDpmEYV4c,1089
7
- azpaddypy-0.3.2.dist-info/METADATA,sha256=rR7GtG1mvgOR0WWYGkGt-2ZTtdwcye-GO8ea2QXNhos,705
8
- azpaddypy-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- azpaddypy-0.3.2.dist-info/top_level.txt,sha256=hsDuboDhT61320ML8X479ezSTwT3rrlDWz1_Z45B2cs,10
10
- azpaddypy-0.3.2.dist-info/RECORD,,
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,,