azpaddypy 0.1.9__py3-none-any.whl → 0.2.0__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
@@ -21,6 +21,33 @@ class AzureLogger:
21
21
  using Azure Monitor OpenTelemetry integration with advanced tracing capabilities.
22
22
  """
23
23
 
24
+ _instances = {}
25
+ _tracer = None
26
+
27
+ @classmethod
28
+ def get_instance(
29
+ cls,
30
+ service_name: str,
31
+ service_version: str = "1.0.0",
32
+ connection_string: Optional[str] = None,
33
+ log_level: int = logging.INFO,
34
+ enable_console_logging: bool = True,
35
+ custom_resource_attributes: Optional[Dict[str, str]] = None,
36
+ ):
37
+ """
38
+ Get or create a singleton AzureLogger instance for a given service_name.
39
+ """
40
+ if service_name not in cls._instances:
41
+ cls._instances[service_name] = cls(
42
+ service_name=service_name,
43
+ service_version=service_version,
44
+ connection_string=connection_string,
45
+ log_level=log_level,
46
+ enable_console_logging=enable_console_logging,
47
+ custom_resource_attributes=custom_resource_attributes,
48
+ )
49
+ return cls._instances[service_name]
50
+
24
51
  def __init__(
25
52
  self,
26
53
  service_name: str,
@@ -30,17 +57,26 @@ class AzureLogger:
30
57
  enable_console_logging: bool = True,
31
58
  custom_resource_attributes: Optional[Dict[str, str]] = None,
32
59
  ):
33
- """
34
- Initialize the Azure Logger with OpenTelemetry tracing
60
+ # On first initialization, clean up all lingering loggers, tracers, and meters
61
+ if not AzureLogger._instances:
62
+ # Remove all handlers from all loggers except the one being initialized
63
+ for name, logger in logging.root.manager.loggerDict.items():
64
+ if isinstance(logger, logging.Logger) and name != service_name:
65
+ logger.handlers.clear()
66
+ # Reset OpenTelemetry tracer and meter providers
67
+ try:
68
+ from opentelemetry import trace, metrics
69
+ from opentelemetry.sdk.trace import TracerProvider
70
+ from opentelemetry.sdk.metrics import MeterProvider
35
71
 
