Skip to content

Commit 73c8eb2

Browse files
committed
Add time stamps and space out levels
1 parent 4fe8fd7 commit 73c8eb2

File tree

3 files changed

+57
-46
lines changed

3 files changed

+57
-46
lines changed

benchmarks/bench_logging_overhead.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
1313
Usage:
1414
# Set the logging level before running
15-
export FLASHINFER_LOGLEVEL_DBG=2
15+
export FLASHINFER_LOGLEVEL_DBG=3
1616
python bench_logging_overhead.py
1717
1818
# Or run with different levels
1919
FLASHINFER_LOGLEVEL_DBG=0 python bench_logging_overhead.py
2020
FLASHINFER_LOGLEVEL_DBG=1 python bench_logging_overhead.py
21-
FLASHINFER_LOGLEVEL_DBG=2 python bench_logging_overhead.py
2221
FLASHINFER_LOGLEVEL_DBG=3 python bench_logging_overhead.py
22+
FLASHINFER_LOGLEVEL_DBG=5 python bench_logging_overhead.py
2323
2424
# Or use the helper script to run all levels
2525
bash benchmark_all_levels.sh
@@ -233,8 +233,8 @@ def main():
233233
level_names = {
234234
0: "No logging (zero-overhead)",
235235
1: "Function name only",
236-
2: "Name + inputs/outputs + metadata",
237-
3: "Name + inputs/outputs + metadata + statistics",
236+
3: "Name + inputs/outputs + metadata",
237+
5: "Name + inputs/outputs + metadata + statistics",
238238
}
239239
print(f" Level description: {level_names.get(LOGGING_LEVEL, 'Unknown')}")
240240

@@ -329,7 +329,7 @@ def main():
329329
print("RECOMMENDATIONS")
330330
print("=" * 80)
331331
print("\nTo benchmark other levels, run:")
332-
for level in [0, 1, 2, 3]:
332+
for level in [0, 1, 3, 5]:
333333
if level != LOGGING_LEVEL:
334334
print(f" FLASHINFER_LOGLEVEL_DBG={level} python {sys.argv[0]}")
335335

flashinfer/api_logging.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ def _substitute_process_id(path: str) -> str:
4646
os.environ.get("FLASHINFER_LOGDEST_DBG", "stdout")
4747
)
4848

49-
# Enable cuDNN, cuBLAS, and cuBLASLt API logging when FlashInfer logging level >= 3
49+
# Enable cuDNN, cuBLAS, and cuBLASLt API logging when FlashInfer logging level >= 5
5050
# Only override if the user hasn't already configured the logging switch
5151
# If the switch is not set, we override both the switch and destination as a bundle
52-
if _API_LOG_LEVEL >= 3:
52+
if _API_LOG_LEVEL >= 5:
5353
# cuBLAS logging: Check switch, set both switch and destination
5454
if "CUBLAS_LOGINFO_DBG" not in os.environ:
5555
os.environ["CUBLAS_LOGINFO_DBG"] = "1"
@@ -104,7 +104,7 @@ def _setup_logger():
104104
else:
105105
handler = logging.FileHandler(_API_LOG_DEST, mode="a")
106106

107-
# Use a simple formatter (we'll format the detailed content ourselves)
107+
# Use a simple formatter (we'll add timestamps manually to key lines)
108108
formatter = logging.Formatter("%(message)s")
109109
handler.setFormatter(formatter)
110110

@@ -116,14 +116,21 @@ def _setup_logger():
116116
_setup_logger()
117117

118118

119+
def _get_timestamp() -> str:
120+
"""Get current timestamp in the format [YYYY-MM-DD HH:MM:SS]."""
121+
from datetime import datetime
122+
123+
return datetime.now().strftime("[%Y-%m-%d %H:%M:%S]")
124+
125+
119126
def _log_system_info():
120127
"""Log system information once at module initialization."""
121128
if _API_LOG_LEVEL == 0:
122129
return
123130

