"logging.config" --- Configuración de registro
**********************************************

**Código fuente:** Lib/logging/config.py


Important
^^^^^^^^^

Esta página solo contiene información de referencia. Para tutoriales,
por favor consulte

* Tutorial Básico

* Tutorial Avanzado

* Guía de registro *Logging*

======================================================================

Esta sección describe la API para configurar el módulo de registro.


Funciones de configuración
==========================

Las siguientes funciones configuran el módulo de registro. Se
encuentran en el módulo "logging.config". Su uso es opcional: puede
configurar el módulo de registro utilizando estas funciones o
realizando llamadas a la API principal (definida en "logging") y
definiendo los manejadores que se declaran en "logging" o
"logging.handlers".

logging.config.dictConfig(config)

   Toma la configuración de registro de un diccionario. El contenido
   de este diccionario se describe en Esquema del diccionario de
   configuración a continuación.

   Si se encuentra un error durante la configuración, esta función
   lanzará un "ValueError", "TypeError", "AttributeError" o
   "ImportError" con un mensaje descriptivo adecuado. La siguiente es
   una lista (posiblemente incompleta) de condiciones que lanzará un
   error:

   * Un "nivel" que no es una cadena o que es una cadena que no
     corresponde a un nivel de registro real.

   * Un valor de "propagación" que no es booleano.

   * Una identificación que no tiene un destino correspondiente.

   * Una identificación de manejador inexistente encontrada durante
     una llamada incremental.

   * Un nombre de registrador no válido.

   * Incapacidad para resolver un objeto interno o externo.

   El análisis se realiza mediante la clase "DictConfigurator", a cuyo
   constructor se le pasa el diccionario utilizado para la
   configuración, y tiene un método "configure()". El módulo
   "logging.config" tiene un atributo invocable "dictConfigClass" que
   se establece inicialmente en "DictConfigurator". Puede reemplazar
   el valor de "dictConfigClass" con una implementación adecuada
   propia.

   "dictConfig()" llama "dictConfigClass" pasando el diccionario
   especificado, y luego llama al método "configure()" en el objeto
   retornado para que la configuración surta efecto:

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

   Por ejemplo, una subclase de "DictConfigurator" podría llamar a
   "DictConfigurator.__ init__()" en su mismo "__init__()", luego
   configurar prefijos personalizados que serían utilizables en la
   siguiente llamada "configure()". "dictConfigClass" estaría
   vinculado a esta nueva subclase, y luego "dictConfig()" podría
   llamarse exactamente como en el estado predeterminado, no
   personalizado.

   Nuevo en la versión 3.2.

logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)

   Lee la configuración de registro desde un archivo de formato de
   "configparser". El formato del archivo debe ser como se describe en
   Formato de archivo de configuración. Esta función se puede invocar
   varias veces desde una aplicación, lo que permite al usuario final
   seleccionar entre varias configuraciones predeterminadas (si el
   desarrollador proporciona un mecanismo para presentar las opciones
   y cargar la configuración elegida).

   Parámetros:
      * **fname** -- Un nombre de archivo, o un objeto similar a un
        archivo, o una instancia derivada de "RawConfigParser". Si se
        pasa una instancia derivada de "RawConfigParser", se usa tal
        cual. De lo contrario, se instancia "Configparser", y la
        configuración leída desde el objeto pasado en "fname". Si eso
        tiene un método "readline()", se supone que es un objeto
        similar a un archivo y se lee usando "read_file()"; de lo
        contrario, se supone que es un nombre de archivo y se pasa a
        "read()".

      * **defaults** -- Los valores predeterminados para pasar al
        ConfigParser se pueden especificar en este argumento.

      * **disable_existing_loggers** -- Si se especifica como "False",
        los registradores que existen cuando se realiza esta llamada
        se dejan habilitados. El valor predeterminado es "True" porque
        esto permite un comportamiento antiguo de una manera
        compatible con versiones anteriores. Este comportamiento
        consiste en deshabilitar cualquier registrador no root
        existente a menos que ellos o sus antepasados se mencionen
        explícitamente en la configuración de registro.

   Distinto en la versión 3.4: Una instancia de una subclase de
   "RawConfigParser" ahora se acepta como un valor para "fname". Esto
   facilita:

   * Uso de un archivo de configuración donde la configuración de
     registro es solo parte de la configuración general de la
     aplicación.

   * Uso de una configuración leída de un archivo, y luego modificada
     por la aplicación que lo usa (por ejemplo, basada en parámetros
     de línea de comandos u otros aspectos del entorno de ejecución)
     antes de pasar a "fileConfig".

