Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 391 – 로깅을 위한 딕셔너리 기반 구성

Author:
Vinay Sajip <vinay_sajip at red-dove.com>
Status:
Final
Type:
Standards Track
Created:
15-Oct-2009
Python-Version:
2.7, 3.2
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 PEP에서는 구성 정보를 담기 위해 딕셔너리를 사용하는 새로운 로깅 구성 방법을 설명합니다.

근거

현재 Python의 로깅 패키지를 구성하는 방법은 로깅 API를 사용하여 프로그래밍 방식으로 로깅을 구성하거나, ConfigParser 기반 구성 파일을 사용하는 것입니다.

프로그래밍 방식의 구성은 최대한의 제어 기능을 제공하지만, 구성을 Python 코드에 고정합니다. 이 방식에서는 런타임에 구성을 쉽게 변경할 수 없으며, 그 결과 사용 중인 애플리케이션의 각 부분에 대해 로깅의 상세도를 유연하게 높이거나 낮출 수 있는 기능을 잃게 됩니다. 이로 인해 문제 진단을 위한 보조 수단으로서 로깅의 유용성이 제한되며, 때로는 로깅이 프로덕션 환경에서 사용할 수 있는 유일한 진단 수단이기도 합니다.

ConfigParser 기반 구성 시스템은 사용할 수 있지만, 사용자가 로깅 패키지의 모든 측면을 구성할 수 있도록 하지는 않습니다. 예를 들어 이 시스템을 사용해서는 필터를 구성할 수 없습니다. 또한 ConfigParser 형식은 일부 사람들에게 (때로는 강한) 거부감을 불러일으키는 것으로 보입니다. 당시 Python 표준에서 지원되는 유일한 구성 형식이었기 때문에 선택되었지만, 많은 사람들은 이를 (또는 단지 로깅 구성에 선택된 특정 스키마를) 어떤 경우에는 순전히 미적인 이유로 ‘crufty’하거나 ‘ugly’하다고 여깁니다.

최근 버전의 Python에는 표준 라이브러리에 JSON 지원이 포함되어 있으며, JSON은 구성 형식으로도 사용할 수 있습니다. Google App Engine과 같은 다른 환경에서는 애플리케이션을 구성하는 데 YAML을 사용하며, 일반적으로 로깅 구성은 애플리케이션 구성의 필수적인 부분으로 간주됩니다. 현재 표준 라이브러리에는 YAML 지원이 포함되어 있지 않지만, JSON과 YAML은 모두 Python 딕셔너리로 역직렬화할 수 있으므로 두 형식 모두에 대한 지원을 공통된 방식으로 제공할 수 있습니다.

구성을 딕셔너리로 전달하여 로깅을 구성하는 방법을 제공하면, JSON 및/또는 YAML 사용자는 물론 사용자 지정 구성 방법을 사용하는 사용자도 원하는 구성을 설명할 수 있는 공통 형식을 통해 로깅을 더 쉽게 구성할 수 있습니다.

현재 ConfigParser 기반 구성 시스템의 또 다른 단점은 증분 구성을 지원하지 않는다는 점입니다. 새로운 구성이 기존 구성을 완전히 대체합니다. 다중 스레드 환경에서 증분 구성에 대한 완전한 유연성을 제공하기는 어렵지만, 새로운 구성 메커니즘에서는 증분 구성에 대한 제한적인 지원을 제공할 수 있습니다.

사양

사양은 두 부분으로 구성됩니다. 하나는 API이고, 다른 하나는 구성 정보를 전달하는 데 사용되는 딕셔너리의 형식, 즉 딕셔너리가 준수해야 하는 스키마입니다.

명명

역사적으로 로깅 패키지는 PEP 8을 준수하지 않았습니다. 향후 어느 시점에 PEP 8을 준수하도록 패키지의 메서드 및 함수 이름을 변경하여 이 문제가 수정될 것입니다. 그러나 통일성을 위해 API에 제안된 추가 사항에서는 로깅이 현재 사용하는 명명 방식과 일관된 명명 체계를 사용합니다.

API

logging.config 모듈에 다음 항목이 추가됩니다.

  • dictConfig()라고 하는 단일 인자를 받는 함수입니다. - 구성을 담고 있는 딕셔너리입니다. 예외는 딕셔너리를 처리하는 중 오류가 발생하면 발생합니다.

