0
# Debugging
1
2
Tools for debugging, introspection, and monitoring eventlet applications including tracing, hub inspection, and performance analysis. These utilities help diagnose issues in concurrent greenthread applications.
3
4
## Capabilities
5
6
### Execution Tracing
7
8
Tools for tracing greenthread execution and function calls.
9
10
```python { .api }
11
def spew():
12
"""
13
Install a detailed tracing hook that prints every function call.
14
Useful for debugging greenthread execution flow.
15
16
Returns:
17
None
18
19
Note:
20
Produces very verbose output. Use unspew() to disable.
21
"""
22
23
def unspew():
24
"""
25
Remove the tracing hook installed by spew().
26
27
Returns:
28
None
29
"""
30
```
31
32
### Hub Inspection
33
34
Tools for inspecting the event hub's internal state.
35
36
```python { .api }
37
def format_hub_listeners():
38
"""
39
Format the current hub's file descriptor listeners as a string.
40
41
Returns:
42
str: formatted information about active listeners
43
"""
44
45
def format_hub_timers():
46
"""
47
Format the current hub's timers as a string.
48
49
Returns:
50
str: formatted information about scheduled timers
51
"""
52
53
def hub_listener_stacks(enabled):
54
"""
55
Toggle recording of stack traces for hub listeners.
56
57
Parameters:
58
- enabled: bool, whether to record listener stack traces
59
60
Returns:
61
None
62
"""
63
64
def hub_timer_stacks(enabled):
65
"""
66
Toggle recording of stack traces for hub timers.
67
68
Parameters:
69
- enabled: bool, whether to record timer stack traces
70
71
Returns:
72
None
73
"""
74
75
def hub_exceptions(enabled):
76
"""
77
Toggle printing of exceptions that occur in hub timers.
78
79
Parameters:
80
- enabled: bool, whether to print hub timer exceptions
81
82
Returns:
83
None
84
"""
85
86
def hub_prevent_multiple_readers(enabled):
87
"""
88
Toggle prevention of multiple readers on the same file descriptor.
89
90
Parameters:
91
- enabled: bool, whether to prevent multiple readers
92
93
Returns:
94
None
95
"""
96
97
def hub_blocking_detection(enabled, resolution=1):
98
"""
99
Toggle detection of blocking behavior in the hub.
100
101
Parameters:
102
- enabled: bool, whether to enable blocking detection
103
- resolution: float, detection resolution in seconds
104
105
Returns:
106
None
107
"""
108
```
109
110
### Thread Pool Debugging
111
112
Debug information for thread pool operations.
113
114
```python { .api }
115
def tpool_exceptions(enabled):
116
"""
117
Toggle printing of exceptions that occur in tpool operations.
118
119
Parameters:
120
- enabled: bool, whether to print tpool exceptions
121
122
Returns:
123
None
124
"""
125
```
126
127
### System Information
128
129
Functions for gathering system and runtime information.
130
131
```python { .api }
132
def format_asyncio_info():
133
"""
134
Format information about asyncio event loop and tasks.
135
136
Returns:
137
str: formatted asyncio debugging information
138
"""
139
140
def format_threads_info():
141
"""
142
Format information about active threads.
143
144
Returns:
145
str: formatted thread debugging information
146
"""
147
```
148
149
### Interactive Debugging
150
151
Backdoor server for interactive debugging of running applications.
152
153
```python { .api }
154
def backdoor_server(sock, locals=None):
155
"""
156
Run a backdoor server on the given socket for interactive debugging.
157
158
Parameters:
159
- sock: listening socket for backdoor connections
160
- locals: dict, local variables available in backdoor session
161
162
Returns:
163
None
164
"""
165
166
def backdoor(port, host='127.0.0.1', locals=None):
167
"""
168
Set up an interactive console backdoor on the specified port.
169
170
Parameters:
171
- port: int, port number for backdoor server
172
- host: str, host address to bind to
173
- locals: dict, local variables available in backdoor session
174
175
Returns:
176
None
177
"""
178
```
179
180
## Usage Examples
181
182
### Basic Debugging Setup
183
184
```python
185
import eventlet
186
import eventlet.debug
187
import time
188
189
def problematic_function():
190
"""Function that might have issues"""
191
print("Starting problematic function")
192
193
# Simulate some work
194
eventlet.sleep(1.0)
195
196
# Simulate a potential issue
197
for i in range(1000000):
198
if i % 100000 == 0:
199
eventlet.sleep(0) # Yield control
200
201
print("Problematic function completed")
202
203
def debug_basic_example():
204
"""Basic debugging example"""
205
206
print("=== Basic Debugging Example ===")
207
208
# Enable various debugging features
209
print("Enabling hub exception printing...")
210
eventlet.debug.hub_exceptions(True)
211
212
print("Enabling tpool exception printing...")
213
eventlet.debug.tpool_exceptions(True)
214
215
print("Enabling hub blocking detection...")
216
eventlet.debug.hub_blocking_detection(True, resolution=0.1)
217
218
# Run some potentially problematic code
219
def worker(worker_id):
220
"""Worker that might block"""
221
try:
222
print(f"Worker {worker_id} starting")
223
224
# This might cause blocking detection to trigger
225
time.sleep(0.2) # Intentional blocking call
226
227
# Some eventlet operations
228
eventlet.sleep(0.5)
229
230
print(f"Worker {worker_id} completed")
231
except Exception as e:
232
print(f"Worker {worker_id} error: {e}")
233
234
# Start workers
235
greenthreads = []
236
for i in range(3):
237
gt = eventlet.spawn(worker, i+1)
238
greenthreads.append(gt)
239
240
# Wait for completion
241
for gt in greenthreads:
242
gt.wait()
243
244
print("Basic debugging example completed")
245
246
if __name__ == "__main__":
247
debug_basic_example()
248
```
249
250
### Hub Inspection and Monitoring
251
252
```python
253
import eventlet
254
import eventlet.debug
255
import time
256
257
def hub_monitoring_example():
258
"""Example of monitoring hub state"""
259
260
print("=== Hub Monitoring Example ===")
261
262
# Enable stack trace recording
263
eventlet.debug.hub_listener_stacks(True)
264
eventlet.debug.hub_timer_stacks(True)
265
266
def network_worker(worker_id, host, port):
267
"""Worker that creates network listeners"""
268
try:
269
print(f"Network worker {worker_id} connecting to {host}:{port}")
270
271
# This creates a listener in the hub
272
sock = eventlet.connect((host, port))
273
274
# Send some data
275
sock.send(b"Hello from worker " + str(worker_id).encode())
276
277
# Receive response
278
response = sock.recv(1024)
279
print(f"Worker {worker_id} received: {response}")
280
281
sock.close()
282
283
except Exception as e:
284
print(f"Network worker {worker_id} error: {e}")
285
286
def timer_worker(worker_id, delays):
287
"""Worker that creates timers"""
288
print(f"Timer worker {worker_id} starting")
289
290
for delay in delays:
291
print(f"Timer worker {worker_id} sleeping for {delay}s")
292
eventlet.sleep(delay)
293
294
print(f"Timer worker {worker_id} completed")
295
296
def monitor_hub():
297
"""Monitor hub state periodically"""
298
for i in range(10):
299
eventlet.sleep(0.5)
300
301
print(f"\n--- Hub Status (iteration {i+1}) ---")
302
303
# Show hub listeners
304
listeners_info = eventlet.debug.format_hub_listeners()
305
if listeners_info.strip():
306
print("Active Listeners:")
307
print(listeners_info)
308
else:
309
print("No active listeners")
310
311
# Show hub timers
312
timers_info = eventlet.debug.format_hub_timers()
313
if timers_info.strip():
314
print("Active Timers:")
315
print(timers_info)
316
else:
317
print("No active timers")
318
319
# Start monitoring
320
eventlet.spawn(monitor_hub)
321
322
# Start some network workers (these will likely fail but create listeners)
323
for i in range(2):
324
eventlet.spawn(network_worker, i+1, 'httpbin.org', 80)
325
326
# Start timer workers
327
timer_delays = [[1.0, 0.5], [0.3, 1.2, 0.8]]
328
for i, delays in enumerate(timer_delays):
329
eventlet.spawn(timer_worker, i+1, delays)
330
331
# Let everything run
332
eventlet.sleep(6)
333
334
print("\nHub monitoring completed")
335
336
if __name__ == "__main__":
337
hub_monitoring_example()
338
```
339
340
### Performance Analysis with Tracing
341
342
```python
343
import eventlet
344
import eventlet.debug
345
import time
346
347
def performance_analysis_example():
348
"""Example of performance analysis with tracing"""
349
350
print("=== Performance Analysis Example ===")
351
352
def slow_function(duration):
353
"""Function that takes some time"""
354
print(f"Starting slow function ({duration}s)")
355
start = time.time()
356
357
# Mix of blocking and non-blocking operations
358
eventlet.sleep(duration / 2) # Non-blocking
359
360
# Simulate CPU work (this might show up in traces)
361
for i in range(int(duration * 100000)):
362
if i % 10000 == 0:
363
eventlet.sleep(0) # Yield occasionally
364
365
elapsed = time.time() - start
366
print(f"Slow function completed in {elapsed:.2f}s")
367
return elapsed
368
369
def traced_worker(worker_id):
370
"""Worker that we'll trace"""
371
print(f"Traced worker {worker_id} starting")
372
373
# Do some work that we want to trace
374
result1 = slow_function(0.5)
375
result2 = slow_function(0.3)
376
377
total = result1 + result2
378
print(f"Traced worker {worker_id} total time: {total:.2f}s")
379
380
return total
381
382
def untraced_worker(worker_id):
383
"""Worker that runs without tracing"""
384
print(f"Untraced worker {worker_id} starting")
385
386
result1 = slow_function(0.4)
387
result2 = slow_function(0.2)
388
389
total = result1 + result2
390
print(f"Untraced worker {worker_id} total time: {total:.2f}s")
391
392
return total
393
394
# Run without tracing first
395
print("Running workers without tracing...")
396
start_time = time.time()
397
398
untraced_gts = []
399
for i in range(2):
400
gt = eventlet.spawn(untraced_worker, i+1)
401
untraced_gts.append(gt)
402
403
for gt in untraced_gts:
404
gt.wait()
405
406
untraced_time = time.time() - start_time
407
print(f"Untraced execution took {untraced_time:.2f}s")
408
409
print("\n" + "="*50)
410
print("Now running with tracing enabled...")
411
print("WARNING: This will produce very verbose output!")
412
413
# Enable tracing for detailed analysis
414
eventlet.debug.spew()
415
416
start_time = time.time()
417
418
traced_gts = []
419
for i in range(1): # Fewer workers due to verbose output
420
gt = eventlet.spawn(traced_worker, i+1)
421
traced_gts.append(gt)
422
423
for gt in traced_gts:
424
gt.wait()
425
426
# Disable tracing
427
eventlet.debug.unspew()
428
429
traced_time = time.time() - start_time
430
print(f"Traced execution took {traced_time:.2f}s")
431
432
print("Performance analysis completed")
433
434
if __name__ == "__main__":
435
performance_analysis_example()
436
```
437
438
### Interactive Debugging Backdoor
439
440
```python
441
import eventlet
442
import eventlet.debug
443
import threading
444
import time
445
446
# Global application state for debugging
447
app_state = {
448
'active_connections': 0,
449
'total_requests': 0,
450
'errors': [],
451
'start_time': time.time()
452
}
453
454
def web_server_simulation():
455
"""Simulate a web server for debugging"""
456
457
def handle_request(request_id):
458
"""Simulate handling a web request"""
459
app_state['active_connections'] += 1
460
app_state['total_requests'] += 1
461
462
try:
463
# Simulate request processing
464
processing_time = eventlet.random.uniform(0.1, 2.0)
465
eventlet.sleep(processing_time)
466
467
# Occasionally simulate an error
468
if eventlet.random.random() < 0.1:
469
raise ValueError(f"Simulated error in request {request_id}")
470
471
print(f"Request {request_id} completed in {processing_time:.2f}s")
472
473
except Exception as e:
474
error_info = {
475
'request_id': request_id,
476
'error': str(e),
477
'timestamp': time.time()
478
}
479
app_state['errors'].append(error_info)
480
print(f"Request {request_id} failed: {e}")
481
482
finally:
483
app_state['active_connections'] -= 1
484
485
# Simulate incoming requests
486
request_id = 0
487
while True:
488
request_id += 1
489
eventlet.spawn(handle_request, request_id)
490
491
# Random delay between requests
492
eventlet.sleep(eventlet.random.uniform(0.1, 0.5))
493
494
def backdoor_debugging_example():
495
"""Example using backdoor for interactive debugging"""
496
497
print("=== Interactive Debugging Backdoor Example ===")
498
print("Starting web server simulation...")
499
500
# Start the simulated web server
501
eventlet.spawn(web_server_simulation)
502
503
# Set up debugging backdoor
504
backdoor_port = 9999
505
backdoor_locals = {
506
'app_state': app_state,
507
'eventlet': eventlet,
508
'debug': eventlet.debug,
509
'time': time
510
}
511
512
print(f"Starting debugging backdoor on port {backdoor_port}")
513
print("Connect with: telnet localhost 9999")
514
print("\nUseful debugging commands to try in the backdoor:")
515
print(" app_state # View application state")
516
print(" app_state['total_requests'] # Get request count")
517
print(" len(app_state['errors']) # Get error count")
518
print(" debug.format_hub_timers() # View hub timers")
519
print(" debug.format_hub_listeners() # View hub listeners")
520
print(" [error['error'] for error in app_state['errors'][-5:]] # Recent errors")
521
print("\nPress Ctrl+C to stop the server")
522
523
try:
524
# Start backdoor server
525
eventlet.debug.backdoor(backdoor_port, locals=backdoor_locals)
526
527
except KeyboardInterrupt:
528
print("\nShutting down server...")
529
530
# Print final statistics
531
uptime = time.time() - app_state['start_time']
532
print(f"\nFinal Statistics:")
533
print(f" Uptime: {uptime:.1f} seconds")
534
print(f" Total requests: {app_state['total_requests']}")
535
print(f" Active connections: {app_state['active_connections']}")
536
print(f" Total errors: {len(app_state['errors'])}")
537
538
if __name__ == "__main__":
539
# Note: This example requires manual interaction via telnet
540
# Run: python script.py
541
# Then in another terminal: telnet localhost 9999
542
backdoor_debugging_example()
543
```
544
545
### System Information and Diagnostics
546
547
```python
548
import eventlet
549
import eventlet.debug
550
import threading
551
import asyncio
552
import time
553
554
def system_diagnostics_example():
555
"""Example of gathering system diagnostic information"""
556
557
print("=== System Diagnostics Example ===")
558
559
def background_worker(worker_type, worker_id):
560
"""Background worker for diagnostics"""
561
if worker_type == 'greenthread':
562
for i in range(5):
563
print(f"Greenthread worker {worker_id} iteration {i+1}")
564
eventlet.sleep(1.0)
565
566
elif worker_type == 'thread':
567
for i in range(3):
568
print(f"Thread worker {worker_id} iteration {i+1}")
569
time.sleep(1.0) # Blocking sleep
570
571
def async_worker():
572
"""Asyncio worker (if available)"""
573
try:
574
import asyncio
575
576
async def async_task():
577
for i in range(3):
578
print(f"Async worker iteration {i+1}")
579
await asyncio.sleep(0.5)
580
581
loop = asyncio.new_event_loop()
582
asyncio.set_event_loop(loop)
583
loop.run_until_complete(async_task())
584
loop.close()
585
586
except Exception as e:
587
print(f"Async worker error: {e}")
588
589
def collect_diagnostics():
590
"""Collect comprehensive diagnostic information"""
591
print("\n--- Collecting System Diagnostics ---")
592
593
# Eventlet hub information
594
print("\n1. Eventlet Hub Status:")
595
listeners = eventlet.debug.format_hub_listeners()
596
timers = eventlet.debug.format_hub_timers()
597
598
if listeners.strip():
599
print("Hub Listeners:")
600
print(listeners)
601
else:
602
print("No active hub listeners")
603
604
if timers.strip():
605
print("Hub Timers:")
606
print(timers)
607
else:
608
print("No active hub timers")
609
610
# Thread information
611
print("\n2. Thread Information:")
612
thread_info = eventlet.debug.format_threads_info()
613
if thread_info.strip():
614
print(thread_info)
615
else:
616
print("No additional thread information available")
617
618
# Asyncio information (if applicable)
619
print("\n3. Asyncio Information:")
620
try:
621
asyncio_info = eventlet.debug.format_asyncio_info()
622
if asyncio_info.strip():
623
print(asyncio_info)
624
else:
625
print("No asyncio information available")
626
except Exception as e:
627
print(f"Error getting asyncio info: {e}")
628
629
# Python threading information
630
print("\n4. Python Threading:")
631
active_threads = threading.active_count()
632
thread_names = [t.name for t in threading.enumerate()]
633
print(f"Active threads: {active_threads}")
634
print(f"Thread names: {thread_names}")
635
636
# Greenlet information
637
print("\n5. Greenlet Information:")
638
try:
639
import greenlet
640
current = greenlet.getcurrent()
641
print(f"Current greenlet: {current}")
642
print(f"Greenlet parent: {current.parent}")
643
except Exception as e:
644
print(f"Error getting greenlet info: {e}")
645
646
# Start various types of workers
647
print("Starting background workers for diagnostics...")
648
649
# Greenthread workers
650
for i in range(2):
651
eventlet.spawn(background_worker, 'greenthread', i+1)
652
653
# Regular thread workers
654
for i in range(1):
655
thread = threading.Thread(target=background_worker, args=('thread', i+1))
656
thread.start()
657
658
# Asyncio worker in separate thread
659
async_thread = threading.Thread(target=async_worker)
660
async_thread.start()
661
662
# Collect diagnostics periodically
663
for i in range(3):
664
eventlet.sleep(2.0)
665
collect_diagnostics()
666
print("\n" + "="*60)
667
668
print("System diagnostics completed")
669
670
def memory_leak_detection():
671
"""Example of detecting potential memory leaks"""
672
673
print("=== Memory Leak Detection Example ===")
674
675
import gc
676
import sys
677
678
def leaky_function():
679
"""Function that might create memory leaks"""
680
# Create some objects that might not be cleaned up
681
data = list(range(10000))
682
683
def closure():
684
return sum(data) # Closure captures data
685
686
# Store closure somewhere it might persist
687
leaky_function.closures = getattr(leaky_function, 'closures', [])
688
leaky_function.closures.append(closure)
689
690
return closure()
691
692
def monitor_memory():
693
"""Monitor memory usage and object counts"""
694
print("Monitoring memory usage...")
695
696
initial_objects = len(gc.get_objects())
697
print(f"Initial object count: {initial_objects}")
698
699
for iteration in range(5):
700
# Run potentially leaky code
701
for i in range(100):
702
result = leaky_function()
703
eventlet.sleep(0.01)
704
705
# Force garbage collection
706
collected = gc.collect()
707
708
# Check object counts
709
current_objects = len(gc.get_objects())
710
object_growth = current_objects - initial_objects
711
712
print(f"Iteration {iteration+1}:")
713
print(f" Objects: {current_objects} (+{object_growth})")
714
print(f" Collected: {collected}")
715
716
if hasattr(leaky_function, 'closures'):
717
print(f" Closures: {len(leaky_function.closures)}")
718
719
eventlet.sleep(1.0)
720
721
# Show object types that have grown
722
print("\nObject type analysis:")
723
type_counts = {}
724
for obj in gc.get_objects():
725
obj_type = type(obj).__name__
726
type_counts[obj_type] = type_counts.get(obj_type, 0) + 1
727
728
# Show top object types
729
top_types = sorted(type_counts.items(), key=lambda x: x[1], reverse=True)[:10]
730
for obj_type, count in top_types:
731
print(f" {obj_type}: {count}")
732
733
monitor_memory()
734
735
if __name__ == "__main__":
736
print("Choose example to run:")
737
print("1. System Diagnostics")
738
print("2. Memory Leak Detection")
739
740
choice = input("Enter choice (1 or 2): ").strip()
741
742
if choice == "1":
743
system_diagnostics_example()
744
elif choice == "2":
745
memory_leak_detection()
746
else:
747
print("Running system diagnostics by default...")
748
system_diagnostics_example()
749
```
750
751
### Custom Debug Utilities
752
753
```python
754
import eventlet
755
import eventlet.debug
756
import time
757
import functools
758
759
class EventletProfiler:
760
"""Custom profiler for eventlet applications"""
761
762
def __init__(self):
763
self.call_counts = {}
764
self.call_times = {}
765
self.start_times = {}
766
767
def profile_function(self, func):
768
"""Decorator to profile function calls"""
769
@functools.wraps(func)
770
def wrapper(*args, **kwargs):
771
func_name = f"{func.__module__}.{func.__name__}"
772
773
# Update call count
774
self.call_counts[func_name] = self.call_counts.get(func_name, 0) + 1
775
776
# Record start time
777
start_time = time.time()
778
779
try:
780
result = func(*args, **kwargs)
781
return result
782
finally:
783
# Record total time
784
elapsed = time.time() - start_time
785
if func_name not in self.call_times:
786
self.call_times[func_name] = 0
787
self.call_times[func_name] += elapsed
788
789
return wrapper
790
791
def get_stats(self):
792
"""Get profiling statistics"""
793
stats = []
794
for func_name in self.call_counts:
795
stats.append({
796
'function': func_name,
797
'calls': self.call_counts[func_name],
798
'total_time': self.call_times.get(func_name, 0),
799
'avg_time': self.call_times.get(func_name, 0) / self.call_counts[func_name]
800
})
801
802
# Sort by total time
803
stats.sort(key=lambda x: x['total_time'], reverse=True)
804
return stats
805
806
def print_stats(self):
807
"""Print profiling statistics"""
808
stats = self.get_stats()
809
810
print("\n=== Profiling Statistics ===")
811
print(f"{'Function':<40} {'Calls':<8} {'Total':<10} {'Avg':<10}")
812
print("-" * 70)
813
814
for stat in stats[:10]: # Top 10
815
print(f"{stat['function']:<40} {stat['calls']:<8} "
816
f"{stat['total_time']:<10.3f} {stat['avg_time']:<10.3f}")
817
818
def custom_debug_example():
819
"""Example of custom debugging utilities"""
820
821
print("=== Custom Debug Utilities Example ===")
822
823
profiler = EventletProfiler()
824
825
@profiler.profile_function
826
def fast_operation():
827
"""Fast operation"""
828
eventlet.sleep(0.1)
829
return "fast result"
830
831
@profiler.profile_function
832
def slow_operation():
833
"""Slow operation"""
834
eventlet.sleep(0.5)
835
return "slow result"
836
837
@profiler.profile_function
838
def cpu_operation():
839
"""CPU-intensive operation"""
840
result = 0
841
for i in range(100000):
842
result += i
843
if i % 10000 == 0:
844
eventlet.sleep(0) # Yield
845
return result
846
847
def worker(worker_id, operations):
848
"""Worker that performs various operations"""
849
print(f"Worker {worker_id} starting")
850
851
for op_type in operations:
852
if op_type == 'fast':
853
result = fast_operation()
854
elif op_type == 'slow':
855
result = slow_operation()
856
elif op_type == 'cpu':
857
result = cpu_operation()
858
859
print(f"Worker {worker_id} completed {op_type} operation")
860
861
print(f"Worker {worker_id} finished")
862
863
# Define work for each worker
864
worker_operations = [
865
['fast', 'slow', 'cpu'],
866
['fast', 'fast', 'slow'],
867
['cpu', 'fast', 'cpu'],
868
['slow', 'cpu', 'fast']
869
]
870
871
print("Starting profiled workers...")
872
873
# Start workers
874
greenthreads = []
875
for i, operations in enumerate(worker_operations):
876
gt = eventlet.spawn(worker, i+1, operations)
877
greenthreads.append(gt)
878
879
# Wait for completion
880
for gt in greenthreads:
881
gt.wait()
882
883
# Print profiling results
884
profiler.print_stats()
885
886
if __name__ == "__main__":
887
custom_debug_example()
888
```
889
890
## Debugging Best Practices
891
892
### Enable Debugging Early
893
894
```python
895
import eventlet.debug
896
897
# Enable debugging at application startup
898
def setup_debugging():
899
"""Configure debugging for production use"""
900
901
# Enable exception printing (low overhead)
902
eventlet.debug.hub_exceptions(True)
903
eventlet.debug.tpool_exceptions(True)
904
905
# Enable blocking detection in development
906
if os.environ.get('DEBUG', '').lower() == 'true':
907
eventlet.debug.hub_blocking_detection(True, resolution=0.1)
908
eventlet.debug.hub_listener_stacks(True)
909
eventlet.debug.hub_timer_stacks(True)
910
911
# Call early in application initialization
912
setup_debugging()
913
```
914
915
### Conditional Tracing
916
917
```python
918
import eventlet.debug
919
import os
920
921
def debug_worker(worker_func, *args, **kwargs):
922
"""Run worker with conditional tracing"""
923
924
enable_tracing = os.environ.get('EVENTLET_TRACE', '').lower() == 'true'
925
926
if enable_tracing:
927
print("Enabling tracing for worker")
928
eventlet.debug.spew()
929
930
try:
931
return worker_func(*args, **kwargs)
932
finally:
933
if enable_tracing:
934
eventlet.debug.unspew()
935
```
936
937
### Safe Backdoor Setup
938
939
```python
940
import eventlet.debug
941
import os
942
943
def setup_backdoor():
944
"""Setup backdoor only in development"""
945
946
if os.environ.get('EVENTLET_BACKDOOR') == 'true':
947
backdoor_port = int(os.environ.get('EVENTLET_BACKDOOR_PORT', '9999'))
948
949
# Only bind to localhost for security
950
eventlet.spawn(eventlet.debug.backdoor, backdoor_port, host='127.0.0.1')
951
print(f"Debug backdoor available on localhost:{backdoor_port}")
952
```