Skip to content

Commit db38951

Browse files
committed
Make WM server logs path accessible by clients to make it easier to find for users
1 parent b0eaa9c commit db38951

7 files changed

Lines changed: 75 additions & 5 deletions

File tree

docs/wm-protocol.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,29 @@ environment, it is stopped first.
658658

659659
### `server/` — Server Lifecycle & Notifications
660660

661+
#### `server/getInfo`
662+
663+
Return static information about the running WM Server instance.
664+
665+
- **Type:** request
666+
- **Clients:** LSP, MCP, CLI
667+
- **Status:** implemented
668+
669+
**Params:** `{}`
670+
671+
**Result:**
672+
673+
```json
674+
{
675+
"log_file_path": "/abs/path/to/.venvs/dev_workspace/logs/wm_server/wm_server.log"
676+
}
677+
```
678+
679+
`log_file_path` is the absolute path to the WM Server's log file for the current process.
680+
Clients can log or display this path so the user can open the file directly when troubleshooting.
681+
682+
---
683+
661684
#### `server/shutdown`
662685

663686
Explicitly shut down the WM Server.

finecode_extension_runner/src/finecode_extension_runner/logs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def save_logs_to_file(
4343
rotation: str = "10 MB",
4444
retention: int = 3,
4545
stdout: bool = True,
46-
):
46+
) -> Path:
4747
if stdout is True:
4848
if isinstance(sys.stdout, io.TextIOWrapper):
4949
# reconfigure to be able to handle special symbols
@@ -101,7 +101,8 @@ def save_logs_to_file(
101101
encoding="utf8",
102102
filter=filter_logs,
103103
)
104-
logger.trace(f"Log file: {file_path}")
104+
logger.trace(f"Log file: {file_path_with_id}")
105+
return file_path_with_id
105106

106107

107108
def set_log_level_for_group(group: str, level: LogLevel | None):

src/finecode/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,8 @@ def start_wm_server(trace: bool, port_file: str | None, disconnect_timeout: int)
491491
"""Start the FineCode WM Server standalone (TCP JSON-RPC). Auto-stops when all clients disconnect."""
492492
from finecode.wm_server import wm_server
493493

494-
logger_utils.init_logger(log_name="wm_server", trace=trace, stdout=False)
494+
log_file_path = logger_utils.init_logger(log_name="wm_server", trace=trace, stdout=False)
495+
wm_server._log_file_path = log_file_path
495496
port_file_path = pathlib.Path(port_file) if port_file else None
496497
asyncio.run(wm_server.start_standalone(port_file=port_file_path, disconnect_timeout=disconnect_timeout))
497498

src/finecode/logger_utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from finecode_extension_runner import logs
99

1010

11-
def init_logger(log_name: str, trace: bool, stdout: bool = False):
11+
def init_logger(log_name: str, trace: bool, stdout: bool = False) -> Path:
1212
venv_dir_path = Path(sys.executable).parent.parent
1313
logs_dir_path = venv_dir_path / "logs"
1414

@@ -23,7 +23,7 @@ def init_logger(log_name: str, trace: bool, stdout: bool = False):
2323
]
2424
)
2525
logs.set_log_level_for_group(group="finecode_jsonrpc.client", level=logs.LogLevel.INFO)
26-
logs.save_logs_to_file(
26+
log_file_path = logs.save_logs_to_file(
2727
file_path=logs_dir_path / log_name / f"{log_name}.log",
2828
log_level="TRACE" if trace else "INFO",
2929
stdout=stdout,
@@ -52,3 +52,5 @@ def emit(self, record: logging.LogRecord) -> None:
5252
)
5353

5454
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
55+
56+
return log_file_path

src/finecode/lsp_server/lsp_server.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,19 @@ async def _on_initialized(ls: LanguageServer, params: types.InitializedParams):
217217
global_state.wm_client = None
218218
return
219219

220+
try:
221+
info = await global_state.wm_client.get_info()
222+
log_path = info.get("log_file_path")
223+
if log_path:
224+
ls.window_log_message(
225+
types.LogMessageParams(
226+
type=types.MessageType.Info,
227+
message=f"FineCode WM Server log: {log_path}",
228+
)
229+
)
230+
except Exception:
231+
pass
232+
220233
# Register notification handlers for server→client push messages.
221234
async def on_tree_changed(params: dict) -> None:
222235
# TODO

src/finecode/wm_client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@ async def connect(self, host: str, port: int) -> None:
8686
self._reader, self._writer = await asyncio.open_connection(host, port)
8787
self._reader_task = asyncio.create_task(self._read_loop())
8888
logger.info(f"Connected to FineCode API at {host}:{port}")
89+
try:
90+
info = await self.get_info()
91+
log_path = info.get("log_file_path")
92+
if log_path:
93+
logger.info(f"WM Server log file: {log_path}")
94+
except Exception:
95+
pass # non-critical, don't block connection
8996

9097
async def close(self) -> None:
9198
if self._reader_task is not None:
@@ -118,6 +125,12 @@ def on_notification(
118125
"""Register an async callback for a server→client notification."""
119126
self._notification_handlers[method] = callback
120127

128+
# -- Server methods -----------------------------------------------------
129+
130+
async def get_info(self) -> dict:
131+
"""Return static info about the WM Server (e.g. log file path)."""
132+
return await self.request("server/getInfo")
133+
121134
# -- Workspace methods --------------------------------------------------
122135

123136
async def list_projects(self) -> list[dict]:

src/finecode/wm_server/wm_server.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
DISCONNECT_TIMEOUT_SECONDS = 30
2828
NO_CLIENT_TIMEOUT_SECONDS = 30
2929

30+
# save so that server/getInfo can return it
31+
_log_file_path: pathlib.Path | None = None
32+
3033

3134
# ---------------------------------------------------------------------------
3235
# JSON-RPC helpers
@@ -632,6 +635,19 @@ async def _handle_runners_remove_env(
632635
return {}
633636

634637

638+
async def _handle_server_get_info(
639+
params: dict | None, ws_context: context.WorkspaceContext
640+
) -> dict:
641+
"""Handle ``server/getInfo``.
642+
643+
Returns static information about the running WM Server instance,
644+
including the path to its log file.
645+
"""
646+
return {
647+
"log_file_path": str(_log_file_path) if _log_file_path is not None else None,
648+
}
649+
650+
635651
async def _handle_server_reset(
636652
params: dict | None, ws_context: context.WorkspaceContext
637653
) -> dict:
@@ -968,6 +984,7 @@ async def _handle_run_with_partial_results_task(
968984
"runners/checkEnv": _handle_runners_check_env,
969985
"runners/removeEnv": _handle_runners_remove_env,
970986
# server/
987+
"server/getInfo": _handle_server_get_info,
971988
"server/reset": _handle_server_reset,
972989
"server/shutdown": _stub("server/shutdown"),
973990
}

0 commit comments

Comments
 (0)