이 API를 사용자 지정할 수 있습니다. API Customization절을 참조하십시오. Incremental Configuration은 자체 절에서 다룹니다.

딕셔너리 스키마 - 개요

스키마를 자세히 설명하기에 앞서 객체 연결, 사용자 정의 객체 지원 및 외부 객체와 내부 객체에 대한 접근에 관해 몇 마디 언급할 필요가 있습니다.

객체 연결

스키마는 객체 그래프에서 서로 연결된 로깅 객체 집합 - 로거, 핸들러, 포매터, 필터 - 을 설명하도록 설계되었습니다. 따라서 스키마는 객체 간 연결을 표현할 수 있어야 합니다. 예를 들어, 특정 로거가 구성된 후 특정 핸들러를 연결한다고 가정해 보겠습니다. 이 논의의 목적상, 두 객체 간 연결에서 로거는 소스를, 핸들러는 대상을 나타낸다고 할 수 있습니다. 물론 구성된 객체에서는 로거가 핸들러에 대한 참조를 보유하는 것으로 이를 나타냅니다. 구성 딕셔너리에서는 각 대상 객체에 해당 객체를 모호하지 않게 식별하는 id를 부여한 다음, 소스 객체의 구성에서 해당 id를 사용하여 소스 객체와 해당 id를 가진 대상 객체 사이에 연결이 존재함을 나타냅니다.

따라서 다음 YAML 조각을 예로 살펴보겠습니다.:

formatters:
  brief:
    # configuration for formatter with id 'brief' goes here
  precise:
    # configuration for formatter with id 'precise' goes here
handlers:
  h1: #This is an id
   # configuration of handler with id 'h1' goes here
   formatter: brief
  h2: #This is another id
   # configuration of handler with id 'h2' goes here
   formatter: precise
loggers:
  foo.bar.baz:
    # other configuration for logger 'foo.bar.baz'
    handlers: [h1, h2]

(참고: 이 문서에서는 딕셔너리에 해당하는 Python 소스 형식보다 조금 더 읽기 쉬운 YAML을 사용합니다.)

로거의 id는 프로그래밍 방식으로 해당 로거에 대한 참조를 얻는 데 사용되는 로거 이름입니다. 예를 들면 foo.bar.baz입니다. 포매터와 필터의 id는 어떤 문자열 값이든 될 수 있으며(예를 들어 앞서 나온 brief, precise와 같은 값), 구성 딕셔너리를 처리하고 객체 간 연결을 결정할 때만 의미가 있고 구성 호출이 완료되면 어디에도 저장되지 않는 일시적인 값입니다.

핸들러 id는 특별하게 처리됩니다. 자세한 내용은 아래의 Handler Ids 절을 참조하십시오.

위의 조각은 foo.bar.baz라는 이름의 로거에 h1h2라는 핸들러 id로 설명되는 두 개의 핸들러를 연결해야 함을 나타냅니다. h1의 포매터는 id가 brief로 설명된 포매터이고, h2의 포매터는 id가 precise로 설명된 포매터입니다.

사용자 정의 객체

스키마는 핸들러, 필터 및 포매터에 대한 사용자 정의 객체를 지원해야 합니다. (로거는 인스턴스마다 서로 다른 유형일 필요가 없으므로 구성에서 사용자 정의 로거 클래스에 대한 지원은 없습니다.)

구성할 객체는 일반적으로 해당 구성의 세부 사항을 설명하는 딕셔너리로 표현됩니다. 일부 경우에는 로깅 시스템이 컨텍스트에서 객체를 어떻게 인스턴스화할지 추론할 수 있지만, 사용자 정의 객체를 인스턴스화할 때는 그렇게 하는 방법을 알 수 없습니다. 사용자 정의 객체 인스턴스화를 완전히 유연하게 지원하려면 사용자가 ‘factory’를 제공해야 합니다. 이는 구성 딕셔너리와 함께 호출되고 인스턴스화된 객체를 반환하는 호출 가능 객체입니다. 이는 factory에 대한 절대 임포트 경로를 특수 키 '()'로 사용할 수 있게 하여 표시합니다. 구체적인 예는 다음과 같습니다.:

