Skip to content

Commit 14ff5e3

Browse files
authored
Merge pull request #66 from traceroot-ai/xinwei_support_tracer_verbose_for_internal_debugging_v1
[Debug] Support new `tracer_verbose` global parameter for internal debugging
2 parents bdbfabd + 2c67fca commit 14ff5e3

File tree

4 files changed

+116
-6
lines changed

4 files changed

+116
-6
lines changed

traceroot/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class TraceRootConfig:
4545
# Verification endpoint
4646
verification_endpoint: str = DEFAULT_VERIFICATION_ENDPOINT
4747

48+
# Verbose logging for debugging
49+
tracer_verbose: bool = False
50+
4851
def __post_init__(self):
4952
self._name = self.name
5053
self._sub_name = (f"{self.service_name}-"

traceroot/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
"TRACEROOT_ENABLE_LOG_CLOUD_EXPORT": "enable_log_cloud_export",
2121
"TRACEROOT_LOCAL_MODE": "local_mode",
2222
"TRACEROOT_VERIFICATION_ENDPOINT": "verification_endpoint",
23+
"TRACEROOT_TRACER_VERBOSE": "tracer_verbose",
2324
}

traceroot/logger.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,45 @@
1313
from traceroot.credentials import CredentialManager
1414

1515

16+
def log_verbose(config: TraceRootConfig, message: str, *args: Any) -> None:
17+
"""Helper function for conditional verbose logging
18+
19+
Args:
20+
config: TraceRootConfig instance
21+
message: Log message to output
22+
*args: Additional arguments to pass to logger
23+
"""
24+
if config.tracer_verbose:
25+
# Import here to avoid circular imports
26+
from traceroot.logger import get_logger
27+
try:
28+
logger = get_logger()
29+
logger.info(f"[TraceRoot] {message}", *args)
30+
except RuntimeError:
31+
# Fallback to print if logger not initialized yet
32+
print(f"[TraceRoot] {message}", *args)
33+
34+
35+
def log_verbose_error(config: TraceRootConfig, message: str, *args:
36+
Any) -> None:
37+
"""Helper function for conditional verbose error logging
38+
39+
Args:
40+
config: TraceRootConfig instance
41+
message: Error message to output
42+
*args: Additional arguments to pass to logger
43+
"""
44+
if config.tracer_verbose:
45+
# Import here to avoid circular imports
46+
from traceroot.logger import get_logger
47+
try:
48+
logger = get_logger()
49+
logger.error(f"[TraceRoot] {message}", *args)
50+
except RuntimeError:
51+
# Fallback to print if logger not initialized yet
52+
print(f"[TraceRoot] ERROR: {message}", *args, file=sys.stderr)
53+
54+
1655
class TraceIdFilter(logging.Filter):
1756
"""Filter to add trace and span IDs to log records"""
1857

@@ -273,6 +312,7 @@ def _setup_cloudwatch_handler(self):
273312
r"""Setup CloudWatch logging handler"""
274313
global _cloudwatch_handler
275314
try:
315+
log_verbose(self.config, "Setting up CloudWatch handler...")
276316
# Fetch AWS credentials from the endpoint
277317
credentials = self.credential_manager.get_credentials()
278318
# Note: config is automatically updated by credential manager
@@ -283,9 +323,11 @@ def _setup_cloudwatch_handler(self):
283323
self.logger.addHandler(cloudwatch_handler)
284324
# Store reference for proper shutdown
285325
_cloudwatch_handler = cloudwatch_handler
286-
except Exception:
326+
log_verbose(self.config, "CloudWatch handler setup completed")
327+
except Exception as e:
287328
# Silently handle credential fetch errors
288-
pass
329+
log_verbose_error(self.config,
330+
f"Failed to setup CloudWatch handler: {e}")
289331