logging.config.listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None)

   Inicia un servidor de socket en el puerto especificado y escucha
   nuevas configuraciones. Si no se especifica ningún puerto, se usa
   el valor predeterminado del módulo "DEFAULT_LOGGING_CONFIG_PORT".
   Las configuraciones de registro se enviarán como un archivo
   adecuado para su procesamiento por "dictConfig()" o "fileConfig()".
   Retorna una instancia de "Thread" en la que puede llamar "start()"
   para iniciar el servidor, y que puede "join()" cuando corresponda .
   Para detener el servidor, llame a "stopListening()".

   El argumento "verificar", si se especifica, debe ser invocable, lo
   que debería verificar si los bytes recibidos en el socket son
   válidos y deben procesarse. Esto podría hacerse encriptando y / o
   firmando lo que se envía a través del socket, de modo que el
   "verificar" invocable pueda realizar la verificación o descifrado
   de la firma. El llamado invocable "verificar" se llama con un solo
   argumento (los bytes recibidos a través del socket) y debe retornar
   los bytes a procesar, o "None" para indicar que los bytes deben
   descartarse. Los bytes retornados podrían ser los mismos que los
   pasados en bytes (por ejemplo, cuando solo se realiza la
   verificación), o podrían ser completamente diferentes (tal vez si
   se realizó el descifrado).

   Para enviar una configuración al socket, lea el archivo de
   configuración y envíelo al socket como una secuencia de bytes
   precedida por una cadena de longitud de cuatro bytes empaquetada en
   binario usando "struct.pack('>L', n)".

   Nota:

     Because portions of the configuration are passed through
     "eval()", use of this function may open its users to a security
     risk. While the function only binds to a socket on "localhost",
     and so does not accept connections from remote machines, there
     are scenarios where untrusted code could be run under the account
     of the process which calls "listen()". Specifically, if the
     process calling "listen()" runs on a multi-user machine where
     users cannot trust each other, then a malicious user could
     arrange to run essentially arbitrary code in a victim user's
     process, simply by connecting to the victim's "listen()" socket
     and sending a configuration which runs whatever code the attacker
     wants to have executed in the victim's process. This is
     especially easy to do if the default port is used, but not hard
     even if a different port is used. To avoid the risk of this
     happening, use the "verify" argument to "listen()" to prevent
     unrecognised configurations from being applied.

   Distinto en la versión 3.4: Se agregó el argumento "verificar".

   Nota:

     Si desea enviar configuraciones al oyente que no deshabiliten los
     registradores existentes, deberá usar un formato JSON para la
     configuración, que utilizará "dictConfig()" para la
     configuración. Este método le permite especificar
     "disable_existing_loggers" como "False" en la configuración que
     envía.

logging.config.stopListening()

   Detiene el servidor de escucha que se creó con una llamada a
   "listen()". Esto normalmente se llama antes de llamar "join()" en
   el valor de retorno de "listen()".


Security considerations
=======================

The logging configuration functionality tries to offer convenience,
and in part this is done by offering the ability to convert text in
configuration files into Python objects used in logging configuration
- for example, as described in Objetos definidos por el usuario.
However, these same mechanisms (importing callables from user-defined
modules and calling them with parameters from the configuration) could
be used to invoke any code you like, and for this reason you should
treat configuration files from untrusted sources with *extreme
caution* and satisfy yourself that nothing bad can happen if you load
them, before actually loading them.


