mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-11 12:29:04 +00:00
Improve cpp log level classification (#1422)
This commit is contained in:
2 files changed
+76
No files matched your search
@@ -34,6 +34,39 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
(default: the bundled Serena version).
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _determine_log_level(line: str) -> int:
|
||||
"""
|
||||
Classify a clangd stderr line using clangd's explicit level prefix.
|
||||
|
||||
See `clang::clangd::Logger::indicator` for details:
|
||||
https://clang.llvm.org/extra/doxygen/classclang_1_1clangd_1_1Logger.html
|
||||
|
||||
Clangd emits each log record prefixed by a single indicator character
|
||||
followed by a timestamp in square brackets, e.g. ``I[12:27:16.234]``.
|
||||
|
||||
The indicators are ``D`` (Debug), ``I`` (Info), ``E`` (Error) and
|
||||
``F`` (Fatal). Continuation lines of multi-line records carry no
|
||||
prefix and are treated as informational.
|
||||
|
||||
Without this override, the base implementation scans the line for
|
||||
the substrings ``error`` and ``exception``, which produces false
|
||||
positives on clangd's reconstructed compile commands in some cases
|
||||
(e.g. ``-DNO_EXCEPTIONS``, ``-fno-exceptions``).
|
||||
"""
|
||||
# classify by clangd's level indicator character
|
||||
if len(line) >= 2 and line[1] == "[":
|
||||
indicator = line[0]
|
||||
if indicator in ("E", "F"):
|
||||
return logging.ERROR
|
||||
if indicator == "I":
|
||||
return logging.INFO
|
||||
if indicator == "D":
|
||||
return logging.DEBUG
|
||||
|
||||
# continuation line or non-prefixed output: default to INFO, do not keyword-scan
|
||||
return logging.INFO
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a ClangdLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.language_servers.clangd_language_server import ClangdLanguageServer
|
||||
|
||||
|
||||
@pytest.mark.cpp
|
||||
class TestClangdLogging:
|
||||
@pytest.mark.parametrize(
|
||||
"expected, line",
|
||||
[
|
||||
# Parametrize names instead of codes for human-readable output in test logs.
|
||||
(logging.getLevelName(logging.INFO), "I[12:00:00.000] clangd version 22.1.3"),
|
||||
(logging.getLevelName(logging.INFO), "V[12:00:00.000] verbose detail"),
|
||||
(logging.getLevelName(logging.DEBUG), "D[12:00:00.000] debug detail"),
|
||||
(logging.getLevelName(logging.ERROR), "E[12:00:00.000] something bad"),
|
||||
(logging.getLevelName(logging.ERROR), "F[12:00:00.000] fatal"),
|
||||
],
|
||||
)
|
||||
def test_log_record_detection(self, expected: str, line: str) -> None:
|
||||
"""
|
||||
Verifies clangd's stderr log classifier maps each level prefix to the
|
||||
expected Python logging level and avoids misclassifying compile-command
|
||||
payloads as ERROR.
|
||||
"""
|
||||
assert ClangdLanguageServer._determine_log_level(line) == logging.getLevelNamesMapping()[expected]
|
||||
|
||||
def test_log_record_info_payload_mentions_fno_exceptions(self) -> None:
|
||||
line = """clang++ -fno-exceptions -c no_exceptions.cpp"""
|
||||
# Basic classifier incorrectly detects line as an error log.
|
||||
assert SolidLanguageServer._determine_log_level(line) == logging.ERROR
|
||||
# Clangd classifier correctly detects line as an info log.
|
||||
assert ClangdLanguageServer._determine_log_level(line) == logging.INFO
|
||||
|
||||
def test_log_record_info_payload_without_prefix(self) -> None:
|
||||
# Multi-line records: first line carries the I[...] prefix, subsequent
|
||||
# lines do not. Treat lack of prefix as continuation.
|
||||
assert ClangdLanguageServer._determine_log_level("[/home/user/project/build]") == logging.INFO
|
||||
|
||||
def test_log_record_info_payload_empty(self) -> None:
|
||||
assert ClangdLanguageServer._determine_log_level("") == logging.INFO
|
||||
Reference in new issue
Block a user