콘텐츠로 이동
무료 평가 시작

구조화된 진단 로그의 개념

PDComm은 통신 문제를 조사할 때 켜는 선택형(opt-in) 진단 로그(diagnostic log)를 제공합니다. 기본값은 꺼짐입니다. 켜면 한 줄에 JSON 객체 하나를 쓰는 JSON Lines(JSONL) 형식으로 기록합니다. 사람이 읽을 수 있고, Fluent Bit 같은 수집기(collector)가 그대로 처리할 수 있습니다.

제품 로그 제어 수단
SECS/GEM SDK (pdcomm_ffi) 환경 변수, C pdcomm_logging_*, C++ pdcomm::DiagnosticLogging, .NET PDComm.DiagnosticLogging
Modbus SDK (pdcomm_modbus_ffi) 환경 변수, C pdcomm_modbus_logging_*, C++ pdcomm::modbus::DiagnosticLogging, .NET PDComm.Modbus.DiagnosticLogging
Modbus 시뮬레이터 환경 변수, 헤드리스·MCP의 logging_configure, logging_status, logging_flush 동작
OPC UA 시뮬레이터 환경 변수, 헤드리스·MCP의 같은 동작, GUI의 진단 로그 설정

라이브러리마다 로거(logger)가 따로 있습니다. 한 프로세스가 SECS/GEM과 Modbus 라이브러리를 함께 쓰면 상태 카운터, instance_id, sequence도 따로 셉니다. 각 로거는 서로 다른 파일에 쓰도록 설정합니다.

{"arch":"aarch64","build_id":"…","component":"ffi","event":"call_entered","fields":{"endpoint_id":1,"input_bytes":39},"instance_id":"55856-1791996289686444000","level":"debug","monotonic_us":286,"os":"macos","pid":55856,"protocol":"modbus","request_id":1,"schema_version":1,"sdk_version":"0.1.0","sequence":1,"thread":"ThreadId(2)","timestamp_unix_ms":1791996289686}

키는 알파벳 순서로 씁니다.

필드 의미
schema_version 레코드 형식 버전입니다. 현재 1입니다.
timestamp_unix_ms 사건 시각입니다. UTC 기준 Unix epoch 밀리초입니다.
monotonic_us 로거가 만들어진 뒤 지난 마이크로초입니다. 같은 로거 안에서만 비교합니다.
sequence 로거마다 1부터 증가하는 번호입니다. 손실과 중복을 찾을 때 씁니다.
instance_id 로거 인스턴스 식별자(<pid>-<시작 나노초>)입니다.
pid, thread 프로세스 ID와 스레드
sdk_version, build_id, os, arch 라이브러리 버전, 빌드 식별자, 운영체제, CPU 아키텍처
level error, warn, info, debug, trace
protocol, component, event 사건이 난 프로토콜(secsgem, modbus 등), 구성 요소, 사건 이름
request_id 같은 API 호출에서 나온 레코드를 묶는 번호입니다.
fields 사건별 세부 값입니다.

한 요청은 여러 레코드를 남깁니다. 예를 들어 Modbus TCP 클라이언트 요청 하나는 call_entered → lock_acquired → action_started → submitted → socket_write → socket_read → completed 또는 failed → call_completed 순서로 기록됩니다.

  • instance_id와 request_id로 같은 요청의 레코드를 모읍니다.
  • Modbus는 fields의 connection_id와 transaction_id를 함께 봅니다. 트랜잭션 ID는 연결마다 다시 쓰일 수 있습니다.
  • SECS/GEM은 HSMS 상태 변화와 system_bytes를 함께 봅니다.
  • monotonic_us는 같은 로거 안의 순서와 간격을 잽니다. 다른 호스트의 로그와 비교할 때는 timestamp_unix_ms를 쓰고 두 호스트의 시계를 동기화합니다.

자세한 순서는 error < warn < info < debug < trace입니다. 평소에는 info, 통신 문제를 조사할 때는 debug, 짧은 시간 동안 아주 자세히 볼 때는 trace를 사용합니다. 대부분의 송수신 사건은 debug에 기록합니다.

파일에 쓰면 크기 기준으로 회전(rotation)합니다. 다음 레코드가 max_bytes(기본 10MiB)를 넘기면 현재 파일을 .1로 바꾸고 이전 파일을 하나씩 밀어냅니다. files(기본 3)는 현재 파일을 포함한 개수입니다. 기본값이면 client.jsonl, client.jsonl.1, client.jsonl.2가 남습니다. Unix에서는 새 파일을 소유자만 읽고 쓰는 권한(0600)으로 만듭니다.

파일을 지정하지 않으면 표준 오류(stderr)에 씁니다. 헤드리스 시뮬레이터와 MCP 서버의 표준 출력은 응답 전용이므로 로그와 섞이지 않습니다.

로그는 통신을 느리게 만들면 안 됩니다. 그래서 다음 규칙을 따릅니다.

  • 레코드는 최대 1024개를 담는 큐(queue)를 거쳐 별도 스레드가 씁니다. 큐가 가득 차면 버립니다.
  • 통신 스레드가 로거 잠금을 바로 얻지 못하면 기다리지 않고 버립니다.
  • 줄바꿈을 포함해 8192바이트를 넘는 레코드는 버립니다.

버린 레코드는 상태의 dropped, 쓰기 실패는 write_errors, 거부한 설정은 configuration_errors로 셉니다. 이 값이 0이 아니면 로그에 없는 사건이 실제로 없었다고 판단하지 않습니다. 비정상 종료 직전 큐와 운영체제 캐시의 레코드는 보장하지 않으므로, 종료 전에 플러시(flush)합니다.

로그에는 프레임, PDU, 메시지 본문 같은 페이로드(payload)를 쓰지 않습니다. 길이(body_bytes, pdu_bytes, bytes)만 씁니다. 비밀 키, 인증 정보, 환경 변수 전체도 쓰지 않습니다. 다만 접속한 상대 주소와 수신 대기 주소는 기록합니다. 로그를 외부에 보낼 때는 이 점을 확인합니다.

forensics를 켜면(debug 또는 trace 레벨 필요) 약 1초마다 프로세스 자원 표본(resource_sample)을 남깁니다. 20ms 이상 걸린 내부 단계는 slow_stage로 기록합니다. 응답이 늦을 때 원인이 SDK 처리인지, 상대 장비인지, 운영체제 스케줄링인지 가르는 데 씁니다.