11import asyncio
2+ import logging
23import threading
34from threading import Event
45from typing import Any , ClassVar , Final , List , Optional , Set , Union
1516 InitializeParamsClientInfoType ,
1617 InitializeResult ,
1718 InitializeResultServerInfoType ,
19+ LogMessageParams ,
20+ LogTraceParams ,
21+ MessageType ,
1822 PositionEncodingKind ,
1923 ProgressToken ,
2024 Registration ,
2933 UnregistrationParams ,
3034 WorkspaceFolder ,
3135)
32- from robotcode .core .utils .logging import LoggingDescriptor
36+ from robotcode .core .utils .logging import TRACE , LoggingDescriptor
3337from robotcode .core .utils .process import pid_exists
3438from robotcode .jsonrpc2 .protocol import (
3539 JsonRPCErrorException ,
3640 JsonRPCErrors ,
3741 JsonRPCException ,
42+ JsonRPCMessage ,
3843 JsonRPCProtocol ,
3944 ProtocolPartDescriptor ,
4045 rpc_method ,
@@ -74,6 +79,34 @@ class LanguageServerException(JsonRPCException):
7479 pass
7580
7681
82+ class LanguageServerLogHandler (logging .Handler ):
83+ MAPPING = {
84+ logging .INFO : MessageType .INFO ,
85+ logging .WARNING : MessageType .WARNING ,
86+ logging .ERROR : MessageType .ERROR ,
87+ TRACE : MessageType .LOG ,
88+ }
89+
90+ def __init__ (self , protocol : "LanguageServerProtocol" ):
91+ super ().__init__ ()
92+ self .protocol = protocol
93+ self .trace : TraceValues = TraceValues .OFF
94+
95+ def emit (self , record : logging .LogRecord ) -> None :
96+ if record .levelno == TRACE :
97+ if self .trace != TraceValues .OFF :
98+ self .protocol .log_trace (record .getMessage ())
99+
100+ type = self .MAPPING .get (record .levelno , None )
101+ if type is None :
102+ type = MessageType .LOG
103+
104+ self .protocol .window_log_message (
105+ type = type ,
106+ message = record .getMessage (),
107+ )
108+
109+
77110class LanguageServerProtocol (JsonRPCProtocol ):
78111 __logger = LoggingDescriptor ()
79112
@@ -131,8 +164,13 @@ def __init__(self, server: JsonRPCServer[Any]):
131164 )
132165
133166 self ._trace = TraceValues .OFF
167+ self ._trace_loghandler : Optional [LanguageServerLogHandler ] = None
168+
134169 self .is_initialized = Event ()
135170
171+ def __del__ (self ) -> None :
172+ self .trace = TraceValues .OFF
173+
136174 @event
137175 def on_shutdown (sender ) -> None : # pragma: no cover, NOSONAR
138176 ...
@@ -149,6 +187,9 @@ def trace(self) -> TraceValues:
149187 def trace (self , value : TraceValues ) -> None :
150188 self ._trace = value
151189
190+ if self ._trace_loghandler :
191+ self ._trace_loghandler .trace = value
192+
152193 @property
153194 def workspace (self ) -> Workspace :
154195 if self ._workspace is None :
@@ -210,7 +251,14 @@ def _initialize(
210251 if self .parent_process_id and pid_exists (self .parent_process_id ):
211252 self .start_parent_process_watcher ()
212253
254+ logger = logging .getLogger ()
255+
256+ if self ._trace_loghandler is None :
257+ self ._trace_loghandler = LanguageServerLogHandler (self )
258+ logger .addHandler (self ._trace_loghandler )
259+
213260 self .trace = trace or TraceValues .OFF
261+
214262 self .client_info = client_info
215263
216264 self .client_capabilities = capabilities
@@ -292,6 +340,13 @@ def _shutdown(self, *args: Any, **kwargs: Any) -> None:
292340
293341 self .shutdown_received = True
294342
343+ logger = logging .getLogger ()
344+
345+ if self ._trace_loghandler in logger .handlers :
346+ logging .getLogger ().removeHandler (self ._trace_loghandler )
347+ del self ._trace_loghandler
348+ self ._trace_loghandler = None
349+
295350 try :
296351 self .cancel_all_received_request ()
297352 except BaseException as e :
@@ -311,6 +366,16 @@ def _exit(self, *args: Any, **kwargs: Any) -> None:
311366 def _set_trace (self , value : TraceValues , * args : Any , ** kwargs : Any ) -> None :
312367 self .trace = value
313368
369+ def window_log_message (self , type : MessageType , message : str ) -> None :
370+ self .send_notification ("window/logMessage" , LogMessageParams (type = type , message = message ))
371+
372+ def log_trace (self , message : str , verbose : Optional [str ] = None ) -> None :
373+ self .send_notification ("$/logTrace" , LogTraceParams (message = message , verbose = verbose ))
374+
375+ def _do_trace_message (self , message : JsonRPCMessage , msg : bytes ) -> None :
376+ if getattr (message , "method" , None ) not in ["$/logTrace" , "window/logMessage" ]:
377+ self ._data_logger .trace (lambda : f"JSON send: { msg .decode ()!r} " )
378+
314379 @rpc_method (name = "$/cancelRequest" , param_type = CancelParams )
315380 @__logger .call
316381 def _cancel_request (self , id : Union [int , str ], ** kwargs : Any ) -> None :
0 commit comments