Esquema del diccionario de configuración
========================================

La descripción de una configuración de registro requiere una lista de
los diversos objetos para crear y las conexiones entre ellos; por
ejemplo, puede crear un manejador llamado 'consola' y luego decir que
el registrador llamado 'inicio' enviará sus mensajes al manejador
'consola'. Estos objetos no se limitan a los proporcionados por el
módulo "logging" porque podría escribir su propia clase de formateador
o manejador. Los parámetros de estas clases también pueden necesitar
incluir objetos externos como "sys.stderr". La sintaxis para describir
estos objetos y conexiones se define en Conexiones de objeto a
continuación.


Detalles del esquema del diccionario
------------------------------------

El diccionario pasado a "dictConfig()" debe contener las siguientes
claves:

* *version* - se establece en un valor entero que representa la
  versión del esquema. El único valor válido en este momento es 1,
  pero tener esta clave permite que el esquema evolucione sin perder
  la compatibilidad con versiones anteriores.

Todas las demás claves son opcionales, pero si están presentes se
interpretarán como se describe a continuación. En todos los casos a
continuación, donde se menciona un 'dict de configuración', se
verificará la clave especial "'()'" para ver si se requiere una
instanciación personalizada. Si es así, el mecanismo descrito en
Objetos definidos por el usuario a continuación se usa para crear una
instancia; de lo contrario, el contexto se usa para determinar qué
instanciar.

* *formatters*: el valor correspondiente será un diccionario en el que
  cada clave es una identificación de formateador y cada valor es un
  diccionario que describe cómo configurar la instancia
  correspondiente "Formatter".

  La configuración diccionario se busca para las claves "format" y
  "datefmt" (con los valores predeterminados de "None") y se utilizan
  para construir una instancia de "Formatter".

  Distinto en la versión 3.8: se puede agregar una tecla "validar"
  (con el valor predeterminado "True") en la sección "formatters" de
  la configuración diccionario, esto es para validar el formato.

* *filters*: el valor correspondiente será un diccionario en el que
  cada clave es una identificación de filtro y cada valor es un
  diccionario que describe cómo configurar la instancia de filtro
  correspondiente.

  El diccionario de configuración busca la clave "nombre" (por defecto
  en la cadena vacía) y esto se utiliza para construir una instancia
  de "logging.Filter".

* *handlers*: el valor correspondiente será un diccionario en el que
  cada clave es una identificación de manejador y cada valor es un
  diccionario que describe cómo configurar la instancia del manejador
  correspondiente.

  El diccionario de configuración busca las siguientes claves:

  * "clase" (obligatorio). Este es el nombre completo de la clase de
    manejador.

  * "nivel" (opcional). El nivel del manejador.

  * "formateador" (opcional). La identificación del formateador para
    este manejador.

  * "filtros" (opcional). Una lista de identificadores de los filtros
    para este manejador.

  Todas las claves *other* se pasan como argumentos de palabras clave
  al constructor del manejador. Por ejemplo, dado el fragmento:

     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

  el manejador con id "consola" se instancia como
  "logging.StreamHandler", usando "sys.stdout" como la secuencia
  subyacente. El manejador con la identificación "archivo" se
  instancia como "logging.handlers.RotatingFileHandler" con los
  argumentos de la palabra clave "filename='logconfig.log',
  maxBytes=1024, backupCount=3".

* *loggers*: el valor correspondiente será un diccionario en el que
  cada clave es un nombre de *logger* y cada valor es un diccionario
  que describe cómo configurar la instancia de *Logger*
  correspondiente.

  El diccionario de configuración busca las siguientes claves:

  * "nivel" (opcional). El nivel del registrador.

  * "propagar" (opcional). La configuración de propagación del
    registrador.

  * "filtros" (opcional). Una lista de identificadores de los filtros
    para este registrador.

  * "manipuladores" (opcional). Una lista de identificadores de los
    manejadores para este registrador.

  Los registradores especificados se configurarán de acuerdo con el
  nivel, la propagación, los filtros y los manejadores especificados.