formatters:
  brief:
    format: '%(message)s'
  default:
    format: '%(asctime)s %(levelname)-8s %(name)-15s %(message)s'
    datefmt: '%Y-%m-%d %H:%M:%S'
  custom:
      (): my.package.customFormatterFactory
      bar: baz
      spam: 99.9
      answer: 42

위의 YAML 조각은 세 개의 포매터를 정의합니다. 첫 번째는 id가 brief인 포매터로, 지정된 형식 문자열을 사용하는 표준 logging.Formatter 인스턴스입니다. 두 번째는 id가 default인 포매터로, 더 긴 형식을 사용하고 시간 형식도 명시적으로 정의하므로 이 두 형식 문자열로 초기화된 logging.Formatter가 생성됩니다. Python 소스 형식으로 나타내면, briefdefault포매터에는 구성 하위 딕셔너리가 있습니다.:

{
  'format' : '%(message)s'
}

및:

{
  'format' : '%(asctime)s %(levelname)-8s %(name)-15s %(message)s',
  'datefmt' : '%Y-%m-%d %H:%M:%S'
}

각각에 해당하며, 이러한 딕셔너리에는 특수 키 '()'가 포함되어 있지 않으므로 인스턴스화 방식은 컨텍스트에서 추론됩니다. 그 결과 표준 logging.Formatter 인스턴스가 생성됩니다. id가 custom인 세 번째 포매터의 구성 하위 딕셔너리는 다음과 같습니다.:

{
  '()' : 'my.package.customFormatterFactory',
  'bar' : 'baz',
  'spam' : 99.9,
  'answer' : 42
}

그리고 여기에는 사용자 정의 인스턴스화를 원한다는 의미의 특수 키 '()'가 포함되어 있습니다. 이 경우 지정된 팩토리 호출 가능 객체가 사용됩니다. 실제 호출 가능 객체이면 직접 사용되고, 그렇지 않으면 예제와 같이 문자열을 지정할 경우 일반 임포트 메커니즘을 사용하여 실제 호출 가능 객체를 찾습니다. 호출 가능 객체는 구성 하위 딕셔너리의 남은 항목을 키워드 인자로 사용하여 호출됩니다. 위 예제에서는 ID가 custom인 포매터가 해당 호출에 의해 반환되는 것으로 간주합니다.:

my.package.customFormatterFactory(bar='baz', spam=99.9, answer=42)

'()'키는 유효한 키워드 매개변수 이름이 아니므로 특수 키로 사용되었으며, 따라서 호출에 사용되는 키워드 인자의 이름과 충돌하지 않습니다. '()'는 해당 값이 호출 가능 객체라는 것을 기억하기 쉽게 하는 역할도 합니다.

외부 객체에 대한 접근

구성에서 외부에 있는 객체를 참조해야 하는 경우가 있으며, 예를 들어 sys.stderr가 있습니다. 구성이 Python 코드를 사용하여 구성된 딕셔너리라면 이는 간단하지만, 구성이 텍스트 파일(예: JSON, YAML)을 통해 제공될 때 문제가 발생합니다. 텍스트 파일에는 sys.stderr와 리터럴 문자열 'sys.stderr'를 구별할 표준적인 방법이 없습니다. 이러한 구별을 가능하게 하기 위해 구성 시스템은 문자열 값에서 특정 특수 접두사를 찾아 특별히 처리합니다. 예를 들어 리터럴 문자열 'ext://sys.stderr'가 구성의 값으로 제공되면 ext://를 제거하고 값의 나머지 부분을 일반 임포트 메커니즘을 사용하여 처리합니다.

이러한 접두사는 프로토콜 처리와 유사한 방식으로 처리됩니다. 즉, 정규 표현식 ^(?P<prefix>[a-z]+)://(?P<suffix>.*)$에 일치하는 접두사를 찾는 일반 메커니즘이 제공되며, prefix가 인식되면 suffix는 접두사에 따라 처리되고 처리 결과가 문자열 값을 대체합니다. 접두사가 인식되지 않으면 문자열 값은 있는 그대로 유지됩니다.