124131
lines = []
125132
lines.append("=" * 80)
126-
lines.append("FlashInfer API Logging - System Information")
133+
lines.append(f"{_get_timestamp()} FlashInfer API Logging - System Information")
127134
lines.append("=" * 80)
128135

129136
try:
@@ -179,9 +186,9 @@ def _log_system_info():
179186
lines.append(f"PyTorch version: {torch.__version__}")
180187

181188
# cuDNN/cuBLAS/cuBLASLt logging status
182-
if _API_LOG_LEVEL >= 3:
189+
if _API_LOG_LEVEL >= 5:
183190
lines.append("")
184-
lines.append("cuDNN/cuBLAS/cuBLASLt Logging: Enabled (Level 3)")
191+
lines.append("cuDNN/cuBLAS/cuBLASLt Logging: Enabled (Level 5)")
185192
cublas_info = os.environ.get("CUBLAS_LOGINFO_DBG", "not set")
186193
cublas_dest = os.environ.get("CUBLAS_LOGDEST_DBG", "not set")
187194
cublaslt_level = os.environ.get("CUBLASLT_LOG_LEVEL", "not set")
@@ -249,7 +256,7 @@ def _format_value(value: Any, level: int, indent: int = 0) -> str:
249256
if level == 1:
250257
return f"{indent_str}Tensor(...)"
251258

252-
# Level 2+: Show metadata
259+
# Level 3+: Show metadata
253260
lines = [f"{indent_str}Tensor("]
254261
lines.append(f"{indent_str} shape={tuple(value.shape)}")
255262
lines.append(f"{indent_str} stride={tuple(value.stride())}")
@@ -258,8 +265,8 @@ def _format_value(value: Any, level: int, indent: int = 0) -> str:
258265
lines.append(f"{indent_str} requires_grad={value.requires_grad}")
259266
lines.append(f"{indent_str} is_contiguous={value.is_contiguous()}")
260267