* *root* - Esta será la configuración para el registrador *root*. El
  procesamiento de la configuración será como para cualquier
  registrador, excepto que la configuración de "propagar" no será
  aplicable.

* *incremental* - si la configuración debe interpretarse como
  incremental a la configuración existente. Este valor predeterminado
  es "False", lo que significa que la configuración especificada
  reemplaza la configuración existente con la misma semántica que la
  utilizada por la API existente "fileConfig()".

  Si el valor especificado es "True", la configuración se procesa como
  se describe en la sección sobre Configuración incremental.

* *disable_existing_loggers* - si se debe deshabilitar cualquier
  registrador no *root* existente. Esta configuración refleja el
  parámetro del mismo nombre en "fileConfig()". Si está ausente, este
  parámetro tiene el valor predeterminado "True". Este valor se ignora
  si *incremental* es "True".


Configuración incremental
-------------------------

Es difícil proporcionar flexibilidad completa para la configuración
incremental. Por ejemplo, debido a que los objetos como los filtros y
formateadores son anónimos, una vez que se configura una
configuración, no es posible hacer referencia a dichos objetos
anónimos al aumentar una configuración.

Además, no hay un caso convincente para alterar arbitrariamente el
gráfico de objetos de registradores, manejadores, filtros,
formateadores en tiempo de ejecución, una vez que se configura una
configuración; la verbosidad de los registradores y manejadores se
puede controlar simplemente estableciendo niveles (y, en el caso de
los registradores, flags de propagación). Cambiar el gráfico de
objetos de manera arbitraria y segura es problemático en un entorno de
subprocesos múltiples; Si bien no es imposible, los beneficios no
valen la complejidad que agrega a la implementación.

Por lo tanto, cuando la tecla "incremental" de un diccionario de
configuración está presente y es "True", el sistema ignorará por
completo cualquier entrada de "formateadores" y "filtros", y procesará
solo el "nivel" configuraciones en las entradas de "manejadores", y
las configuraciones de "nivel" y "propagar" en las entradas de
"registradores" y "raíz".

El uso de un valor en la configuración diccionario permite que las
configuraciones se envíen a través del cable como dictados en vinagre
a un escucha de socket. Por lo tanto, la verbosidad de registro de una
aplicación de larga ejecución puede modificarse con el tiempo sin
necesidad de detener y reiniciar la aplicación.


Conexiones de objeto
--------------------

El esquema describe un conjunto de objetos de registro (registradores,
manejadores, formateadores, filtros) que están conectados entre sí en
un gráfico de objetos. Por lo tanto, el esquema necesita representar
conexiones entre los objetos. Por ejemplo, supongamos que, una vez
configurado, un registrador particular le ha adjuntado un manejador
particular. A los fines de esta discusión, podemos decir que el
registrador representa la fuente y el manejador el destino de una
conexión entre los dos. Por supuesto, en los objetos configurados esto
está representado por el registrador que tiene una referencia al
manejador. En la configuración dict, esto se hace dando a cada objeto
de destino una identificación que lo identifica inequívocamente, y
luego utilizando la identificación en la configuración del objeto de
origen para indicar que existe una conexión entre el origen y el
objeto de destino con esa identificación.

Entonces, por ejemplo, considere el siguiente fragmento de 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]

(Nota: YAML se usa aquí porque es un poco más legible que el
formulario fuente Python equivalente para el diccionario.)

Los identificadores para los registradores son los nombres de los
registradores que se utilizarían mediante programación para obtener
una referencia a esos registradores, por ejemplo "foo.bar.baz". Los
identificadores para Formateadores y Filtros pueden ser cualquier
valor de cadena (como "breve", "preciso" arriba) y son transitorios,
ya que solo son significativos para procesar el diccionario de
configuración y se utilizan para determinar conexiones entre objetos ,
y no persisten en ninguna parte cuando se completa la llamada de
configuración.