구현은 ext://와 같은 표준 접두사 집합을 제공하지만, 이 메커니즘을 완전히 비활성화하거나 특별한 처리를 위한 추가 또는 다른 접두사를 제공할 수도 있습니다.

내부 객체에 대한 접근

외부 객체뿐만 아니라 구성에 있는 객체를 참조해야 하는 경우도 있습니다. 이는 구성 시스템이 알고 있는 항목에 대해 암묵적으로 수행됩니다. 예를 들어 로거 또는 핸들러의 level에 대한 문자열 값 'DEBUG'라는 값은 자동으로 logging.DEBUG값으로 변환되며, handlers, filtersformatter 항목은 객체 ID를 받아 적절한 대상 객체로 확인됩니다.

그러나 로깅 시스템이 알지 못하는 사용자 정의 객체의 경우를 위해 더 일반적인 메커니즘을 제공해야 합니다. 예를 들어 다른 핸들러에 위임할 대상인 target을 받는 logging.handlers.MemoryHandler의 인스턴스를 생각해 보십시오. 시스템은 이미 이 클래스를 알고 있으므로 구성에서 지정된 target은 관련 대상 핸들러의 객체 ID이기만 하면 되며, 시스템은 해당 ID를 사용하여 핸들러를 확인합니다. 그러나 사용자가 alternate핸들러를 가진 my.package.MyHandler를 정의하는 경우 구성 시스템은 alternate가 핸들러를 참조한다는 사실을 알 수 없습니다. 이를 지원하기 위해 사용자가 지정할 수 있는 일반적인 확인 시스템이 제공됩니다.:

handlers:
  file:
    # configuration of file handler goes here

  custom:
    (): my.package.MyHandler
    alternate: cfg://handlers.file

리터럴 문자열 'cfg://handlers.file'ext://접두사가 있는 문자열과 유사한 방식으로 확인되지만, 임포트 네임스페이스가 아니라 구성 자체에서 찾습니다. 이 메커니즘은 str.format이 제공하는 방식과 유사하게 점 또는 인덱스를 사용하여 접근할 수 있도록 합니다. 따라서 다음 코드 조각이 주어지면:

handlers:
  email:
    class: logging.handlers.SMTPHandler
    mailhost: localhost
    fromaddr: my_app@domain.tld
    toaddrs:
      - support_team@domain.tld
      - dev_team@domain.tld
    subject: Houston, we have a problem.

구성에서 문자열 'cfg://handlers'는 키가 handlers인 딕셔너리로 해석되고, 문자열 'cfg://handlers.emailhandlers 딕셔너리에서 키가 email인 딕셔너리로 해석되는 식입니다. 문자열 'cfg://handlers.email.toaddrs[1]'dev_team.domain.tld'로 해석되고, 문자열 'cfg://handlers.email.toaddrs[0]''support_team@domain.tld' 값으로 해석됩니다. subject 값은 'cfg://handlers.email.subject' 또는 동등하게 'cfg://handlers.email[subject]'를 사용하여 액세스할 수 있습니다. 후자의 형식은 키에 공백이나 영숫자가 아닌 문자가 포함된 경우에만 사용하면 됩니다. 인덱스 값이 십진수 숫자로만 구성된 경우에는 해당 정수 값을 사용하여 액세스를 시도하고, 필요한 경우 문자열 값으로 대체합니다.

문자열 cfg://handlers.myhandler.mykey.123이 주어지면 이는 config_dict['handlers']['myhandler']['mykey']['123']로 해석됩니다. 문자열이 cfg://handlers.myhandler.mykey[123]로 지정되면 시스템은 config_dict['handlers']['myhandler']['mykey'][123]에서 값을 가져오려고 시도하며, 실패하면 config_dict['handlers']['myhandler']['mykey']['123']로 대체합니다.

핸들러 ID

일부 특정 로깅 구성에서는 원하는 효과를 얻기 위해 핸들러 수준을 사용해야 합니다. 그러나 항상 이름으로 식별할 수 있는 로거와 달리, 핸들러에는 증분 구성 호출을 통해 수준을 변경할 수 있는 영구 핸들이 없습니다.