36
- Args:
37
- service_name: Name of your service/application
38
- service_version: Version of your service
39
- connection_string: Application Insights connection string
40
- log_level: Logging level (default: INFO)
41
- enable_console_logging: Enable console output for local development
42
- custom_resource_attributes: Additional resource attributes
43
- """
72
+ trace.set_tracer_provider(TracerProvider())
73
+ metrics.set_meter_provider(MeterProvider())
74
+ except Exception as e:
75
+ print(f"Warning: Failed to reset OpenTelemetry providers: {e}")
76
+ # Prevent re-initialization if already exists
77
+ if hasattr(self, "_initialized") and self._initialized:
78
+ return
79
+ self._initialized = True
44
80
  self.service_name = service_name
45
81
  self.service_version = service_version
46
82
  self.connection_string = connection_string or os.getenv(
@@ -75,19 +111,19 @@ class AzureLogger:
75
111
  "Warning: No Application Insights connection string found. Telemetry disabled."
76
112
  )
77
113
 
78
- # Set up logger without instrumentor
114
+ # Set up logger without instrumentation
79
115
  self.logger = logging.getLogger(service_name)
80
116
  self.logger.setLevel(log_level)
81
117
 
82
- # Clear existing handlers to avoid duplicates
83
- self.logger.handlers.clear()
84
-
85
- # Add console handler for local development
86
- if enable_console_logging:
87
- self._setup_console_handler()
118
+ # Only add handlers if none exist
119
+ if not self.logger.handlers:
120
+ if enable_console_logging:
121
+ self._setup_console_handler()
88
122
 
89
- # Get tracer for manual span creation
90
- self.tracer = trace.get_tracer(__name__)
123
+ # Get tracer for manual span creation (singleton per class)
124
+ if AzureLogger._tracer is None:
125
+ AzureLogger._tracer = trace.get_tracer(__name__)
126
+ self.tracer = AzureLogger._tracer
91
127
 
92
128
  # Initialize correlation context
93
129
  self._correlation_id = None
@@ -174,7 +210,7 @@ class AzureLogger:
174
210
  enhanced_extra.update(extra)
175
211
 
176
212
  return enhanced_extra
177
-
213
+
178
214
  def exception(self, message: str, extra: Optional[Dict[str, Any]] = None):
179
215
  """Log debug message with enhanced context"""
180
216
  self.logger.exception(message, extra=self._enhance_extra(extra))
@@ -606,6 +642,36 @@ class AzureLogger:
606
642
  except Exception as e:
607
643
  self.warning(f"Failed to flush telemetry: {e}")
608
644
 
645
+ async def flush_async(self):
646
+ """Asynchronously flush all pending telemetry data"""
647
+ if self._telemetry_enabled:
648
+ try:
649
+ from opentelemetry.sdk.trace import TracerProvider
650
+
651
+ tracer_provider = trace.get_tracer_provider()
652
+ if hasattr(tracer_provider, "force_flush"):
653
+ await asyncio.to_thread(
654
+ tracer_provider.force_flush, timeout_millis=5000
655
+ )
656
+ except Exception as e:
657
+ self.warning(f"Failed to async flush telemetry: {e}")
658
+
659
+ def add_handler(self, handler: logging.Handler):
660
+ """Add a logging handler to the logger."""
661
+ self.logger.addHandler(handler)
662
+
663
+ def remove_handler(self, handler: logging.Handler):
664
+ """Remove a logging handler from the logger."""
665
+ self.logger.removeHandler(handler)
666
+
667
+ def list_handlers(self) -> list:
668
+ """List all handlers attached to the logger."""
669
+ return self.logger.handlers
670
+
671
+ def clear_handlers(self):
672
+ """Remove all handlers from the logger."""
673
+ self.logger.handlers.clear()
674
+
609
675
 
610
676
  # Factory functions for easy instantiation
611
677
  def create_app_logger(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azpaddypy
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Add your description here
5
5
  Classifier: Operating System :: OS Independent
6
6
  Requires-Python: >=3.11
@@ -0,0 +1,9 @@
1
+ azpaddypy/mgmt/__init__.py,sha256=5-0eZuJMZlCONZNJ5hEvWXfvLIM36mg7FsEVs32bY_A,183
2
+ azpaddypy/mgmt/logging.py,sha256=UYlep7e3TddWo1EtJliGg45Zv-22iSbIYdC8Iz1STKU,28462
3
+ azpaddypy/test_function/__init__.py,sha256=0NjUl36wvUWV79GpRwBFkgkBaC6uDZsTdaSVOIHMFEU,3481
4
+ azpaddypy/test_function/function_app.py,sha256=6nX54-iq0L1l_hZpD6E744-j79oLxdaldFyWDCpwH7c,3867
5
+ azpaddypy-0.2.0.dist-info/licenses/LICENSE,sha256=hQ6t0g2QaewGCQICHqTckBFbMVakGmoyTAzDpmEYV4c,1089
6
+ azpaddypy-0.2.0.dist-info/METADATA,sha256=EDK4QI8b__iy5AX6PmYxKsUmegQStO_y9WUzQiAZ5zw,304
7
+ azpaddypy-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ azpaddypy-0.2.0.dist-info/top_level.txt,sha256=hsDuboDhT61320ML8X479ezSTwT3rrlDWz1_Z45B2cs,10
9
+ azpaddypy-0.2.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- azpaddypy/mgmt/__init__.py,sha256=5-0eZuJMZlCONZNJ5hEvWXfvLIM36mg7FsEVs32bY_A,183
2
- azpaddypy/mgmt/logging.py,sha256=TCOjec_DSvN4wqqzGihn24XXjUZ2NpFm2cG3UxYUWSA,25666
3
- azpaddypy/test_function/__init__.py,sha256=0NjUl36wvUWV79GpRwBFkgkBaC6uDZsTdaSVOIHMFEU,3481
4
- azpaddypy/test_function/function_app.py,sha256=6nX54-iq0L1l_hZpD6E744-j79oLxdaldFyWDCpwH7c,3867
5
- azpaddypy-0.1.9.dist-info/licenses/LICENSE,sha256=hQ6t0g2QaewGCQICHqTckBFbMVakGmoyTAzDpmEYV4c,1089
6
- azpaddypy-0.1.9.dist-info/METADATA,sha256=srjAa16qE_vCvXvKucLGUCC00EnxPB7CIU4L80_WWwQ,304
7
- azpaddypy-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- azpaddypy-0.1.9.dist-info/top_level.txt,sha256=hsDuboDhT61320ML8X479ezSTwT3rrlDWz1_Z45B2cs,10
9
- azpaddypy-0.1.9.dist-info/RECORD,,