El fragmento anterior indica que el registrador llamado "foo.bar.baz"
debe tener dos manejadores adjuntos, que se describen mediante los
identificadores de manejador "h1" y "h2". El formateador para "h1" es
el descrito por identificador "brief", y el formateador para "h2" es
el descrito por identificador "precise".


Objetos definidos por el usuario
--------------------------------

El esquema admite objetos definidos por el usuario para manejadores,
filtros y formateadores. (Los registradores no necesitan tener
diferentes tipos para diferentes instancias, por lo que no hay soporte
en este esquema de configuración para las clases de registrador
definidas por el usuario).

Los objetos a configurar son descritos por diccionarios que detallan
su configuración. En algunos lugares, el sistema de registro podrá
inferir del contexto cómo se va a instanciar un objeto, pero cuando se
va a instanciar un objeto definido por el usuario, el sistema no sabrá
cómo hacerlo. Con el fin de proporcionar una flexibilidad completa
para la creación de instancias de objetos definidos por el usuario, el
usuario debe proporcionar una 'fábrica', una llamada que se llama con
un diccionario de configuración y que retorna el objeto instanciado.
Esto se indica mediante una ruta de importación absoluta a la fábrica
disponible bajo la tecla especial "'()'". Aquí hay un ejemplo
concreto:

   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

El fragmento de YAML anterior define tres formateadores. El primero,
con identificador``breve``, es una instancia estándar
"logging.Formatter" con la cadena de formato especificada. El segundo,
con identificador "predeterminado", tiene un formato más largo y
también define el formato de hora explícitamente, y dará como
resultado "logging.Formatter" inicializado con esas dos cadenas de
formato. En forma de fuente Python, los formateadores "breve" y
"predeterminado" tienen sub-diccionarios de configuración:

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

y:

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

respectivamente, y como estos diccionarios no contienen la clave
especial "'()'", la instanciación se infiere del contexto: como
resultado, se crean las instancias estándar "logging.Formatter". El
sub-diccionario de configuración para el tercer formateador, con
identificador "personalizado", es:

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

y esto contiene la clave especial "'()'", lo que significa que se
desea la creación de instancias definida por el usuario. En este caso,
se utilizará la llamada especificada de fábrica especificada. Si es
una llamada real, se usará directamente; de lo contrario, si
especifica una cadena (como en el ejemplo), la llamada real se ubicará
utilizando mecanismos de importación normales. Se llamará al invocable
con los elementos **restantes** en el sub-diccionario de configuración
como argumentos de palabras clave. En el ejemplo anterior, se supondrá
que el formateador con identificador "personalizado" será retornado
por la llamada:

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

La clave "'()'" se ha utilizado como clave especial porque no es un
nombre de parámetro de palabra clave válido, por lo que no entrará en
conflicto con los nombres de los argumentos de palabras clave
utilizados en la llamada. El "'()'" también sirve como mnemónico de
que el valor correspondiente es invocable.


Acceso a objetos externos
-------------------------

Hay momentos en que una configuración debe referirse a objetos
externos a la configuración, por ejemplo, "sys.stderr". Si el
diccionario de configuración se construye utilizando el código Python,
esto es sencillo, pero surge un problema cuando la configuración se
proporciona a través de un archivo de texto (por ejemplo, JSON, YAML).
En un archivo de texto, no hay una forma estándar de distinguir
"sys.stderr" de la cadena literal "'sys.stderr'". Para facilitar esta
distinción, el sistema de configuración busca ciertos prefijos
especiales en valores de cadena y los trata especialmente. Por
ejemplo, si la cadena literal "'ext://sys.stderr'" se proporciona como
un valor en la configuración, entonces la "ext://" se eliminará y se
procesará el resto del valor utilizando mecanismos normales de
importación.

