2525import torch
2626
2727
28+ # Helper function to substitute %i with process ID in file paths
29+ def _substitute_process_id (path : str ) -> str :
30+ """
31+ Replace %i with the current process ID in a path.
32+
33+ This is useful for multi-process/multi-GPU environments where each process
34+ needs its own log file.
35+
36+ Example: "flashinfer_log_%i.txt" -> "flashinfer_log_12345.txt"
37+ """
38+ if "%i" in path :
39+ return path .replace ("%i" , str (os .getpid ()))
40+ return path
41+
42+
2843# Read environment variables once at module load time
2944_API_LOG_LEVEL = int (os .environ .get ("FLASHINFER_LOGLEVEL_DBG" , "0" ))
30- _API_LOG_DEST = os .environ .get ("FLASHINFER_LOGDEST_DBG" , "stdout" )
45+ _API_LOG_DEST = _substitute_process_id (
46+ os .environ .get ("FLASHINFER_LOGDEST_DBG" , "stdout" )
47+ )
48+
49+ # Enable cuDNN, cuBLAS, and cuBLASLt API logging when FlashInfer logging level >= 3
50+ # Only override if the user hasn't already configured the logging switch
51+ # If the switch is not set, we override both the switch and destination as a bundle
52+ if _API_LOG_LEVEL >= 3 :
53+ # cuBLAS logging: Check switch, set both switch and destination
54+ if "CUBLAS_LOGINFO_DBG" not in os .environ :
55+ os .environ ["CUBLAS_LOGINFO_DBG" ] = "1"
56+ os .environ ["CUBLAS_LOGDEST_DBG" ] = _substitute_process_id (
57+ "flashinfer_cublas_log_%i.txt"
58+ )
59+
60+ # cuBLASLt logging: Check switch, set both switch and destination
61+ if "CUBLASLT_LOG_LEVEL" not in os .environ :
62+ os .environ ["CUBLASLT_LOG_LEVEL" ] = "2"
63+ os .environ ["CUBLASLT_LOG_FILE" ] = _substitute_process_id (
64+ "flashinfer_cublaslt_log_%i.txt"
65+ )
66+
67+ # cuDNN backend logging: Check switch, set both switch and destination
68+ if "CUDNN_LOGLEVEL_DBG" not in os .environ :
69+ os .environ ["CUDNN_LOGLEVEL_DBG" ] = "2.5"
70+ os .environ ["CUDNN_LOGDEST_DBG" ] = _substitute_process_id (
71+ "flashinfer_cudnn_backend_log_%i.txt"
72+ )
73+
74+ # cuDNN frontend logging: Check switch, set both switch and destination
75+ if "CUDNN_FRONTEND_LOG_INFO" not in os .environ :
76+ os .environ ["CUDNN_FRONTEND_LOG_INFO" ] = "1"
77+ os .environ ["CUDNN_FRONTEND_LOG_FILE" ] = _substitute_process_id (
78+ "flashinfer_cudnn_frontend_log_%i.txt"
79+ )
3180
3281# Create logger using Python's logging library
3382_logger = logging .getLogger ("flashinfer.api" )
@@ -129,6 +178,28 @@ def _log_system_info():
129178 # PyTorch version
130179 lines .append (f"PyTorch version: { torch .__version__ } " )
131180
181+ # cuDNN/cuBLAS/cuBLASLt logging status
182+ if _API_LOG_LEVEL >= 3 :
183+ lines .append ("" )
184+ lines .append ("cuDNN/cuBLAS/cuBLASLt Logging: Enabled (Level 3)" )
185+ cublas_info = os .environ .get ("CUBLAS_LOGINFO_DBG" , "not set" )
186+ cublas_dest = os .environ .get ("CUBLAS_LOGDEST_DBG" , "not set" )
187+ cublaslt_level = os .environ .get ("CUBLASLT_LOG_LEVEL" , "not set" )
188+ cublaslt_file = os .environ .get ("CUBLASLT_LOG_FILE" , "not set" )
189+ cudnn_level = os .environ .get ("CUDNN_LOGLEVEL_DBG" , "not set" )
190+ cudnn_dest = os .environ .get ("CUDNN_LOGDEST_DBG" , "not set" )
191+ cudnn_fe_info = os .environ .get ("CUDNN_FRONTEND_LOG_INFO" , "not set" )
192+ cudnn_fe_file = os .environ .get ("CUDNN_FRONTEND_LOG_FILE" , "not set" )
193+
194+ lines .append (f" CUBLAS_LOGINFO_DBG={ cublas_info } " )
195+ lines .append (f" CUBLAS_LOGDEST_DBG={ cublas_dest } " )
196+ lines .append (f" CUBLASLT_LOG_LEVEL={ cublaslt_level } " )
197+ lines .append (f" CUBLASLT_LOG_FILE={ cublaslt_file } " )
198+ lines .append (f" CUDNN_LOGLEVEL_DBG={ cudnn_level } " )
199+ lines .append (f" CUDNN_LOGDEST_DBG={ cudnn_dest } " )
200+ lines .append (f" CUDNN_FRONTEND_LOG_INFO={ cudnn_fe_info } " )
201+ lines .append (f" CUDNN_FRONTEND_LOG_FILE={ cudnn_fe_file } " )
202+
132203 except Exception as e :
133204 lines .append (f"Error gathering system information: { e } " )
134205
@@ -460,6 +531,7 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
460531 - "stdout": Log to standard output
461532 - "stderr": Log to standard error
462533 - <path>: Log to specified file path
534+ - Use %i in path for process ID substitution (e.g., "log_%i.txt" -> "log_12345.txt")
463535
464536 Examples
465537 --------
@@ -482,6 +554,13 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
482554 - **CUDA Graph Compatibility**: At level 3, tensor statistics (min/max/mean/nan_count)
483555 are automatically skipped during CUDA graph capture to avoid synchronization issues.
484556 The message "[statistics skipped: CUDA graph capture in progress]" will be logged.
557+ - **cuDNN/cuBLAS/cuBLASLt Integration**: At level 3, if not already set by the user, the following
558+ environment variables are automatically configured to enable cuDNN, cuBLAS, and cuBLASLt logging:
559+ - CUBLAS_LOGINFO_DBG=1, CUBLAS_LOGDEST_DBG=flashinfer_cublas_log_%i.txt
560+ - CUBLASLT_LOG_LEVEL=2, CUBLASLT_LOG_FILE=flashinfer_cublaslt_log_%i.txt
561+ - CUDNN_LOGLEVEL_DBG=2.5, CUDNN_LOGDEST_DBG=flashinfer_cudnn_backend_log_%i.txt
562+ - CUDNN_FRONTEND_LOG_INFO=1, CUDNN_FRONTEND_LOG_FILE=flashinfer_cudnn_frontend_log_%i.txt
563+ The %i pattern is automatically replaced with the process ID for multi-process environments.
485564 - The logger does not propagate to the root logger to avoid duplicate logs.
486565 """
487566 # If logging is disabled, return original function with zero overhead
0 commit comments