따라서 이 PEP에서는 핸들러에 선택적 name 속성을 추가할 것을 제안합니다. 이를 사용하면 이름을 핸들러에 매핑하는 딕셔너리에 항목이 추가됩니다. (핸들러가 닫히면 해당 항목이 제거됩니다.) 증분 구성 호출이 이루어지면 이 딕셔너리에서 핸들러를 조회하여 구성의 값에 따라 핸들러 수준을 설정합니다. 자세한 내용은 Incremental Configuration절을 참조하십시오.

이론적으로 이러한 “영구 이름” 기능은 필터와 포매터에도 제공할 수 있습니다. 그러나 이를 증분 방식으로 구성할 수 있도록 해야 한다는 주장을 뒷받침할 충분한 근거는 없습니다. 실용성이 순수성보다 중요하다는 원칙에 따라, 핸들러에만 이 새로운 name 속성을 부여합니다. 구성에서 핸들러의 ID가 해당 핸들러의 name이 됩니다.

핸들러 이름 조회 딕셔너리는 구성 용도로만 사용되며 패키지의 공개 API 일부가 되지 않습니다.

딕셔너리 스키마 - 세부 사항

dictConfig()에 전달되는 딕셔너리에는 다음 키가 포함되어야 합니다.

  • version - 스키마 버전을 나타내는 정수 값으로 설정해야 합니다. 현재 유효한 값은 1뿐이지만, 이 키가 있으면 하위 호환성을 유지하면서 스키마를 발전시킬 수 있습니다.

그 밖의 모든 키는 선택 사항이지만, 존재하는 경우 아래에 설명된 대로 해석됩니다. 아래에서 ‘구성 딕셔너리’가 언급되는 모든 경우에는 사용자 지정 인스턴스화가 필요한지 확인하기 위해 특수한 '()' 키가 있는지 검사합니다. 필요한 경우 위에서 설명한 메커니즘을 사용하여 인스턴스화하고, 그렇지 않은 경우 컨텍스트를 사용하여 인스턴스화 방법을 결정합니다.

  • formatters - 해당 값은 각 키가 포매터 ID이고 각 값이 해당 Formatter 인스턴스를 구성하는 방법을 설명하는 딕셔너리입니다.

    구성 딕셔너리에서 formatdatefmt 키를 검색하고(기본값은 None), 이를 사용하여 logging.Formatter 인스턴스를 구성합니다.

  • filters - 해당 값은 각 키가 필터 ID이고 각 값이 해당 Filter 인스턴스를 구성하는 방법을 설명하는 딕셔너리입니다.

    구성 딕셔너리에서 name 키를 검색하고(기본값은 빈 문자열), 이를 사용하여 logging.Filter 인스턴스를 구성합니다.

  • handlers - 해당 값은 각 키가 핸들러 ID이고 각 값이 해당 Handler 인스턴스를 구성하는 방법을 설명하는 딕셔너리입니다.

    구성 딕셔너리에서 다음 키를 검색합니다:

    • class (필수). 이는 핸들러 클래스의 정규화된 이름입니다.
    • level (선택 사항). 핸들러의 수준입니다.
    • formatter (선택 사항). 이 핸들러의 포매터 ID입니다.
    • filters (선택 사항). 이 핸들러의 필터 ID 목록입니다.

    기타 모든 키는 키워드 인자로 핸들러의 생성자에 전달됩니다. 예를 들어 다음 스니펫이 주어지면:

    handlers:
      console:
        class : logging.StreamHandler
        formatter: brief
        level   : INFO
        filters: [allow_foo]
        stream  : ext://sys.stdout
      file:
        class : logging.handlers.RotatingFileHandler
        formatter: precise
        filename: logconfig.log
        maxBytes: 1024
        backupCount: 3
    

    console ID를 가진 핸들러는 logging.StreamHandler로 인스턴스화되며, 기본 스트림으로 sys.stdout를 사용합니다. file ID를 가진 핸들러는 logging.handlers.RotatingFileHandler로 인스턴스화되며, 키워드 인자 filename='logconfig.log', maxBytes=1024, backupCount=3를 사용합니다.

  • loggers - 해당 값은 각 키가 로거 이름이고 각 값이 해당 Logger 인스턴스를 구성하는 방법을 설명하는 딕셔너리입니다.

    구성 딕셔너리에서 다음 키를 검색합니다.

    • level (선택 사항). 로거의 수준입니다.
    • propagate (선택 사항). 로거의 전파 설정입니다.
    • filters (선택 사항). 이 로거의 필터 ID 목록입니다.
    • handlers (선택 사항). 이 로거의 핸들러 ID 목록입니다.

    지정된 로거는 지정된 수준, 전파, 필터 및 핸들러에 따라 구성됩니다.

  • root - 루트 로거의 구성입니다. propagate 설정은 적용되지 않는다는 점을 제외하면 구성 처리는 모든 로거와 동일하게 수행됩니다.
  • incremental - 구성을 기존 구성에 대한 증분 구성으로 해석할지 여부입니다. 이 값은 기본적으로 False로 설정되며, 이는 지정된 구성이 기존 fileConfig() API와 동일한 의미 체계로 기존 구성을 대체함을 의미합니다.

    지정된 값이 True이면 아래의 Incremental Configuration 절에 설명된 대로 구성을 처리합니다.

  • disable_existing_loggers - 기존 로거를 비활성화할지 여부입니다. 이 설정은 fileConfig() API에서 동일한 이름을 가진 매개변수를 반영합니다. 이 매개변수가 없으면 기본값은 True입니다. incrementalTrue이면 이 값은 무시됩니다.

