fix(ble-log): 앱 시작~종료를 한 파일에 통으로 자동 저장

Downloads 에 VesiScan_BLE_*.log 가 여러 개 생기고 그마저 내용이 비던 문제.
원인이 셋이었다.

1. 재연결마다 파일이 갈라짐
   connected() 가 liveLogFile=null 로 핸들을 버려서 BLE 가 끊겼다 붙을 때마다
   새 파일이 생겼다. 앱 시작~첫 연결 구간도 별도 파일로 빠졌다.
   → 프로세스 1회당 파일 1개. 재연결 시에는 구분선만 남긴다.

2. 파일 쓰기에 동기화가 없음
   rx() 는 BLE 콜백 스레드, 나머지는 UI 스레드에서 호출되는데 매번 파일을
   새로 열어 쓰며 잠금이 없었다. 동시 쓰기로 줄이 섞이거나 유실됐다.
   → synchronized + 핸들 유지, 줄마다 flush (강제 종료에도 그 시점까지 보존).

3. 저장 실패가 조용히 묻힘
   Downloads 쓰기가 막히면 예외를 삼켜 아무 데도 안 남았다.
   → 내부 저장소로 폴백하고 실제 경로를 파일 헤더에 기록.

추가로 두 가지:

- Export 버튼이 메모리 링버퍼(2000줄)를 덤프해 "마지막 몇 분"만 나오던 것을
  실시간 세션 파일을 그대로 내보내도록 변경.
- disconnected() 가 비정상 종료 경로에서만 호출돼, 사용자가 직접 끊으면
  아무 기록 없이 로그가 끊겼다. 이유(user/unexpected/gatt_error)를 구분해
  항상 남기도록 수정.

임상 모드 step 별 ble.log 는 부가 사본으로 그대로 유지한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-24 09:54:38 +09:00
parent 1d08c5e0cb
commit a86fefe321
3 changed files with 128 additions and 42 deletions
@@ -1315,8 +1315,15 @@ class BleManager private constructor(private val context: Context) {
gatt.close()
// 위와 같은 이유로 항상 남긴다 (gatt_error 경로).
if (wasConnected) {
debugLogger.disconnected(
if (isUserDisconnect) "user_gatt_error" else "gatt_error",
status,
)
}
if (wasConnected && lastConnectedAddress != null && !isUserDisconnect) {
debugLogger.disconnected("gatt_error", status)
onUnexpectedDisconnect?.invoke()
scheduleAutoReconnect()
} else if (reconnectAttempt > 0 && lastConnectedAddress != null) {
@@ -1422,8 +1429,15 @@ class BleManager private constructor(private val context: Context) {
onConnectionStateChanged?.invoke(false)
// 2026-08-24: 끊긴 이유와 무관하게 **항상** 로그에 남긴다.
// 이전에는 아래 if 안에서만 기록해서, 사용자가 직접 끊으면
// (isUserDisconnect=true) 아무 표시 없이 로그가 그냥 끊겼다.
debugLogger.disconnected(
if (isUserDisconnect) "user" else "unexpected",
status,
)
if (!isUserDisconnect && lastConnectedAddress != null) {
debugLogger.disconnected("unexpected", status)
onUnexpectedDisconnect?.invoke()
scheduleAutoReconnect()
}