Better handling of stack traces and exc_info (#6253)

This commit is contained in:
Robert Brennan 2025-01-14 10:22:39 -05:00 committed by GitHub
parent 37b7173481
commit 4da812c781
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 41 additions and 50 deletions

View File

@ -109,7 +109,6 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
except toml.TomlDecodeError as e:
logger.openhands_logger.warning(
f'Cannot parse config from toml, toml values have not been applied.\nError: {e}',
exc_info=False,
)
return
@ -167,7 +166,6 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
except (TypeError, KeyError) as e:
logger.openhands_logger.warning(
f'Cannot parse [{key}] config from toml, values have not been applied.\nError: {e}',
exc_info=False,
)
else:
logger.openhands_logger.warning(f'Unknown section [{key}] in {toml_file}')
@ -204,7 +202,6 @@ def load_from_toml(cfg: AppConfig, toml_file: str = 'config.toml'):
except (TypeError, KeyError) as e:
logger.openhands_logger.warning(
f'Cannot parse [sandbox] config from toml, values have not been applied.\nError: {e}',
exc_info=False,
)

View File

@ -52,6 +52,14 @@ LOG_COLORS: Mapping[str, ColorType] = {
}
class StackInfoFilter(logging.Filter):
def filter(self, record):
if record.levelno >= logging.ERROR:
record.stack_info = True
record.exc_info = True
return True
class NoColorFormatter(logging.Formatter):
"""Formatter for non-colored logging in files."""
@ -260,6 +268,9 @@ if LOG_LEVEL in logging.getLevelNamesMapping():
current_log_level = logging.getLevelNamesMapping()[LOG_LEVEL]
openhands_logger.setLevel(current_log_level)
if DEBUG:
openhands_logger.addFilter(StackInfoFilter())
if current_log_level == logging.DEBUG:
LOG_TO_FILE = True
openhands_logger.debug('DEBUG mode enabled.')

View File

@ -301,8 +301,6 @@ class EventStream:
except Exception as e:
logger.error(
f'Error in event callback {callback_id} for subscriber {subscriber_id}: {str(e)}',
exc_info=True,
stack_info=True,
)
# Re-raise in the main thread so the error is not swallowed
raise e

View File

@ -46,5 +46,4 @@ class RetryMixin:
exception = retry_state.outcome.exception()
logger.error(
f'{exception}. Attempt #{retry_state.attempt_number} | You can customize retry values in the configuration.',
exc_info=False,
)

View File

@ -268,7 +268,7 @@ class LLMSummarizingCondenser(Condenser):
return [summary_event]
except Exception as e:
logger.error('Error condensing events: %s', str(e), exc_info=False)
logger.error(f'Error condensing events: {str(e)}')
raise e

View File

@ -522,9 +522,7 @@ if __name__ == '__main__':
observation = await client.run_action(action)
return event_to_dict(observation)
except Exception as e:
logger.error(
f'Error processing command: {str(e)}', exc_info=True, stack_info=True
)
logger.error(f'Error while running /execute_action: {str(e)}')
raise HTTPException(
status_code=500,
detail=traceback.format_exc(),
@ -716,7 +714,7 @@ if __name__ == '__main__':
return sorted_entries
except Exception as e:
logger.error(f'Error listing files: {e}', exc_info=True)
logger.error(f'Error listing files: {e}')
return []
logger.debug(f'Starting action execution API on port {args.port}')

View File

@ -193,11 +193,7 @@ class Runtime(FileEditRuntimeMixin):
e, AgentRuntimeDisconnectedError
):
err_id = 'STATUS$ERROR_RUNTIME_DISCONNECTED'
logger.error(
'Unexpected error while running action',
exc_info=True,
stack_info=True,
)
self.log('error', f'Unexpected error while running action: {str(e)}')
self.log('error', f'Problematic action: {str(event)}')
self.send_error_message(err_id, str(e))
self.close()

View File

@ -195,8 +195,8 @@ class BrowserEnv:
self.process.join(5) # Wait for the process to terminate
self.agent_side.close()
self.browser_side.close()
except Exception:
logger.error('Encountered an error when closing browser env', exc_info=True)
except Exception as e:
logger.error(f'Encountered an error when closing browser env: {e}')
@staticmethod
def image_to_png_base64_url(

View File

@ -42,7 +42,7 @@ async def get_vscode_url(request: Request):
logger.debug(f'Runtime VSCode URL: {runtime.vscode_url}')
return JSONResponse(status_code=200, content={'vscode_url': runtime.vscode_url})
except Exception as e:
logger.error(f'Error getting VSCode URL: {e}', exc_info=True)
logger.error(f'Error getting VSCode URL: {e}')
return JSONResponse(
status_code=500,
content={
@ -81,7 +81,7 @@ async def get_hosts(request: Request):
logger.debug(f'Runtime hosts: {runtime.web_hosts}')
return JSONResponse(status_code=200, content={'hosts': runtime.web_hosts})
except Exception as e:
logger.error(f'Error getting runtime hosts: {e}', exc_info=True)
logger.error(f'Error getting runtime hosts: {e}')
return JSONResponse(
status_code=500,
content={

View File

@ -68,7 +68,7 @@ async def list_files(request: Request, conversation_id: str, path: str | None =
try:
file_list = await call_sync_from_async(runtime.list_files, path)
except AgentRuntimeUnavailableError as e:
logger.error(f'Error listing files: {e}', exc_info=True)
logger.error(f'Error listing files: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': f'Error listing files: {e}'},
@ -95,7 +95,7 @@ async def list_files(request: Request, conversation_id: str, path: str | None =
try:
file_list = await filter_for_gitignore(file_list, '')
except AgentRuntimeUnavailableError as e:
logger.error(f'Error filtering files: {e}', exc_info=True)
logger.error(f'Error filtering files: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': f'Error filtering files: {e}'},
@ -131,7 +131,7 @@ async def select_file(file: str, request: Request):
try:
observation = await call_sync_from_async(runtime.run_action, read_action)
except AgentRuntimeUnavailableError as e:
logger.error(f'Error opening file {file}: {e}', exc_info=True)
logger.error(f'Error opening file {file}: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': f'Error opening file: {e}'},
@ -141,7 +141,7 @@ async def select_file(file: str, request: Request):
content = observation.content
return {'code': content}
elif isinstance(observation, ErrorObservation):
logger.error(f'Error opening file {file}: {observation}', exc_info=False)
logger.error(f'Error opening file {file}: {observation}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': f'Error opening file: {observation}'},
@ -207,9 +207,7 @@ async def upload_file(request: Request, conversation_id: str, files: list[Upload
runtime.config.workspace_mount_path_in_sandbox,
)
except AgentRuntimeUnavailableError as e:
logger.error(
f'Error saving file {safe_filename}: {e}', exc_info=True
)
logger.error(f'Error saving file {safe_filename}: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': f'Error saving file: {e}'},
@ -234,7 +232,7 @@ async def upload_file(request: Request, conversation_id: str, files: list[Upload
return JSONResponse(status_code=status.HTTP_200_OK, content=response_content)
except Exception as e:
logger.error(f'Error during file upload: {e}', exc_info=True)
logger.error(f'Error during file upload: {e}')
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
@ -284,7 +282,7 @@ async def save_file(request: Request):
try:
observation = await call_sync_from_async(runtime.run_action, write_action)
except AgentRuntimeUnavailableError as e:
logger.error(f'Error saving file: {e}', exc_info=True)
logger.error(f'Error saving file: {e}')
return JSONResponse(
status_code=500,
content={'error': f'Error saving file: {e}'},
@ -306,7 +304,7 @@ async def save_file(request: Request):
)
except Exception as e:
# Log the error and return a 500 response
logger.error(f'Error saving file: {e}', exc_info=True)
logger.error(f'Error saving file: {e}')
raise HTTPException(status_code=500, detail=f'Error saving file: {e}')
@ -321,7 +319,7 @@ async def zip_current_workspace(
try:
zip_file = await call_sync_from_async(runtime.copy_from, path)
except AgentRuntimeUnavailableError as e:
logger.error(f'Error zipping workspace: {e}', exc_info=True)
logger.error(f'Error zipping workspace: {e}')
return JSONResponse(
status_code=500,
content={'error': f'Error zipping workspace: {e}'},
@ -337,7 +335,7 @@ async def zip_current_workspace(
return response
except Exception as e:
logger.error(f'Error zipping workspace: {e}', exc_info=True)
logger.error(f'Error zipping workspace: {e}')
raise HTTPException(
status_code=500,
detail='Failed to zip workspace',

View File

@ -214,11 +214,9 @@ async def _get_conversation_info(
if is_running
else ConversationStatus.STOPPED,
)
except Exception: # type: ignore
logger.warning(
f'Error loading conversation: {conversation.conversation_id[:5]}',
exc_info=True,
stack_info=True,
except Exception as e:
logger.error(
f'Error loading conversation {conversation.conversation_id}: {str(e)}',
)
return None

View File

@ -70,7 +70,7 @@ async def get_litellm_models() -> list[str]:
model_list.append('ollama/' + model['name'])
break
except requests.exceptions.RequestException as e:
logger.error(f'Error getting OLLAMA models: {e}', exc_info=True)
logger.error(f'Error getting OLLAMA models: {e}')
return list(sorted(set(model_list)))

View File

@ -205,7 +205,7 @@ class AgentSession:
try:
await self.runtime.connect()
except AgentRuntimeUnavailableError as e:
logger.error(f'Runtime initialization failed: {e}', exc_info=True)
logger.error(f'Runtime initialization failed: {e}')
if self._status_callback:
self._status_callback(
'error', 'STATUS$ERROR_RUNTIME_DISCONNECTED', str(e)

View File

@ -92,12 +92,10 @@ class SessionManager:
await self._process_message(message)
except asyncio.CancelledError:
return
except Exception:
except Exception as e:
try:
asyncio.get_running_loop()
logger.warning(
'error_reading_from_redis', exc_info=True, stack_info=True
)
logger.error(f'error_reading_from_redis:{str(e)}')
except RuntimeError:
return # Loop has been shut down
@ -259,8 +257,8 @@ class SessionManager:
await conversation.disconnect()
self._detached_conversations.clear()
return
except Exception:
logger.warning('error_cleaning_detached_conversations', exc_info=True)
except Exception as e:
logger.warning(f'error_cleaning_detached_conversations: {str(e)}')
await asyncio.sleep(_CLEANUP_EXCEPTION_WAIT_TIME)
async def get_agent_loop_running(self, user_id, sids: set[str]) -> set[str]:

View File

@ -187,8 +187,8 @@ class Session:
await asyncio.sleep(0.001) # This flushes the data to the client
self.last_active_ts = int(time.time())
return True
except RuntimeError:
logger.error('Error sending', stack_info=True, exc_info=True)
except RuntimeError as e:
logger.error(f'Error sending data to websocket: {str(e)}')
self.is_alive = False
return False

View File

@ -76,10 +76,8 @@ class FileConversationStore(ConversationStore):
try:
conversations.append(await self.get_metadata(conversation_id))
except Exception:
logger.warning(
logger.error(
f'Error loading conversation: {conversation_id}',
exc_info=True,
stack_info=True,
)
conversations.sort(key=_sort_key, reverse=True)
conversations = conversations[start:end]