작동 예제

다음은 YAML 형식으로 작성된 실제 작동 구성입니다(이메일 주소는 가짜입니다).:

formatters:
  brief:
    format: '%(levelname)-8s: %(name)-15s: %(message)s'
  precise:
    format: '%(asctime)s %(name)-15s %(levelname)-8s %(message)s'
filters:
  allow_foo:
    name: foo
handlers:
  console:
    class : logging.StreamHandler
    formatter: brief
    level   : INFO
    stream  : ext://sys.stdout
    filters: [allow_foo]
  file:
    class : logging.handlers.RotatingFileHandler
    formatter: precise
    filename: logconfig.log
    maxBytes: 1024
    backupCount: 3
  debugfile:
    class : logging.FileHandler
    formatter: precise
    filename: logconfig-detail.log
    mode: a
  email:
    class: logging.handlers.SMTPHandler
    mailhost: localhost
    fromaddr: my_app@domain.tld
    toaddrs:
      - support_team@domain.tld
      - dev_team@domain.tld
    subject: Houston, we have a problem.
loggers:
  foo:
    level : ERROR
    handlers: [debugfile]
  spam:
    level : CRITICAL
    handlers: [debugfile]
    propagate: no
  bar.baz:
    level: WARNING
root:
  level     : DEBUG
  handlers  : [console, file]

점진적 구성

점진적 구성에 완전한 유연성을 제공하기는 어렵습니다. 예를 들어 필터와 포매터 같은 객체는 이름이 없으므로, 구성을 설정하고 나면 구성을 확장할 때 이러한 이름 없는 객체를 참조할 수 없습니다.

또한 구성을 설정하고 난 뒤 런타임에 로거, 핸들러, 필터, 포매터의 객체 그래프를 임의로 변경해야 할 설득력 있는 이유도 없습니다. 로거와 핸들러의 상세도는 레벨만 설정하면 제어할 수 있으며(로거의 경우 전파 플래그도 설정합니다), 로거와 핸들러의 상세도는 제어할 수 있습니다. 다중 스레드 환경에서 객체 그래프를 안전하게 임의로 변경하는 것은 문제가 됩니다. 불가능하지는 않지만, 그로 인해 구현에 추가되는 복잡성에 비해 이점이 크지 않습니다.

따라서 구성 딕셔너리에 incremental 키가 존재하고 True인 경우, 시스템은 formattersfilters 항목을 완전히 무시하고 handlers 항목의 level 설정과 loggersroot 항목의 levelpropagate 설정만 처리합니다.

예를 들어 dictConfig()가 기본값으로 Falseincremental 키워드 인자를 받도록 만드는 등, 다른 방법으로 점진적 구성을 제공하는 것도 물론 가능합니다. 구성 딕셔너리의 값을 사용하자고 제안하는 이유는 구성을 피클된 딕셔너리로 소켓 리스너에 네트워크를 통해 전송할 수 있기 때문입니다. 따라서 장시간 실행되는 애플리케이션의 로깅 상세도를 애플리케이션을 중지하고 다시 시작할 필요 없이 시간에 따라 변경할 수 있습니다.