261-
# Level 3: Add statistics
262-
if level >= 3:
268+
# Level 5: Add statistics
269+
if level >= 5:
263270
try:
264271
# Skip statistics if we're in CUDA graph capture mode
265272
# (operations like .min()/.max()/.mean() cause synchronization issues)
@@ -452,11 +459,11 @@ def _log_function_inputs(
452459
kwargs : dict
453460
Keyword arguments
454461
level : int
455-
Logging level (2 or 3)
462+
Logging level (3 or 5)
456463
"""
457464
lines = []
458465
lines.append("=" * 80)
459-
lines.append(f"FlashInfer API Call: {func_name}")
466+
lines.append(f"{_get_timestamp()} FlashInfer API Call: {func_name}")
460467
lines.append("-" * 80)
461468

462469
# Log explicitly provided inputs
@@ -499,7 +506,7 @@ def _log_function_outputs(func_name: str, result: Any, level: int) -> None:
499506
result : Any
500507
Function return value
501508
level : int
502-
Logging level (2 or 3)
509+
Logging level (3 or 5)
503510
"""
504511
lines = []
505512
# Log outputs
@@ -524,8 +531,8 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
524531
FLASHINFER_LOGLEVEL_DBG : int (default: 0)
525532
- 0: No logging (zero overhead - decorator returns original function)
526533
- 1: Log function name only (logged BEFORE execution - crash-safe)
527-
- 2: Log function name + inputs/outputs with metadata (inputs logged BEFORE execution - crash-safe)
528-
- 3: Log function name + inputs/outputs with metadata + tensor statistics (inputs logged BEFORE execution - crash-safe)
534+
- 3: Log function name + inputs/outputs with metadata (inputs logged BEFORE execution - crash-safe)
535+
- 5: Log function name + inputs/outputs with metadata + tensor statistics (inputs logged BEFORE execution - crash-safe)
529536
530537
FLASHINFER_LOGDEST_DBG : str (default: "stdout")
531538
- "stdout": Log to standard output
@@ -543,18 +550,20 @@ def flashinfer_api_log(func: Callable = None) -> Callable:
543550
544551
Notes
545552
-----
553+
- Key header lines include a timestamp in the format: [YYYY-MM-DD HH:MM:SS]
554+
(e.g., "FlashInfer API Call: function_name", "FlashInfer API Logging - System Information")
546555
- When FLASHINFER_LOGLEVEL_DBG=0, the decorator has truly zero overhead
547556
as it returns the original function unchanged.
548557
- Function names and inputs are logged BEFORE execution:
549558
- Level 1: Function name only
550-
- Levels 2-3: Function name + inputs with metadata
559+
- Levels 3-5: Function name + inputs with metadata
551560
This means critical debugging information is preserved even if the function
552561
crashes (e.g., CUDA illegal memory access, out-of-bounds, etc.).
553-
- Outputs are logged AFTER successful execution for levels 2 and 3.
554-
- **CUDA Graph Compatibility**: At level 3, tensor statistics (min/max/mean/nan_count)
562+
- Outputs are logged AFTER successful execution for levels 3 and 5.
563+
- **CUDA Graph Compatibility**: At level 5, tensor statistics (min/max/mean/nan_count)
555564
are automatically skipped during CUDA graph capture to avoid synchronization issues.
556565
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
566+
- **cuDNN/cuBLAS/cuBLASLt Integration**: At level 5, if not already set by the user, the following
558567
environment variables are automatically configured to enable cuDNN, cuBLAS, and cuBLASLt logging:
559568
- CUBLAS_LOGINFO_DBG=1, CUBLAS_LOGDEST_DBG=flashinfer_cublas_log_%i.txt
560569
- CUBLASLT_LOG_LEVEL=2, CUBLASLT_LOG_FILE=flashinfer_cublaslt_log_%i.txt
@@ -588,20 +597,22 @@ def wrapper(*args, **kwargs):
588597
try:
589598
if _API_LOG_LEVEL == 1:
590599
# Level 1: Just log function name before execution (crash-safe)
591-
_logger.debug(f"FlashInfer API Call: {func_name}")
592-
elif _API_LOG_LEVEL >= 2:
593-
# Level 2+: Log full inputs before execution (crash-safe)
600+
_logger.debug(
601+
f"{_get_timestamp()} FlashInfer API Call: {func_name}"
602+
)
603+
elif _API_LOG_LEVEL >= 3:
604+
# Level 3+: Log full inputs before execution (crash-safe)
594605
_log_function_inputs(f, func_name, args, kwargs, _API_LOG_LEVEL)
595606
except Exception as e:
596607
_logger.error(f"[LOGGING ERROR in {func_name} (pre-execution)]: {e}")
597608

598609
# Call the original function (may crash here with CUDA errors)
599610
result = f(*args, **kwargs)
600611

601-
# Log outputs AFTER successful execution (level 2+ only)
612+
# Log outputs AFTER successful execution (level 3+ only)
602613
try:
603-
if _API_LOG_LEVEL >= 2:
604-
# Level 2+: Log outputs (inputs were already logged above)
614+
if _API_LOG_LEVEL >= 3:
615+
# Level 3+: Log outputs (inputs were already logged above)
605616
_log_function_outputs(func_name, result, _API_LOG_LEVEL)
606617
except Exception as e:
607618
_logger.error(f"[LOGGING ERROR in {func_name} (outputs)]: {e}")

tests/utils/test_logging.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ def test_function(x, y):
119119
finally:
120120
Path(log_file).unlink(missing_ok=True)
121121

122-
def test_level_2_inputs_outputs(self):
123-
"""Test that level 2 logs inputs and outputs with metadata."""
122+
def test_level_3_inputs_outputs(self):
123+
"""Test that level 3 logs inputs and outputs with metadata."""
124124
with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".txt") as f:
125125
log_file = f.name
126126

127127
try:
128-
decorator = self.setup_logging(level=2, dest=log_file)
128+
decorator = self.setup_logging(level=3, dest=log_file)
129129

130130
@decorator
131131
def test_function(tensor, value):
@@ -151,19 +151,19 @@ def test_function(tensor, value):
151151
# Should log outputs
152152
assert "Output value:" in log_contents
153153

