Skip to content

Commit

Permalink
Create a websocket logger for using with external log parsing tool (#…
Browse files Browse the repository at this point in the history
…1121)

* Change log_server setting from boolean to a list of logger types
  (boolean still supported for backward-compatibility)
* Use a simple, third party websocket server. The only one I found that
  didn't require async / coroutines support.
  • Loading branch information
rchl committed Jun 18, 2020
1 parent 861137c commit 723b129
Show file tree
Hide file tree
Showing 19 changed files with 664 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
- run: echo ::add-path::$HOME/.local/bin
- run: sudo apt update
- run: sudo apt install --no-install-recommends -y x11-xserver-utils python3-pip
- run: pip3 install mypy==0.750 flake8==3.7.9 yapf==0.29.0 --user
- run: pip3 install mypy==0.780 flake8==3.8.3 yapf==0.30.0 --user
- run: mypy -p plugin
- run: flake8 plugin tests
- run: yapf --diff plugin/core/main.py
13 changes: 11 additions & 2 deletions LSP.sublime-settings
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,19 @@
// Show verbose debug messages in the sublime console.
"log_debug": false,

// Show messages from language servers in the Language Servers output panel.
// Log communication from and to language servers.
// Set to an array of values:
// - "panel" - log to the LSP Language Servers output panel
// - "remote" - start a local websocket server on port 9981. Can be connected to with
// a websocket client to receive the log messages in real time.
// For backward-compatibility, when set to "true", enables the "panel" logger and when
// set to "false" disables logging.
// This output panel can be toggled from the command palette with the
// command "LSP: Toggle Panel: Language Servers".
"log_server": false,
"log_server": [
// "panel",
// "remote",
],

// Show language server stderr output in the Language Servers output panel.
// This output panel can be toggled from the command palette with the
Expand Down
2 changes: 1 addition & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ Add these settings to LSP settings, your Sublime settings, Syntax-specific setti
* `show_symbol_action_links` `false` *show links to symbol actions like go to, references and rename in the hover popup*
* `disabled_capabilities`, `[]` *Turn off client capabilities (features): "hover", "completion", "documentHighlight", "colorProvider", "signatureHelp"
* `log_debug` `false` *show debug logging in the sublime console*
* `log_server` `false` *log language server communication in a dedicated panel*
* `log_server` `[]` *log communication from and to language servers*
* `log_stderr` `false` *show language server stderr output in the console*


Expand Down
2 changes: 1 addition & 1 deletion docs/troubleshooting.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## Self-help instructions

Enable LSP logging: In LSP Settings enable the `log_debug` setting.
Enable server logging: set `log_server` and `log_stderr` to `true`
Enable server logging: set `log_server` to ["panel"] and `log_stderr` to `true`
Run "LSP: Toggle Log Panel" from the command palette. No restart is needed.
If you believe the issue is with this package, please include the output from the Sublime console in your issue report!

Expand Down
4 changes: 4 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ mypy_path = stubs
[mypy-tests]
check_untyped_defs = True
disallow_untyped_defs = False

[mypy-third_party.*]
ignore_errors = True
ignore_missing_imports = True
2 changes: 1 addition & 1 deletion plugin/core/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def trace() -> None:
debug("TRACE (unknown frame)")
return
previous_frame = current_frame.f_back
file_name, line_number, function_name, _, __ = inspect.getframeinfo(previous_frame)
file_name, line_number, function_name, _, __ = inspect.getframeinfo(previous_frame) # type: ignore
file_name = file_name[len(sublime.packages_path()) + len("/LSP/"):]
debug("TRACE {0:<32} {1}:{2}".format(function_name, file_name, line_number))

Expand Down
22 changes: 13 additions & 9 deletions plugin/core/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

class Logger(metaclass=ABCMeta):

@abstractmethod
def stderr_message(self, message: str) -> None:
pass

