본문 바로가기
기록/Python

[Python] 로그 일별 생성 PermissionError: [WinError 32] 오류 해결 기록

by 자임 2022. 10. 27.



https://lifesteps.tistory.com/179

 

[Python/Linux] 파이썬 로그 일별 생성 및 주기적으로 삭제 설정 방법 기록

https://lifesteps.tistory.com/175?category=1007014 [Python] 파이썬 로그 찍는 방법 및 오류 해결 ### log 세팅 import logging def get_logger(name=None): # 1 logger instance를 만듭니다. logger = logging..

lifesteps.tistory.com

 

이 글 이어서 오류 해결 기록



지긋지긋하게 본 오류:
PermissionError: [WinError 32] 다른 프로세스가 파일을 사용 중이기 때문에 프로세스가 액세스 할 수 없습니다: '[경로]\\apiLog_info_20221024.log' -> '[경로]\\apiLog_info_20221024.log.2022-10-24'
Call stack:

오류 해결한 줄 알았는데, 로컬에서는 내가 자체적으로 서버를 껐다 켜면서 테스트했기 때문에 해결된 것처럼 보인 거였음.
운영은 서버가 계속 돌아가면서 구동되기 때문에 여전히 일별 로그 생성할 때 오류남.


 

 


오류 해결해 보려고 참고했던 블로그 :
Error 32가 뜻하는 바도 그렇고 분명 뭔가 멀티프로세스 환경에서 다른 프로세스가 핸들을 물고 있어서 Rotating이 되지 않는 것이 분명했으므로 이를 확인하기 위해 테스트 코드를 짜 봤다.
출처: https://5kyc1ad.tistory.com/269

핸들러의 가장 큰 역할은 목적지 설정이라고 생각하면 됩니다.
getLogger() 는 이름이 제공되는 경우 지정된 이름을 가진 로거 인스턴스에 대한 참조를 반환하고, 그렇지 않으면 root 를 반환합니다. 이름은 마침표로 구분된 계층적 구조입니다. 같은 이름으로 getLogger() 를 여러 번 호출하면 같은 로거 객체에 대한 참조를 반환합니다. 
출처: https://yurimkoo.github.io/python/2019/08/11/logging.html

파이썬 logging 라이브러리 자체가 멀티 프로세싱 환경을 고려하지 않고 만들어졌기 때문에 발생한 일이라 간단하게 해결하긴 힘들어 보인다.
출처: https://5kyc1ad.tistory.com/301

동일 모듈 뿐만 아니라, 여러 모듈에서 동일한 인스턴스이름 logging.getLogger('someLogger')를 여러 번 호출해도 동일한 로거 객체에 대한 참조가 반환됩니다.
출처: https://valuefactory.tistory.com/601

웹 서버는 현재 로그 파일(app.log)을 이전 날짜 파일(app.log.yyyy-mm-dd)로 변경한 다음 새로운 로그 파일을 생성하려고 하는데 python 소스 파일이 변경시 감시하는 프로세스가 현재 로그 파일(app.log)을 엑세스하고 있기 때문에 파일 이름을 변경을 하지 못해서 발생하는 오류이다.
출처: https://yscho03.tistory.com/3

delay=True로 바꿔봤는데 안 됨.
참고: https://stackoverflow.com/questions/22459850/permissionerror-when-using-python-3-3-4-and-rotatingfilehandler



 


원인에 대한 생각:
일단 내가 쓰는 건 log.py로 모듈을 하나 만들어두고 다른 곳에서 전부 이 모듈을 임포트해서 log를 찍는 방식이다.

설정해준 대로 자정이 되면 log 파일 이름을 바꾸고 새 로그 파일을 생성해야 하는데, 로그 파일 이름을 바꿀 때, 이 로그 파일을 누군가가 계속 잡고 있어서 수정하려하면 퍼미션 오류가 나는 것 같다.. 까지는 납득함.
로그 인스턴스 생성 구조가 잘못되었다는 느낌은 받았는데 정확한 이해가 안 돼서 초반에 엄청 난항을 겪었다.
파이썬의 클래스, 객체, 인스턴스에 대한 개념을 다시 공부하고 나서야 원인을 찾을 수 있었다.

샘명의 은인: https://trustyou.tistory.com/70

 

 

 

 


해결(코드비교):


수정 전

log.py

import logging
from logging.handlers import TimedRotatingFileHandler

def get_logger(name=None):
    logger = logging.getLogger(name)
    logger.setLevel(logging.INFO)

    # log format
    formatter = logging.Formatter("%(asctime)s - %(levelname)s - [%(funcName)s:%(lineno)d] - %(message)s")

    # handler
    console = logging.StreamHandler()
    file_handler_info = TimedRotatingFileHandler("[경로]/apiLog_info.log"
                                            , when='midnight', encoding='utf-8', delay=False)
    file_handler_error = TimedRotatingFileHandler("[경로]/apiLog_error.log"
                                            , when='midnight', encoding='utf-8', delay=False)

    # level 설정
    console.setLevel(logging.INFO)
    file_handler_info.setLevel(logging.INFO)
    file_handler_error.setLevel(logging.ERROR)

    # format
    console.setFormatter(formatter)
    file_handler_info.setFormatter(formatter)
    file_handler_error.setFormatter(formatter)

    # handler add
    logger.addHandler(console)
    logger.addHandler(file_handler_info)
    logger.addHandler(file_handler_error)

    return logger


이런식으로 만들어두고 각 파일에서 log 임포트 해준 다음

 



import log

logger = log.get_logger("runserver")
logger.info(e)

/

import log

logger = log.get_logger("test")
logger.info("test")
 

이렇게... 인스턴스를 파일 별로 다 생성해서 쓰는 바람에 오류가 났던 거였음.





수정 후

log.py

import logging
from logging.handlers import TimedRotatingFileHandler

logger = logging.getLogger('log')
logger.setLevel(logging.INFO)

# log format
formatter = logging.Formatter("%(asctime)s - %(levelname)s - [%(funcName)s:%(lineno)d] - %(message)s")

# handler
console = logging.StreamHandler()
file_handler_info = TimedRotatingFileHandler("[경로]/apiLog_info.log"
                                        , when='midnight', encoding='utf-8', delay=False)
file_handler_error = TimedRotatingFileHandler("[경로]/apiLog_error.log"
                                        , when='midnight', encoding='utf-8', delay=False)

# level 설정
console.setLevel(logging.INFO)
file_handler_info.setLevel(logging.INFO)
file_handler_error.setLevel(logging.ERROR)

# format
console.setFormatter(formatter)
file_handler_info.setFormatter(formatter)
file_handler_error.setFormatter(formatter)

# handler add
logger.addHandler(console)
logger.addHandler(file_handler_info)
logger.addHandler(file_handler_error)


이렇게 바꾸고


log.logger.info(e)


이렇게 로그 남겨주니까 로그파일 일별 분리 완전 잘 됨.

파이썬 구조에 대해 제대로 파악하지 않고 따라 개발해서 생긴 일인 것 같다. 객체지향에 대한 공부도 좀 더 필요한듯.



*
파이썬 클래스-인스턴스-객체 / 메서드-생성자 설명 쉽게 잘 쓰여 있는 글
https://wikidocs.net/85
https://kingnamji.tistory.com/6
https://wikidocs.net/28#_2