El manejo de dichos prefijos se realiza de manera análoga al manejo
del protocolo: existe un mecanismo genérico para buscar prefijos que
coincidan con la expresión regular
"^(?P<prefix>[a-z]+)://(?P<suffix>.*)$" por el cual, si se reconoce el
"prefix", el "suffix" se procesa de manera dependiente del prefijo y
el resultado del procesamiento reemplaza el valor de la cadena. Si no
se reconoce el prefijo, el valor de la cadena se dejará tal cual.


Acceso a objetos internos
-------------------------

Además de los objetos externos, a veces también es necesario hacer
referencia a los objetos en la configuración. El sistema de
configuración hará esto implícitamente para las cosas que conoce. Por
ejemplo, el valor de cadena "'DEBUG'" para un "nivel" en un
registrador o manejador se convertirá automáticamente al valor
"logging.DEBUG", y los "manejadores", "Las entradas de filtros" y
"formateador" tomarán una identificación de objeto y se resolverán en
el objeto de destino apropiado.

Sin embargo, se necesita un mecanismo más genérico para los objetos
definidos por el usuario que no conoce el módulo "logging". Por
ejemplo, considere "logging.handlers.MemoryHandler", que toma un
argumento "target" que es otro manejador para delegar. Dado que el
sistema ya conoce esta clase, entonces en la configuración, el
"objetivo" dado solo necesita ser la identificación del objeto del
manejador de destino relevante, y el sistema resolverá el manejador
desde la identificación. Sin embargo, si un usuario define un
"my.package.MyHandler" que tiene un manejador "alternativo", el
sistema de configuración no sabría que el "alternativo" se refería a
un manejador. Para atender esto, un sistema de resolución genérico
permite al usuario especificar:

   handlers:
     file:
       # configuration of file handler goes here

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

La cadena literal "'cfg://handlers.file'" se resolverá de manera
análoga a las cadenas con el prefijo "ext://", pero buscando en la
configuración misma en lugar del espacio de nombres de importación. El
mecanismo permite el acceso por punto o por índice, de manera similar
a la proporcionada por "str.format". Por lo tanto, dado el siguiente
fragmento:

   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.

en la configuración, la cadena "'cfg://handlers'" se resolvería al
diccionario con la clave "handlers", la cadena de caracteres
"'cfg://handlers.email" se resolvería al diccionario con clave "correo
electrónico" en el diccionario "manejadores", y así sucesivamente. La
cadena de caracteres "'cfg: //handlers.email.toaddrs [1]" se
resolvería en "'dev_team.domain.tld'" y la cadena de caracteres
"'cfg://handlers.email.toaddrs[0]" resolvería el valor
"'support_team@domain.tld'". Se puede acceder al valor de "asunto"
usando "'cfg://handlers.email.subject'" o, de manera equivalente,
"'cfg://handlers.email[subject]'". La última forma solo debe usarse si
la clave contiene espacios o caracteres no alfanuméricos. Si un valor
de índice consta solo de dígitos decimales, se intentará acceder
utilizando el valor entero correspondiente, volviendo al valor de
cadena si es necesario.

Dada una cadena "cfg://handlers.myhandler.mykey.123", esto se
resolverá en "config_dict['handlers']['myhandler']['mykey']['123']".
Si la cadena se especifica como "cfg:
//handlers.myhandler.mykey[123]", el sistema intentará recuperar el
valor de "config_dict['handlers']['myhandler']['mykey'][123]", y
vuelva a "config_dict['handlers']['myhandler']['mykey']['123']" si eso
falla.


Resolución de importación e importadores personalizados
-------------------------------------------------------

La resolución de importación, por defecto, utiliza la función
incorporada "__import__()" para importar. Es posible que desee
reemplazar esto con su propio mecanismo de importación: si es así,
puede reemplazar el atributo "importer" de "DictConfigurator" o su
superclase, la clase "BaseConfigurator". Sin embargo, debe tener
cuidado debido a la forma en que se accede a las funciones desde las
clases a través de descriptores. Si está utilizando un Python
invocable para realizar sus importaciones, y lo desea definir a nivel
de clase en lugar de a nivel de instancia, debe envolverlo con
"staticmethod()". Por ejemplo:

   from importlib import import_module
   from logging.config import BaseConfigurator

   BaseConfigurator.importer = staticmethod(import_module)