154-
# Should NOT log statistics (level 3 only)
154+
# Should NOT log statistics (level 5 only)
155155
assert "min=" not in log_contents
156156
assert "max=" not in log_contents
157157
finally:
158158
Path(log_file).unlink(missing_ok=True)
159159

160-
def test_level_3_statistics(self):
161-
"""Test that level 3 logs tensor statistics."""
160+
def test_level_5_statistics(self):
161+
"""Test that level 5 logs tensor statistics."""
162162
with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".txt") as f:
163163
log_file = f.name
164164

165165
try:
166-
decorator = self.setup_logging(level=3, dest=log_file)
166+
decorator = self.setup_logging(level=5, dest=log_file)
167167

168168
@decorator
169169
def test_function(tensor):
@@ -191,7 +191,7 @@ def test_enum_logging(self):
191191
log_file = f.name
192192

193193
try:
194-
decorator = self.setup_logging(level=2, dest=log_file)
194+
decorator = self.setup_logging(level=3, dest=log_file)
195195

196196
@decorator
197197
def test_function(mode: TestEnum, strategy: StringEnum):
@@ -221,7 +221,7 @@ def test_default_parameters(self):
221221
log_file = f.name
222222

223223
try:
224-
decorator = self.setup_logging(level=2, dest=log_file)
224+
decorator = self.setup_logging(level=3, dest=log_file)
225225

226226
@decorator
227227
def test_function(x, y=10, z=20, mode=TestEnum.OPTION_A):
@@ -252,7 +252,7 @@ def test_explicit_vs_default_parameters(self):
252252
log_file = f.name
253253

254254
try:
255-
decorator = self.setup_logging(level=2, dest=log_file)
255+
decorator = self.setup_logging(level=3, dest=log_file)
256256

257257
@decorator
258258
def test_function(x, y=10, z=20):
@@ -316,7 +316,7 @@ def test_crash_safety_inputs_logged_before_execution(self):
316316
log_file = f.name
317317

318318
try:
319-
decorator = self.setup_logging(level=2, dest=log_file)
319+
decorator = self.setup_logging(level=3, dest=log_file)
320320

321321
@decorator
322322
def crashing_function(x, y):
@@ -349,7 +349,7 @@ def test_different_data_types(self):
349349
log_file = f.name
350350

351351
try:
352-
decorator = self.setup_logging(level=2, dest=log_file)
352+
decorator = self.setup_logging(level=3, dest=log_file)
353353

354354
@decorator
355355
def test_function(
@@ -387,7 +387,7 @@ def test_tensor_metadata(self):
387387
log_file = f.name
388388

389389
try:
390-
decorator = self.setup_logging(level=2, dest=log_file)
390+
decorator = self.setup_logging(level=3, dest=log_file)
391391

392392
@decorator
393393
def test_function(tensor):
@@ -420,7 +420,7 @@ def test_nested_structures(self):
420420
log_file = f.name
421421

422422
try:
423-
decorator = self.setup_logging(level=2, dest=log_file)
423+
decorator = self.setup_logging(level=3, dest=log_file)
424424

425425
@decorator
426426
def test_function(nested):
@@ -510,7 +510,7 @@ def test_kwargs_logging(self):
510510
log_file = f.name
511511

512512
try:
513-
decorator = self.setup_logging(level=2, dest=log_file)
513+
decorator = self.setup_logging(level=3, dest=log_file)
514514

515515
@decorator
516516
def test_function(a, b, c):
@@ -534,12 +534,12 @@ def test_function(a, b, c):
534534

535535
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
536536
def test_cuda_graph_compatibility(self):
537-
"""Test that level 3 logging is compatible with CUDA graph capture."""
537+
"""Test that level 5 logging is compatible with CUDA graph capture."""
538538
with tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".txt") as f:
539539
log_file = f.name
540540

541541
try:
542-
decorator = self.setup_logging(level=3, dest=log_file)
542+
decorator = self.setup_logging(level=5, dest=log_file)
543543

544544
@decorator
545545
def test_cuda_function(tensor):

0 commit comments

Comments
 (0)