@@ -35,6 +35,14 @@ def get_server_info_path() -> Path:
3535 return cache_dir / "embedding_server.json"
3636
3737
38+ def get_server_log_path () -> Path :
39+ """Get the path to the server log file."""
40+ from ..config .config import Config
41+ config = Config ()
42+ cache_dir = Path (config .get_database_path ())
43+ return cache_dir / "embedding_server.log"
44+
45+
3846class HttpEmbedding (EmbeddingInterface ):
3947 """HTTP client that connects to the shared embedding server."""
4048
@@ -86,28 +94,50 @@ def _spawn_server(self):
8694 # Use the same Python interpreter
8795 python_exe = sys .executable
8896
89- # Create a detached subprocess that survives parent exit
90- if os .name == 'nt' : # Windows
91- creationflags = subprocess .CREATE_NEW_PROCESS_GROUP | subprocess .DETACHED_PROCESS
92- self ._server_process = subprocess .Popen (
93- [python_exe , "-m" , "src.embedding_server" , "--port" , str (self .port )],
94- creationflags = creationflags ,
95- stdout = subprocess .DEVNULL ,
96- stderr = subprocess .DEVNULL ,
97- cwd = str (Path (__file__ ).parent .parent .parent ),
98- )
99- else : # Unix
100- # Use nohup-style detachment
101- self ._server_process = subprocess .Popen (
102- [python_exe , "-m" , "src.embedding_server" , "--port" , str (self .port )],
103- stdout = subprocess .DEVNULL ,
104- stderr = subprocess .PIPE , # Keep stderr for debugging
105- stdin = subprocess .DEVNULL ,
106- start_new_session = True , # Detach from parent
107- cwd = str (Path (__file__ ).parent .parent .parent ),
108- )
97+ # Redirect stderr to a log file to avoid pipe buffer filling up and blocking
98+ log_path = get_server_log_path ()
99+
100+ # Rotate log if too large (10MB)
101+ try :
102+ if log_path .exists () and log_path .stat ().st_size > 10 * 1024 * 1024 :
103+ old_log = log_path .with_suffix (".log.old" )
104+ log_path .rename (old_log )
105+ except Exception as e :
106+ print (f"Warning: Failed to rotate log file: { e } " , file = sys .stderr )
109107
110- print (f"Spawned embedding server (PID { self ._server_process .pid } )" , file = sys .stderr )
108+ try :
109+ # Open in append mode
110+ log_file = open (log_path , "a" )
111+ except Exception as e :
112+ print (f"Warning: Could not open log file { log_path } : { e } " , file = sys .stderr )
113+ log_file = subprocess .DEVNULL
114+
115+ try :
116+ # Create a detached subprocess that survives parent exit
117+ if os .name == 'nt' : # Windows
118+ creationflags = subprocess .CREATE_NEW_PROCESS_GROUP | subprocess .DETACHED_PROCESS
119+ self ._server_process = subprocess .Popen (
120+ [python_exe , "-m" , "src.embedding_server" , "--port" , str (self .port )],
121+ creationflags = creationflags ,
122+ stdout = subprocess .DEVNULL ,
123+ stderr = log_file ,
124+ cwd = str (Path (__file__ ).parent .parent .parent ),
125+ )
126+ else : # Unix
127+ # Use nohup-style detachment
128+ self ._server_process = subprocess .Popen (
129+ [python_exe , "-m" , "src.embedding_server" , "--port" , str (self .port )],
130+ stdout = subprocess .DEVNULL ,
131+ stderr = log_file ,
132+ stdin = subprocess .DEVNULL ,
133+ start_new_session = True , # Detach from parent
134+ cwd = str (Path (__file__ ).parent .parent .parent ),
135+ )
136+ finally :
137+ if log_file != subprocess .DEVNULL :
138+ log_file .close ()
139+
140+ print (f"Spawned embedding server (PID { self ._server_process .pid } ) logging to { log_path } " , file = sys .stderr )
111141
112142 def _ensure_server (self ):
113143 """Ensure the embedding server is running, spawning if needed.
@@ -159,10 +189,21 @@ def _ensure_server(self):
159189
160190 # Check if process died
161191 if self ._server_process and self ._server_process .poll () is not None :
162- stderr_output = ""
163- if self ._server_process .stderr :
164- stderr_output = self ._server_process .stderr .read ().decode ('utf-8' , errors = 'replace' )
165- raise RuntimeError (f"Embedding server failed to start. Exit code: { self ._server_process .returncode } \n Stderr: { stderr_output } " )
192+ # Read last few lines of log file for error details
193+ stderr_output = "Check log file for details"
194+ try :
195+ log_path = get_server_log_path ()
196+ if log_path .exists ():
197+ # Read last 1KB
198+ size = log_path .stat ().st_size
199+ with open (log_path , 'r' ) as f :
200+ if size > 1024 :
201+ f .seek (size - 1024 )
202+ stderr_output = f .read ()
203+ except Exception :
204+ pass
205+
206+ raise RuntimeError (f"Embedding server failed to start. Exit code: { self ._server_process .returncode } \n Log tail: { stderr_output } " )
166207
167208 raise RuntimeError (f"Embedding server failed to start within { STARTUP_TIMEOUT } s" )
168209
0 commit comments