참고: 실제 경험을 바탕으로 한 점진적 구성 요구 사항에 대한 피드백을 특히 환영합니다.

API 사용자 지정

기본적인 dictConfig() API만으로는 모든 사용 사례를 충족하기에 충분하지 않습니다. 다음을 제공하여 API 사용자 지정을 지원합니다.

  • DictConfigurator라는 클래스입니다. 이 클래스의 생성자에는 구성에 사용되는 딕셔너리가 전달되고, configure() 메서드를 가집니다.
  • dictConfigClass라는 호출 가능 객체입니다. 기본적으로 DictConfigurator로 설정됩니다. 이는 원하는 경우 DictConfigurator를 적절한 사용자 정의 구현으로 교체할 수 있도록 제공됩니다.

dictConfig() 함수는 지정된 딕셔너리를 전달하여 dictConfigClass를 호출한 다음, 반환된 객체에서 configure() 메서드를 호출하여 실제로 구성을 적용합니다.:

def dictConfig(config):
    dictConfigClass(config).configure()

이는 모든 사용자 지정 요구 사항을 충족해야 합니다. 예를 들어 DictConfigurator의 서브클래스는 자체 __init__()에서 DictConfigurator.__init__()를 호출한 다음, 이어지는 configure() call에서 사용할 수 있는 사용자 지정 접두사를 설정할 수 있습니다. dictConfigClass는 해당 서브클래스에 바인딩되고, 그러면 기본적인 사용자 지정이 없는 상태에서와 정확히 같은 방식으로 dictConfig()를 호출할 수 있습니다.

소켓 리스너 구현 변경

기존 소켓 리스너 구현은 다음과 같이 수정됩니다. 구성 메시지를 받으면 json 모듈을 사용하여 딕셔너리로 역직렬화하려고 시도합니다. 이 단계가 실패하면 메시지가 fileConfig 형식이라고 간주하고 이전과 같이 처리합니다. 역직렬화에 성공하면 dictConfig()를 호출하여 결과 딕셔너리를 처리합니다.

구성 오류

구성 중 오류가 발생하면 시스템은 적절하게 설명적인 메시지와 함께 ValueError, TypeError, AttributeError 또는 ImportError를 발생시킵니다. 다음은 오류를 발생시키는 조건의 목록이며, 완전하지 않을 수 있습니다.

  • 문자열이 아니거나 실제 로깅 레벨에 해당하지 않는 문자열인 level
  • 불리언이 아닌 propagate
  • 대응하는 대상이 없는 ID
  • 증분 호출 중 존재하지 않는 핸들러 id가 발견된 경우
  • 잘못된 로거 이름
  • 내부 또는 외부 객체로 해석할 수 없는 경우

커뮤니티에서의 논의

이 PEP는 python-dev와 python-list에 발표되었습니다. 많은 논의가 이루어지지는 않았지만, 이는 틈새 주제로서는 어쩌면 당연한 일일 것입니다.

python-dev에서의 논의 스레드:

https://mail.python.org/pipermail/python-dev/2009-October/092695.html https://mail.python.org/pipermail/python-dev/2009-October/092782.html https://mail.python.org/pipermail/python-dev/2009-October/093062.html

그리고 python-list에서는:

https://mail.python.org/pipermail/python-list/2009-October/1223658.html https://mail.python.org/pipermail/python-list/2009-October/1224228.html

이 제안에 찬성하는 몇몇 의견이 있었으며, 제안 전체에 대한 반대는 없었지만, 세부 사항에 관한 몇 가지 질문과 이의가 있었습니다. 저자는 PEP를 수정함으로써 이러한 사항들을 반영했다고 판단합니다.

참조 구현

변경 사항의 참조 구현은 dictconfig.py 모듈로 제공되며, 부속 단위 테스트는 test_dictconfig.py에 포함되어 있고, 다음 위치에서 확인할 수 있습니다:

http://bitbucket.org/vinay.sajip/dictconfig

이는 소켓 리스너 변경 사항을 제외한 모든 기능을 포함합니다.