No necesita envolver con "staticmethod()" si está configurando la
importación invocable en un configurador *instance*.


Formato de archivo de configuración
===================================

El formato del archivo de configuración que entiende "fileConfig()" se
basa en la funcionalidad "configparser". El archivo debe contener
secciones llamadas "[loggers]", "[handlers]" y "[formateadores]" que
identifican por nombre las entidades de cada tipo que se definen en el
archivo. Para cada una de esas entidades, hay una sección separada que
identifica cómo se configura esa entidad. Por lo tanto, para un
registrador llamado "log01" en la sección "[loggers]", los detalles de
configuración relevantes se encuentran en una sección
"[logger_log01]". Del mismo modo, un manejador llamado "hand01" en la
sección "[handlers]" tendrá su configuración en una sección llamada
"[handler_hand01]", mientras que un formateador llamado "form01" en el
"[formateadores]" sección tendrá su configuración especificada en una
sección llamada "[formatter_form01]". La configuración del registrador
raíz debe especificarse en una sección llamada "[logger_root]".

Nota:

  La API "fileConfig()" es más antigua que la API "dictConfig()" y no
  proporciona funcionalidad para cubrir ciertos aspectos del registro.
  Por ejemplo, no puede configurar objetos "Filter", que permiten el
  filtrado de mensajes más allá de niveles enteros simples, usando
  "fileConfig()". Si necesita tener instancias de "Filter" en su
  configuración de registro, deberá usar "dictConfig()". Tenga en
  cuenta que las mejoras futuras a la funcionalidad de configuración
  se agregarán a "dictConfig()", por lo que vale la pena considerar la
  transición a esta API más nueva cuando sea conveniente hacerlo.

A continuación se dan ejemplos de estas secciones en el archivo.

   [loggers]
   keys=root,log02,log03,log04,log05,log06,log07

   [handlers]
   keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09

   [formatters]
   keys=form01,form02,form03,form04,form05,form06,form07,form08,form09

El registrador raíz debe especificar un nivel y una lista de
manejadores. A continuación se muestra un ejemplo de una sección de
registrador raíz.

   [logger_root]
   level=NOTSET
   handlers=hand01

La entrada de "nivel" puede ser una de "DEBUG, INFO, WARNING, ERROR,
CRITICAL" o "NOTSET". Solo para el registrador raíz, "NOTSET"
significa que todos los mensajes se registrarán. Los valores de nivel
son "eval()"uados en el contexto del espacio de nombres del paquete
"logging".

La entrada "manejadores" es una lista separada por comas de nombres de
manejadores, que debe aparecer en la sección "[manejadores]". Estos
nombres deben aparecer en la sección "[manejadores]" y tener las
secciones correspondientes en el archivo de configuración.

Para los registradores que no sean el registrador raíz, se requiere
información adicional. Esto se ilustra en el siguiente ejemplo.

   [logger_parser]
   level=DEBUG
   handlers=hand01
   propagate=1
   qualname=compiler.parser

Las entradas de "nivel" y "manejadores" se interpretan como para el
registrador raíz, excepto que si el nivel de un registrador no raíz se
especifica como "NOTSET", el sistema consulta a los registradores más
arriba en la jerarquía para determinar el nivel efectivo del
registrador. La entrada "propagar" se establece en 1 para indicar que
los mensajes deben propagarse a los manejadores que están más arriba
en la jerarquía del registrador, o 0 para indicar que los mensajes
**no** se propagan a los manejadores en la jerarquía superior. La
entrada "qualname" es el nombre jerárquico del canal del registrador,
es decir, el nombre utilizado por la aplicación para obtener el
registrador.