@abstractmethod
def outgoing_response(self, request_id: Any, params: Any) -> None:
pass
Expand Down Expand Up @@ -121,7 +125,7 @@ class Client(TransportCallbacks):
def __init__(self, logger: Logger) -> None:
self.transport = None # type: Optional[Transport]
self.request_id = 0 # Our request IDs are always integers.
self.logger = logger
self._logger = logger
self._response_handlers = {} # type: Dict[int, Tuple[Callable, Optional[Callable[[Any], None]]]]
self._sync_request_result = SyncRequestStatus()
self._sync_request_lock = Lock()
Expand All @@ -139,7 +143,7 @@ def send_request(
self.request_id += 1
request_id = self.request_id
self._response_handlers[request_id] = (handler, error_handler)
self.logger.outgoing_request(request_id, request.method, request.params, blocking=False)
self._logger.outgoing_request(request_id, request.method, request.params, blocking=False)
self.send_payload(request.to_payload(request_id))

def execute_request(
Expand All @@ -159,7 +163,7 @@ def execute_request(
try:
self.request_id += 1
request_id = self.request_id
self.logger.outgoing_request(request_id, request.method, request.params, blocking=True)
self._logger.outgoing_request(request_id, request.method, request.params, blocking=True)
self._sync_request_result.prepare(request_id) # After this, is_requesting() returns True.
self.send_payload(request.to_payload(request_id))
self._response_handlers[request_id] = (handler, error_handler)
Expand Down Expand Up @@ -204,15 +208,15 @@ def flush_deferred_responses(self) -> None:
self._deferred_responses.clear()

def send_notification(self, notification: Notification) -> None:
self.logger.outgoing_notification(notification.method, notification.params)
self._logger.outgoing_notification(notification.method, notification.params)
self.send_payload(notification.to_payload())

def send_response(self, response: Response) -> None:
self.logger.outgoing_response(response.request_id, response.result)
self._logger.outgoing_response(response.request_id, response.result)
self.send_payload(response.to_payload())

def send_error_response(self, request_id: Any, error: Error) -> None:
self.logger.outgoing_error_response(request_id, error)
self._logger.outgoing_error_response(request_id, error)
self.send_payload({'jsonrpc': '2.0', 'id': request_id, 'error': error.to_lsp()})

def exit(self) -> None:
Expand All @@ -238,7 +242,7 @@ def deduce_payload(
result = payload.get("params")
if "id" in payload:
req_id = payload["id"]
self.logger.incoming_request(req_id, method, result)
self._logger.incoming_request(req_id, method, result)
if handler is None:
self.send_error_response(req_id, Error(ErrorCode.MethodNotFound, method))
else:
Expand All @@ -247,7 +251,7 @@ def deduce_payload(
else:
if self._sync_request_result.is_idle():
res = (handler, result, None, "notification", method)
self.logger.incoming_notification(method, result, res[0] is None)
self._logger.incoming_notification(method, result, res[0] is None)
return res
else:
self._deferred_notifications.append(payload)
Expand All @@ -256,7 +260,7 @@ def deduce_payload(
handler, result, is_error = self.response_handler(response_id, payload)
response_tuple = (handler, result, None, None, None)
blocking = self._sync_request_result.is_ready()
self.logger.incoming_response(response_id, result, is_error, blocking)
self._logger.incoming_response(response_id, result, is_error, blocking)
return response_tuple
else:
debug("Unknown payload type: ", payload)
Expand Down
1 change: 1 addition & 0 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ def call_manager(self, method: str, *args: Any) -> None:

def on_stderr_message(self, message: str) -> None:
self.call_manager('handle_stderr_log', self, message)
self._logger.stderr_message(message)

def _supports_workspace_folders(self) -> bool:
return self.has_capability("workspace.workspaceFolders.supported")
Expand Down
3 changes: 2 additions & 1 deletion plugin/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ def update_settings(settings: Settings, settings_obj: sublime.Settings) -> None:
settings.show_references_in_quick_panel = read_bool_setting(settings_obj, "show_references_in_quick_panel", False)
settings.disabled_capabilities = read_array_setting(settings_obj, "disabled_capabilities", [])
settings.log_debug = read_bool_setting(settings_obj, "log_debug", False)
settings.log_server = read_bool_setting(settings_obj, "log_server", False)
log_server_default = ["panel"] if read_bool_setting(settings_obj, "log_server", False) else []
settings.log_server = read_array_setting(settings_obj, "log_server", log_server_default)
settings.log_stderr = read_bool_setting(settings_obj, "log_stderr", False)
settings.lsp_format_on_save = read_bool_setting(settings_obj, "lsp_format_on_save", False)
settings.lsp_code_actions_on_save = read_dict_setting(settings_obj, "lsp_code_actions_on_save", {})
Expand Down
2 changes: 1 addition & 1 deletion plugin/core/transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def on_stderr_message(self, message: str) -> None:
class JsonRpcTransport(Transport):

def __init__(self, name: str, process: subprocess.Popen, socket: Optional[socket.socket], reader: IO[bytes],
writer: IO[bytes], stderr: IO[bytes], callback_object: TransportCallbacks) -> None:
writer: IO[bytes], stderr: Optional[IO[bytes]], callback_object: TransportCallbacks) -> None:
self._process = process
self._socket = socket
self._reader = reader
Expand Down
2 changes: 1 addition & 1 deletion plugin/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self) -> None:
self.show_references_in_quick_panel = False
self.disabled_capabilities = [] # type: List[str]
self.log_debug = False
self.log_server = False
self.log_server = [] # type: List[str]
self.log_stderr = False
self.lsp_format_on_save = False
self.lsp_code_actions_on_save = {} # type: Dict[str, bool]
Expand Down
Loading

0 comments on commit 723b129

Please sign in to comment.