290332
def refresh_credentials(self) -> bool:
291333
"""Manually refresh AWS credentials, update otlp endpoint
@@ -299,17 +341,25 @@ def refresh_credentials(self) -> bool:
299341
if self.config.local_mode or not self.config.enable_span_cloud_export:
300342
# No credentials needed in local mode or
301343
# when span cloud export is disabled
344+
log_verbose(
345+
self.config, f"Skipping credential refresh "
346+
f"(local_mode={self.config.local_mode}, "
347+
f"enable_span_cloud_export="
348+
f"{self.config.enable_span_cloud_export})")
302349
return False
303350

304351
try:
352+
log_verbose(self.config, "Refreshing credentials...")
305353
# Force refresh credentials
306354
credentials = self.credential_manager.get_credentials(
307355
force_refresh=True)
308356
if not credentials:
357+
log_verbose_error(self.config, "Failed to get credentials")
309358
return False
310359

311360
# Only recreate CloudWatch handler if log cloud export is enabled
312361
if self.config.enable_log_cloud_export:
362+
log_verbose(self.config, "Recreating CloudWatch handler...")
313363
# Create new CloudWatch handler first (before removing old one)
314364
new_cloudwatch_handler = self._create_cloudwatch_handler(
315365
credentials)
@@ -329,11 +379,16 @@ def refresh_credentials(self) -> bool:
329379
self.logger.addHandler(new_cloudwatch_handler)
330380
# Store reference for proper shutdown
331381
_cloudwatch_handler = new_cloudwatch_handler
382+
log_verbose(self.config,
383+
"CloudWatch handler recreated successfully")
332384

385+
log_verbose(self.config,
386+
"Credential refresh completed successfully")
333387
return True
334388

335-
except Exception:
389+
except Exception as e:
336390
# Silently handle credential refresh errors
391+
log_verbose_error(self.config, f"Credential refresh failed: {e}")
337392
return False
338393

339394
def _setup_otlp_logging_handler(self):

traceroot/tracer.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
from traceroot.config import TraceRootConfig
2828
from traceroot.constants import ENV_VAR_MAPPING
2929
from traceroot.credentials import CredentialManager
30-
from traceroot.logger import initialize_logger, shutdown_logger
30+
from traceroot.logger import (initialize_logger, log_verbose,
31+
log_verbose_error, shutdown_logger)
3132
from traceroot.utils.config import find_traceroot_config
3233

3334
# Global state
@@ -73,7 +74,7 @@ def _load_env_config() -> dict[str, Any]:
7374
if config_field in [
7475
"enable_span_console_export", "enable_log_console_export",
7576
"enable_span_cloud_export", "enable_log_cloud_export",
76-
"local_mode"
77+
"local_mode", "tracer_verbose"
7778
]:
7879
env_config[config_field] = value.lower() in ('true', '1',
7980
'yes', 'on')
@@ -139,15 +140,29 @@ def init(**kwargs: Any) -> TracerProvider:
139140

140141
_config = config
141142

143+
log_verbose(
144+
config, "Initializing TraceRoot with config:", {
145+
"service_name": config.service_name,
146+
"environment": config.environment,
147+
"local_mode": config.local_mode,
148+
"enable_span_console_export": config.enable_span_console_export,
149+
"enable_span_cloud_export": config.enable_span_cloud_export,
150+
"enable_log_console_export": config.enable_log_console_export,
151+
"enable_log_cloud_export": config.enable_log_cloud_export,
152+
"tracer_verbose": config.tracer_verbose
153+
})
154+
142155
# Initialize shared credential manager
143156
global _credential_manager
144157
_credential_manager = CredentialManager(config)
145158

146159
# TODO(xinwei): separate logger initialization from tracer initialization.
147160
# Initialize logger first
161+
log_verbose(config, "Initializing logger...")
148162
initialize_logger(config, _credential_manager)
149163

150164
# Create resource with service information
165+
log_verbose(config, "Creating OpenTelemetry resource...")
151166
resource = Resource(
152167
attributes={
153168
SERVICE_NAME: config.service_name,
@@ -159,39 +174,50 @@ def init(**kwargs: Any) -> TracerProvider:
159174
})
160175

161176
# Create tracer provider
177+
log_verbose(config, "Creating tracer provider...")
162178
provider = TracerProvider(resource=resource)
163179

164180
# Add span processors based on configuration
165181
if config.enable_span_console_export:
182+
log_verbose(config, "Adding console span processor...")
166183
console_processor = SimpleSpanProcessor(ConsoleSpanExporter())
167184
provider.add_span_processor(console_processor)
168185

169186
# Only add cloud export if enabled
170187
if config.enable_span_cloud_export:
188+
log_verbose(config, "Setting up cloud span export...")
171189
# Ensure we have fresh credentials and OTLP
172190
# endpoint before creating exporter
173191
if _credential_manager:
192+
log_verbose(config, "Getting credentials for cloud export...")
174193
_credential_manager.get_credentials()
175194

195+
log_verbose(
196+
config, f"Creating OTLP span exporter with endpoint: "
197+
f"{config.otlp_endpoint}")
176198
exporter = OTLPSpanExporter(endpoint=config.otlp_endpoint)
177199
batch_processor = BatchSpanProcessor(exporter)
178200
provider.add_span_processor(batch_processor)
201+
log_verbose(config, "Added batch span processor for cloud export")
179202

180203
# Set as global tracer provider
204+
log_verbose(config, "Setting global tracer provider...")
181205
otel_trace.set_tracer_provider(provider)
182206
_tracer_provider = provider
183207

184208
# Configure propagators to enable distributed tracing
185209
# This is crucial for FastAPI to properly extract trace context from
186210
# HTTP headers
187211
# and create child spans instead of new root spans
212+
log_verbose(config, "Configuring propagators for distributed tracing...")
188213
propagator = CompositePropagator([
189214
TraceContextTextMapPropagator(
190215
), # Handles traceparent/tracestate headers (W3C Trace Context)
191216
W3CBaggagePropagator(), # Handles baggage header (W3C Baggage)
192217
])
193218
set_global_textmap(propagator)
194219

220+
log_verbose(config, "TraceRoot initialization completed successfully")
195221
return provider
196222

197223

@@ -205,6 +231,8 @@ def shutdown_tracing() -> None:
205231
global _tracer_provider, _config, _credential_manager
206232

207233
if _tracer_provider is not None:
234+
if _config and _config.tracer_verbose:
235+
log_verbose(_config, "Shutting down tracing...")
208236
_tracer_provider.shutdown()
209237
_tracer_provider = None
210238
_config = None
@@ -247,6 +275,11 @@ def _trace(function: Callable, options: TraceOptions, *args: Any,
247275
"""Internal context manager for tracing function execution"""
248276
# no-op if tracing is not initialized
249277
if not is_initialized():
278+
if _config and _config.tracer_verbose:
279+
log_verbose(
280+
_config,
281+
"Tracing not initialized, skipping trace for function:",
282+
function.__name__)
250283
yield None
251284
return
252285

@@ -257,10 +290,19 @@ def _trace(function: Callable, options: TraceOptions, *args: Any,
257290
# Get span name from options
258291
_span_name = options.get_span_name(function)
259292

293+
if _config and _config.tracer_verbose:
294+
log_verbose(
295+
_config, f"Starting span: {_span_name} for function: "
296+
f"{function.__name__}")
297+
260298
# Create and start new span
261299
_span = tracer.start_as_current_span(_span_name)
262-
except Exception:
300+
except Exception as e:
263301
# If span creation fails, yield None and continue without tracing
302+
if _config and _config.tracer_verbose:
303+
log_verbose_error(
304+
_config,
305+
f"Failed to create span for function {function.__name__}: {e}")
264306
yield None
265307
return
266308

@@ -273,8 +315,17 @@ def _trace(function: Callable, options: TraceOptions, *args: Any,
273315
span.set_attribute("service_environment", _config.environment)
274316
span.set_attribute("telemetry_sdk_language", "python")
275317

318+
if _config and _config.tracer_verbose:
319+
log_verbose(
320+
_config,
321+
f"Setting span attributes for function: {function.__name__}")
322+
276323
# Add parameter attributes if requested
277324
if options.trace_params:
325+
if _config and _config.tracer_verbose:
326+
log_verbose(
327+
_config,
328+
f"Tracing parameters for function: {function.__name__}")
278329
parameter_values = _params_to_dict(
279330
function,
280331
options.trace_params,

0 commit comments

Comments
 (0)