Las secciones que especifican la configuración del manejador se
ejemplifican a continuación.

   [handler_hand01]
   class=StreamHandler
   level=NOTSET
   formatter=form01
   args=(sys.stdout,)

La entrada "class" indica la clase del manejador (según lo determinado
por "eval()" en el espacio de nombres del paquete "logging"). El ``
nivel '' se interpreta como para los registradores, y "NOTSET" se
entiende como 'registrar todo'.

La entrada "formateador" indica el nombre clave del formateador para
este manejador. Si está en blanco, se utiliza un formateador
predeterminado ("logging._defaultFormatter"). Si se especifica un
nombre, debe aparecer en la sección "[formateadores]" y tener una
sección correspondiente en el archivo de configuración.

La entrada "args", cuando "eval()" ua en el contexto del espacio de
nombres del paquete "logging", es la lista de argumentos para el
constructor de la clase de manejador. Consulte los constructores de
los manejadores relevantes, o los ejemplos a continuación, para ver
cómo se construyen las entradas típicas. Si no se proporciona, el
valor predeterminado es "()".

La entrada opcional "kwargs", cuando "eval()"úa en el contexto del
espacio de nombres del paquete "logging", es el argumento de palabra
clave diccionario al constructor para la clase de manejador. Si no se
proporciona, el valor predeterminado es "{}".

   [handler_hand02]
   class=FileHandler
   level=DEBUG
   formatter=form02
   args=('python.log', 'w')

   [handler_hand03]
   class=handlers.SocketHandler
   level=INFO
   formatter=form03
   args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)

   [handler_hand04]
   class=handlers.DatagramHandler
   level=WARN
   formatter=form04
   args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)

   [handler_hand05]
   class=handlers.SysLogHandler
   level=ERROR
   formatter=form05
   args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)

   [handler_hand06]
   class=handlers.NTEventLogHandler
   level=CRITICAL
   formatter=form06
   args=('Python Application', '', 'Application')

   [handler_hand07]
   class=handlers.SMTPHandler
   level=WARN
   formatter=form07
   args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')
   kwargs={'timeout': 10.0}

   [handler_hand08]
   class=handlers.MemoryHandler
   level=NOTSET
   formatter=form08
   target=
   args=(10, ERROR)

   [handler_hand09]
   class=handlers.HTTPHandler
   level=NOTSET
   formatter=form09
   args=('localhost:9022', '/log', 'GET')
   kwargs={'secure': True}

Las secciones que especifican la configuración del formateador se
caracterizan por lo siguiente.

   [formatter_form01]
   format=F1 %(asctime)s %(levelname)s %(message)s
   datefmt=
   class=logging.Formatter

La entrada "format" es la cadena de formato general, y la entrada
"datefmt" es una cadena de formato fecha/hora "strftime()" compatible.
Si está vacío, el paquete sustituye algo que es casi equivalente a
especificar la cadena de formato de fecha "'%Y-%m-%d %H:%M:%S'". Este
formato también especifica milisegundos, que se agregan al resultado
del uso de la cadena de formato anterior, con un separador de coma. Un
ejemplo de tiempo en este formato es "2003-01-23 00:29:50,411".

La entrada "clase" es opcional. Indica el nombre de la clase del
formateador (como módulo de puntos y nombre de clase). Esta opción es
útil para crear instancias de una subclase "Formatter". Las subclases
de "Formatter" pueden presentar trazas de excepción en un formato
expandido o condensado.

Nota:

  Debido al uso de "eval()" como se describió anteriormente, existen
  riesgos potenciales de seguridad que resultan del uso de "listen()"
  para enviar y recibir configuraciones a través de sockets. Los
  riesgos se limitan a donde múltiples usuarios sin confianza mutua
  ejecutan código en la misma máquina; consulte la documentación de
  "listen()" para obtener más información.

Ver también:

  Módulo "logging"
     Referencia de API para el módulo de registro.

  Módulo "logging.handlers"
     Manejadores útiles incluidos con el módulo de registro.
