Skip to content

Commit 27d21b5

Browse files
committed
feat: improved logging with time information
- Operations that take a little longer now have an indication of how long they took - 2 new command line switches `--log-format` and `--log-style`, see also the Python logging documentation
1 parent d3b39be commit 27d21b5

File tree

6 files changed

+427
-379
lines changed

6 files changed

+427
-379
lines changed

packages/core/src/robotcode/core/utils/logging.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
import os
88
import reprlib
99
import time
10+
from contextlib import contextmanager
1011
from enum import Enum
1112
from typing import (
1213
Any,
1314
Callable,
1415
ClassVar,
16+
Dict,
17+
Iterator,
1518
List,
1619
Optional,
1720
Type,
@@ -188,12 +191,19 @@ def log(
188191
condition: Optional[Callable[[], bool]] = None,
189192
*args: Any,
190193
stacklevel: int = 2,
194+
context_name: Optional[str] = None,
191195
**kwargs: Any,
192196
) -> None:
193197
if self.is_enabled_for(level) and condition is not None and condition() or condition is None:
198+
depth = 0
199+
if context_name is not None:
200+
depth = self._measure_contexts.get(context_name, 0)
201+
202+
msg = (" " * depth) + (msg() if callable(msg) else msg)
203+
194204
self.logger.log(
195205
level,
196-
msg() if callable(msg) else msg,
206+
msg,
197207
*args,
198208
stacklevel=stacklevel,
199209
**kwargs,
@@ -216,6 +226,41 @@ def debug(
216226
**kwargs,
217227
)
218228

229+
_log_measure_time = log
230+
_measure_contexts: Dict[str, int] = {}
231+
232+
@contextmanager
233+
def measure_time(
234+
self,
235+
msg: Union[str, Callable[[], str]],
236+
*args: Any,
237+
level: int = logging.DEBUG,
238+
context_name: Optional[str] = None,
239+
**kwargs: Any,
240+
) -> Iterator[None]:
241+
if self.is_enabled_for(level):
242+
depth = 0
243+
244+
if context_name is not None:
245+
depth = self._measure_contexts.get(context_name, 0)
246+
247+
self._measure_contexts[context_name] = depth + 1
248+
249+
self._log_measure_time(level, f"{' '*depth}Start {msg() if callable(msg) else msg}", *args, **kwargs)
250+
251+
start_time = time.monotonic()
252+
try:
253+
yield
254+
finally:
255+
duration = time.monotonic() - start_time
256+
257+
if context_name is not None:
258+
self._measure_contexts[context_name] = depth
259+
260+
self._log_measure_time(
261+
level, f"{' '*depth}End {msg() if callable(msg) else msg} took {duration} seconds", *args, **kwargs
262+
)
263+
219264
def info(
220265
self,
221266
msg: Union[str, Callable[[], str]],

packages/language_server/src/robotcode/language_server/common/parts/diagnostics.py

Lines changed: 132 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -356,72 +356,74 @@ def run_workspace_diagnostics(self) -> None:
356356
check_current_task_canceled(1)
357357
continue
358358

359-
self._logger.debug(lambda: f"start collecting workspace diagnostics for {len(documents)} documents")
359+
with self._logger.measure_time(
360+
lambda: f"analyzing workspace for {len(documents)} documents",
361+
context_name="workspace_diagnostics",
362+
):
360363

361-
self.on_workspace_diagnostics_analyze(self)
364+
self.on_workspace_diagnostics_analyze(self)
362365

363-
if self._break_diagnostics_loop_event.is_set():
364-
self._logger.debug("break workspace diagnostics loop 1")
365-
self.on_workspace_diagnostics_break(self)
366-
continue
366+
if self._break_diagnostics_loop_event.is_set():
367+
self._logger.debug("break workspace diagnostics loop 1", context_name="workspace_diagnostics")
368+
self.on_workspace_diagnostics_break(self)
369+
continue
367370

368-
start = time.monotonic()
369-
with self.parent.window.progress(
370-
"Analyze Workspace",
371-
cancellable=False,
372-
current=0,
373-
max=len(documents),
374-
start=False,
375-
) as progress:
376-
for i, document in enumerate(documents):
377-
self._logger.debug(lambda: f"Analyze {document}")
378-
check_current_task_canceled()
371+
with self.parent.window.progress(
372+
"Analyze Workspace",
373+
cancellable=False,
374+
current=0,
375+
max=len(documents),
376+
start=False,
377+
) as progress:
378+
for i, document in enumerate(documents):
379+
check_current_task_canceled()
380+
381+
if self._break_diagnostics_loop_event.is_set():
382+
self._logger.debug(
383+
"break workspace diagnostics loop 2", context_name="workspace_diagnostics"
384+
)
385+
self.on_workspace_diagnostics_break(self)
386+
break
379387

380-
if self._break_diagnostics_loop_event.is_set():
381-
self._logger.debug("break workspace diagnostics loop 2")
382-
self.on_workspace_diagnostics_break(self)
383-
break
388+
done_something = True
384389

385-
done_something = True
386-
387-
analysis_mode = self.get_analysis_progress_mode(document.uri)
388-
389-
if analysis_mode == AnalysisProgressMode.DETAILED:
390-
progress.begin()
391-
path = document.uri.to_path()
392-
folder = self.parent.workspace.get_workspace_folder(document.uri)
393-
name = path if folder is None else path.relative_to(folder.uri.to_path())
394-
395-
progress.report(f"Analyze {i+1}/{len(documents)}: {name}", current=i + 1)
396-
elif analysis_mode == AnalysisProgressMode.SIMPLE:
397-
progress.begin()
398-
progress.report(f"Analyze {i+1}/{len(documents)}", current=i + 1)
399-
400-
try:
401-
with self._current_diagnostics_task_lock:
402-
self._current_diagnostics_task = run_as_task(self._analyse_document, document)
403-
self._current_diagnostics_task.result(self._diagnostics_task_timeout)
404-
405-
except CancelledError:
406-
self._logger.debug(lambda: f"Analyzing {document} cancelled")
407-
except BaseException as e:
408-
ex = e
409-
self._logger.exception(
410-
lambda: f"Error in analyzing ${document}: {ex}",
411-
exc_info=ex,
412-
)
413-
finally:
414-
with self._current_diagnostics_task_lock:
415-
self._current_diagnostics_task = None
390+
analysis_mode = self.get_analysis_progress_mode(document.uri)
416391

417-
self._logger.debug(
418-
lambda: f"Analyzing workspace for {len(documents)} " f"documents takes {time.monotonic() - start}s"
419-
)
392+
if analysis_mode == AnalysisProgressMode.DETAILED:
393+
progress.begin()
394+
path = document.uri.to_path()
395+
folder = self.parent.workspace.get_workspace_folder(document.uri)
396+
name = path if folder is None else path.relative_to(folder.uri.to_path())
420397

421-
if self._break_diagnostics_loop_event.is_set():
422-
self._logger.debug("break workspace diagnostics loop 3")
423-
self.on_workspace_diagnostics_break(self)
424-
continue
398+
progress.report(f"Analyze {i+1}/{len(documents)}: {name}", current=i + 1)
399+
elif analysis_mode == AnalysisProgressMode.SIMPLE:
400+
progress.begin()
401+
progress.report(f"Analyze {i+1}/{len(documents)}", current=i + 1)
402+
403+
try:
404+
with self._current_diagnostics_task_lock:
405+
self._current_diagnostics_task = run_as_task(self._analyse_document, document)
406+
self._current_diagnostics_task.result(self._diagnostics_task_timeout)
407+
408+
except CancelledError:
409+
self._logger.debug(
410+
lambda: f"Analyzing {document.uri} cancelled", context_name="workspace_diagnostics"
411+
)
412+
except BaseException as e:
413+
ex = e
414+
self._logger.exception(
415+
lambda: f"Error in analyzing ${document.uri}: {ex}",
416+
exc_info=ex,
417+
context_name="workspace_diagnostics",
418+
)
419+
finally:
420+
with self._current_diagnostics_task_lock:
421+
self._current_diagnostics_task = None
422+
423+
if self._break_diagnostics_loop_event.is_set():
424+
self._logger.debug("break workspace diagnostics loop 3", context_name="workspace_diagnostics")
425+
self.on_workspace_diagnostics_break(self)
426+
continue
425427

426428
self.on_workspace_diagnostics_collect(self)
427429

@@ -431,78 +433,86 @@ def run_workspace_diagnostics(self) -> None:
431433
if doc.opened_in_editor or self.get_diagnostics_mode(document.uri) == DiagnosticsMode.WORKSPACE
432434
]
433435

434-
for document in set(documents) - set(documents_to_collect):
435-
check_current_task_canceled()
436+
with self._logger.measure_time(
437+
lambda: f"collect workspace diagnostic for {len(documents_to_collect)} documents",
438+
context_name="collect_workspace_diagnostics",
439+
):
436440

437-
if self._break_diagnostics_loop_event.is_set():
438-
self._logger.debug("break workspace diagnostics loop 4")
439-
self.on_workspace_diagnostics_break(self)
440-
break
441-
442-
self.reset_document_diagnostics_data(document)
443-
444-
start = time.monotonic()
445-
with self.parent.window.progress(
446-
"Collect Diagnostics",
447-
cancellable=False,
448-
current=0,
449-
max=len(documents_to_collect),
450-
start=False,
451-
) as progress:
452-
for i, document in enumerate(documents_to_collect):
453-
self._logger.debug(lambda: f"Collect diagnostics for {document}")
441+
for document in set(documents) - set(documents_to_collect):
454442
check_current_task_canceled()
455443

456444
if self._break_diagnostics_loop_event.is_set():
457-
self._logger.debug("break workspace diagnostics loop 5")
445+
self._logger.debug("break workspace diagnostics loop 4")
458446
self.on_workspace_diagnostics_break(self)
459447
break
460448

461-
mode = self.get_diagnostics_mode(document.uri)
462-
if mode == DiagnosticsMode.OFF:
463-
self.reset_document_diagnostics_data(document)
464-
continue
465-
466-
done_something = True
467-
468-
analysis_mode = self.get_analysis_progress_mode(document.uri)
469-
470-
if analysis_mode == AnalysisProgressMode.DETAILED:
471-
progress.begin()
472-
path = document.uri.to_path()
473-
folder = self.parent.workspace.get_workspace_folder(document.uri)
474-
name = path if folder is None else path.relative_to(folder.uri.to_path())
475-
476-
progress.report(f"Collect {i+1}/{len(documents_to_collect)}: {name}", current=i + 1)
477-
elif analysis_mode == AnalysisProgressMode.SIMPLE:
478-
progress.begin()
479-
progress.report(f"Collect {i+1}/{len(documents_to_collect)}", current=i + 1)
480-
481-
try:
482-
with self._current_diagnostics_task_lock:
483-
self._current_diagnostics_task = self.create_document_diagnostics_task(
484-
document,
485-
False,
486-
mode == DiagnosticsMode.WORKSPACE or document.opened_in_editor,
487-
)
488-
if self._current_diagnostics_task is not None:
489-
self._current_diagnostics_task.result(self._diagnostics_task_timeout)
490-
except CancelledError:
491-
self._logger.debug(lambda: f"Collecting diagnostics for {document} cancelled")
492-
except BaseException as e:
493-
ex = e
494-
self._logger.exception(
495-
lambda: f"Error getting diagnostics for ${document}: {ex}",
496-
exc_info=ex,
449+
self.reset_document_diagnostics_data(document)
450+
451+
with self.parent.window.progress(
452+
"Collect Diagnostics",
453+
cancellable=False,
454+
current=0,
455+
max=len(documents_to_collect),
456+
start=False,
457+
) as progress:
458+
for i, document in enumerate(documents_to_collect):
459+
self._logger.debug(
460+
lambda: f"collect diagnostics for {document.uri}",
461+
context_name="collect_workspace_diagnostics",
497462
)
498-
finally:
499-
with self._current_diagnostics_task_lock:
500-
self._current_diagnostics_task = None
463+
check_current_task_canceled()
501464

502-
self._logger.debug(
503-
lambda: f"collecting workspace diagnostics for {len(documents_to_collect)} "
504-
f"documents takes {time.monotonic() - start}s"
505-
)
465+
if self._break_diagnostics_loop_event.is_set():
466+
self._logger.debug(
467+
"break workspace diagnostics loop 5", context_name="collect_workspace_diagnostics"
468+
)
469+
self.on_workspace_diagnostics_break(self)
470+
break
471+
472+
mode = self.get_diagnostics_mode(document.uri)
473+
if mode == DiagnosticsMode.OFF:
474+
self.reset_document_diagnostics_data(document)
475+
continue
476+
477+
done_something = True
478+
479+
analysis_mode = self.get_analysis_progress_mode(document.uri)
480+
481+
if analysis_mode == AnalysisProgressMode.DETAILED:
482+
progress.begin()
483+
path = document.uri.to_path()
484+
folder = self.parent.workspace.get_workspace_folder(document.uri)
485+
name = path if folder is None else path.relative_to(folder.uri.to_path())
486+
487+
progress.report(f"Collect {i+1}/{len(documents_to_collect)}: {name}", current=i + 1)
488+
elif analysis_mode == AnalysisProgressMode.SIMPLE:
489+
progress.begin()
490+
progress.report(f"Collect {i+1}/{len(documents_to_collect)}", current=i + 1)
491+
492+
try:
493+
with self._current_diagnostics_task_lock:
494+
self._current_diagnostics_task = self.create_document_diagnostics_task(
495+
document,
496+
False,
497+
mode == DiagnosticsMode.WORKSPACE or document.opened_in_editor,
498+
)
499+
if self._current_diagnostics_task is not None:
500+
self._current_diagnostics_task.result(self._diagnostics_task_timeout)
501+
except CancelledError:
502+
self._logger.debug(
503+
lambda: f"Collecting diagnostics for {document.uri} cancelled",
504+
context_name="collect_workspace_diagnostics",
505+
)
506+
except BaseException as e:
507+
ex = e
508+
self._logger.exception(
509+
lambda: f"Error getting diagnostics for ${document.uri}: {ex}",
510+
exc_info=ex,
511+
context_name="collect_workspace_diagnostics",
512+
)
513+
finally:
514+
with self._current_diagnostics_task_lock:
515+
self._current_diagnostics_task = None
506516

507517
except (SystemExit, KeyboardInterrupt, CancelledError):
508518
raise

0 commit comments

Comments
 (0)