log4j2를 사용하여 로그 파일을 생성하는 Java 애플리케이션이 있고 다른 스크립트를 사용하여 프로세스를 다시 시작하기 전에 프로세스를 중지하는 스크립트가 있습니다. 매일 자정에 중지하고 다시 시작하는 사이에 5분간의 일시 중지가 있습니다. 시작 스크립트에서 "mv" 명령을 사용하여 로그 파일의 이름을 확장자로 timstamp로 바꿉니다. 문제는 로그 파일 중 하나의 파일 시작 부분(수 MB)에 널 문자가 포함되어 있고 로그 파일이 바이너리 파일이 된다는 것입니다. 문제에 대한 추가 컨텍스트를 제공하기 위한 몇 가지 관찰 참고 사항: 1.- 동일한 버전의 Java 애플리케이션을 사용하는 다른 호스트에서 동일한 시작 스크립트가 사용됩니다. 이 문제는 전혀 존재하지 않습니다. 2.- 가끔. 즉, 한 주에는 5개의 로그 파일이 모두 손상되고 다른 주에는 로그 파일이 정상입니다. 3.- 유사한 개발자 Linux 호스트에서는 재현할 수 없습니다. 프로덕션 Linux 호스트에서만 가능합니다. 4.- 로그 파일 크기는 일반적으로 하루에 약 4 - 6GB입니다. 5.- 애플리케이션은 매일 자정에 중지 + 5분 동안 일시 중지 + 스크립트를 통해 시작됩니다. 6.- hexdump를 사용하여 바이너리 로그 파일의 내용을 봅니다. 몇 MB의 널 문자로 시작한 다음 일반적인 일반 ASCII 콘텐츠로 시작합니다.
어떤 조언이라도 대단히 감사하겠습니다. 감사해요!
답변1
"바이너리" 로그 파일의 크기를 보고 ls -l
얻을 수 있는 크기와 비교할 때 du -k
흥미로운 점을 발견할 수 있습니다. 파일이 디스크에서 차지하는 공간보다 더 큰 것처럼 보입니다!
Java 애플리케이션 프로세스의 두 번째 복사본이 실행 중이거나 프로덕션 애플리케이션이 종료를 완료하는 데 5분 이상 걸리는 경우도 있습니다.
따라서 null 문자가 발생하면 이전에 로그 파일이 기록된 위치를 기억하는 응용 프로그램 프로세스가 여전히 실행 중입니다. 쓰기 위해 파일을 열고, seek()
해당 위치에 쓰고, 로그 메시지를 씁니다. 평소와 같습니다.
그러나 파일이 이전에 존재하지 않았다면( mv
삭제되었기 때문에) 정확히 이것이 생성되는 것입니다.스파스 파일. 이것은 매우 오래된 Unix 파일 시스템 기능입니다.
스파스 파일은 본질적으로 데이터 압축의 가장 간단한 프로토타입입니다. 널 바이트만 포함하는 전체 디스크 블록은 실제로 디스크에 데이터로 저장되지 않지만 파일 블록이 어디에 있는지 시스템에 알려주는 파일 시스템 메타데이터는 효과적으로 특수 태그를 얻습니다. "이 파일 위치에 X 널 바이트 블록을 삽입하십시오"를 의미합니다.
쓰기용 파일을 열어 스파스 파일을 만든 다음실제로 아무것도 쓰지 않고도 현재 파일의 끝을 넘어서는 콘텐츠를 찾습니다.그런 다음 뭔가를 쓰십시오. 대부분의 Unix 스타일 파일 시스템은 널로 채워진 중간 쓰기 블록을 디스크에 명시적으로 쓰는 대신 이전 EOF와 새로 작성된 데이터 사이에 블록을 희소 블록으로 자동 추가합니다. 파일을 읽을 때 파일 시스템 드라이버는 빈 바이트 블록으로 희소 블록을 자동으로 채우므로 애플리케이션은 이를 전혀 인식할 필요가 없습니다.
fuser
로그 파일이 열려 있는지 확인하고 열려 있으면 오류 메시지와 함께 중지 하는 테스트를 애플리케이션 시작 스크립트에 추가할 수 있습니다 . 이 같은:
LOGFILE=/some/where/log4j2.log
if fuser -s $LOGFILE; then
echo "ERROR: $LOGFILE is still in use. Maybe the app is still running. Make it stop." >&2
exit 1
fi
# add here your commands to rotate the logs and start the application.
log4j2
실제로 자체 로그 파일 회전 기능이 있습니다. 가장 좋은 솔루션은 아마도 외부 스크립트를 사용하는 대신 이러한 기능을 사용하는 것입니다.