Skip to content

Task 2-1: Hex Packet Logging cho Binary Protocol Debug

Phase: 2 Priority: Medium Module: ConnectedClient, TranslateMsgToWWJ Depends on: Không có Reference: docs/BountyHunter-ControlServer/details/feature-iot-bridge/SPEC.md

Background

Binary protocol giữa ControlServer và claw machines sử dụng raw hex bytes. Khi có bug (packet sai format, checksum lỗi, length mismatch), developer hiện tại không có cách nào xem raw bytes — chỉ thấy exception stacktrace hoặc silent discard. PLAN.md Phase 1 đề cập hex packet logging nhưng chưa implement. Thiếu visibility này làm debug production incident rất khó: developer phải attach debugger hoặc thêm log tạm thời mỗi lần có sự cố.

Tasks

Note: SLF4J log.debug() chỉ evaluate arguments nếu DEBUG level enabled. Tuy nhiên, Arrays.toString(byte[]) tạo intermediate String ngay cả khi level không match. Dùng log.isDebugEnabled() guard hoặc implement LogUtil.toHexString() như một lazy Supplier<String> để tránh overhead khi không cần log. Trong production (INFO level), không có hex string nào được tạo ra.

  • [ ] Tạo static utility method LogUtil.toHexString(byte[] data) trong package util:
public class LogUtil {
    /**
     * Converts byte array to uppercase hex string with spaces.
     * Example: {0xFE, 0x01, 0x00} → "FE 01 00"
     */
    public static String toHexString(byte[] data) {
        if (data == null || data.length == 0) return "(empty)";
        StringBuilder sb = new StringBuilder(data.length * 3);
        for (int i = 0; i < data.length; i++) {
            if (i > 0) sb.append(' ');
            sb.append(String.format("%02X", data[i] & 0xFF));
        }
        return sb.toString();
    }
}
  • [ ] Log outbound packets trong sendDataToMachine() (trước khi out.write(data)):
if (log.isDebugEnabled()) {
    log.debug("[TX→{}] {} bytes: {}", macIp, data.length, LogUtil.toHexString(data));
}
out.write(data);
out.flush();
  • [ ] Log inbound packets sau khi parse type trong ClientMessageHandler (hoặc readMessages() loop):
if (log.isDebugEnabled()) {
    log.debug("[RX←{}] type={} {} bytes: {}", macIp, resultType, rawBytes.length, LogUtil.toHexString(rawBytes));
}
  • [ ] Log WARN khi checksum mismatch (thay vì silent discard):
log.warn("[RX←{}] Checksum MISMATCH — expected=0x{} actual=0x{} raw: {}",
    macIp,
    String.format("%02X", expectedChecksum),
    String.format("%02X", actualChecksum),
    LogUtil.toHexString(rawBytes));
  • [ ] Log handshake magic bytes "aa" khi initial connection:
log.debug("[→{}] Sending handshake: {}", remoteAddr, LogUtil.toHexString(HANDSHAKE_BYTES));
  • [ ] Verify không có hex log ở INFO level bằng cách kiểm tra tất cả call sites chỉ dùng log.debug() hoặc log.isDebugEnabled() guard

Verification / Acceptance Criteria

  • [ ] Với logging.level.bap.partner=DEBUG (hoặc package tương đương): hex bytes xuất hiện trong logs cho mọi inbound và outbound packet
  • [ ] Format nhất quán: [TX→<macIp>] cho outbound, [RX←<macIp>] cho inbound
  • [ ] Với INFO level (production default): không có hex string nào trong logs, zero performance overhead từ LogUtil.toHexString()
  • [ ] Checksum mismatch → WARN log với raw bytes, không chỉ là silent exception
  • [ ] Unit test LogUtil.toHexString(): {0xFE, 0x01, 0x00}"FE 01 00", empty array → "(empty)", null → "(empty)"

Files to Modify

  • src/main/java/.../ConnectedClient.java
  • src/main/java/.../TranslateMsgToWWJ.java (hoặc ClientMessageHandler.java)
  • src/main/java/.../util/LogUtil.java ← file mới tạo