Удаленка для jenkins+selenide+selenoid+allure+docker спецов на 2-3 часа в день. 100% remote! Присоединиться к проекту

[Заметка] Как уменшить уровень логирования для SudsLibrary библиотеки с помощью Python в Robot Framework?


(Mykhailo Poliarush) #1

От автора: этой заметкой я хотел бы начать новую волну небольших заметок и статей, которые я искренне надеюсь, подхватят и другие автоматизаторы. Ведь каждый день мы сталкиваемся с некоторыми проблемами и как то их решаем. Так давайте не только концентрироваться на поиске решения своей проблемы, а и делиться успехами и наработанным опытом. Это не должна быть статья на 3000 слов, а всего лишь 2-5 абзацев текста, а также какое-то количество вставленного кода. Просто создаем пост с нужным контентом в нужном разделе. Не так и сложно, верно? Или нет?

Кто хоть раз создавал свою библиотеку для RobotFramework знают, что это сделать вполне не сложно, а я бы сказал, что очень даже просто. Схема подключения библиотеки выглядит следующим образом:

  1. Выбираем библиотеку или код, на котором будет основываться наша библиотека
  2. Пишем код на python или java, где каждый метод класса это будет keyword в тесте
  3. Дальше подключаем написанный класс через импорт модуля в тесте.

Вот и все, ничего сложного.

Проблема

Так в чем собственно проблема? Есть такая библиотека SudsLibrary в RobotFramework, которая основывается на одноименной python библиотеке suds для работы с веб-сервисами. Библиотека работает как надо. Но когда запускаешь средней сложности тест с 30-50 вызовами вебсервисов на уровне логирования DEBUG (а в RobotFramework есть TRACE, DEBUG, INFO, WARN), то сама библиотека suds закидывает бесполезными логами, что логи RobotFramework-a могут вырасти до нескольких десятков мегабайт. C чем я не хочу мириться, но как же это побороть. Вроде бы нужно просто уменьшить уровень логирования в библиотеке и все должно быть нормально, но …

Как выглядит абсолютно бесполезная информация в спам логах?

20131004 20:20:30.781 : DEBUG : (u'CommonRequest', u'http://host.net/dataroamingbucket/wsdl/v1/'), found as: <Complex:0x2a65f90 name="CommonRequest" />
20131004 20:20:30.781 : DEBUG : Element:0x2adb3d0, convert type="xs:string" to (u'string', u'http://www.w3.org/2001/XMLSchema')
20131004 20:20:30.781 : DEBUG : Element:0x2adb550, convert type="xs:string" to (u'string', u'http://www.w3.org/2001/XMLSchema')
20131004 20:20:30.782 : DEBUG : Element:0x2adb490, convert type="xs:string" to (u'string', u'http://www.w3.org/2001/XMLSchema')
20131004 20:20:30.782 : DEBUG : Element:0x2adb2d0, convert type="xs:dateTime" to (u'dateTime', u'http://www.w3.org/2001/XMLSchema')
20131004 20:20:30.782 : DEBUG : Element:0x2adb270, convert type="xs:string" to (u'string', u'http://www.w3.org/2001/XMLSchema')
20131004 20:20:30.782 : DEBUG : Element:0x2adb890, convert type="xs:string" to (u'string', u'http://www.w3.org/2001/XMLSchema')

или

20131004 20:20:31.076 : DEBUG : (u'GetSmsCodeRequestType', u'http://host.net/dataroamingbucket/wsdl/v1/'), found as: <Complex:0x305c510 name="GetSmsCodeRequestType" />
20131004 20:20:31.076 : DEBUG : 
Element:0x305c2f0, resolving: (u'OrderBucketRequestType', u'http://host.net/dataroamingbucket/wsdl/v1/')
 using:(TypeQuery){
   id = "TypeQuery:0x4a3da30"
   ref[] = 
      "OrderBucketRequestType",
      "http://host.net/dataroamingbucket/wsdl/v1/",
   history[] = 
      <Element:0x305c2f0 name="request" type="(u'OrderBucketRequestType', u'http://host.net/dataroamingbucket/wsdl/v1/')" />,
   resolved = False
 }
20131004 20:20:31.076 : DEBUG : (u'OrderBucketRequestType', u'http://host.net/dataroamingbucket/wsdl/v1/'), found as: <Complex:0x305c3d0 name="OrderBucketRequestType" />

или

20131004 20:20:34.930 : DEBUG : TypeQuery:0x49dcb70, found builtin (string)
20131004 20:20:34.930 : DEBUG : 
push: (<suds.resolver.Frame instance at 0x049DC0F8>)
<suds.resolver.Frame instance at 0x04A87C10>
<suds.resolver.Frame instance at 0x049DC0F8>
20131004 20:20:34.930 : DEBUG : 
pop: (<suds.resolver.Frame instance at 0x049DC0F8>)
<suds.resolver.Frame instance at 0x04A87C10>
20131004 20:20:34.930 : DEBUG : 
pop: (<suds.resolver.Frame instance at 0x04A87C10>)

Как же все таки решить проблему?

К сожалению, решение данной проблемы не такое уж простое, как я бы хотел. Потому, что suds библиотека использует встроенный модуль логирования logging, а RobotFramework свой. Отключить логирование для конкретного модуля не представляется возможным, точнее все мои попытки это сделать не увенчались успехом.

Пробовал, вот так вот в коде

import logging
logging.basicConfig(level=logging.INFO)
my_logger = logging.getLogger('suds.client')
my_logger.setLevel(0)
my_logger.disabled = True
my_logger.propagate = False

Но увы ничего с этого не работает, работает только полное отключение какого-то уровня логирования.

logging.disable(logging.DEBUG)

А вот полностью, отключать мне не хочется, потому я делаю следующий хак.

Решение

Создаю свою библиотеку, которая будет расширять функциональность SudsLibrary и там создаю декоратор, который будет отключать уровень логирования перед вызовом нужного метода и включать его обратно после выполнения. Все что нужно будет мне сделать после этого, вызвать нужный декоратор на нужном методе.

Код выглядит так:

from SudsLibrary import SudsLibrary
from robot.api import logger
from functools import wraps, partial
import logging

logging.basicConfig(level=logging.INFO)


def disable_logging(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        logger.debug("Temporarily stop DEBUG logging to reduce size of log\n"
                     "Please enable thru SudsLibraryExtended if you needed")
        logging.disable(logging.DEBUG)
        results = function(*args, **kwargs)
        logging.disable(0)
        logger.debug("Start DEBUG logging back")
        return results
    wrapper.__wrapped__ = function
    return wrapper


class SudsLibraryExtended(SudsLibrary):
    ROBOT_LIBRARY_SCOPE = "GLOBAL"

    @disable_logging
    def create_soap_client(self, *argv, **kwargv):
        super(SudsLibraryExtended, self).create_soap_client(*argv, **kwargv)

    @disable_logging
    def _call(*argv, **kwargv):
        super(SudsLibraryExtended, self)._call(*argv, **kwargv):

Теперь конечно мне надо будет уже использовать мою библиотеку, а не оригинальную. Например, вот так вот:

*** Settings ***
Force Tags        smoke    webservices
Library           SudsLibraryExtended

*** Test Cases ***
CAPI Create Sale Order
    [Tags]    capi
    Create Soap Client    ${ENV['WSDL']}
    Set Http Authentication    ${ENV['USERNAME']}    ${ENV['PASSWORD']}
    Set Return Xml    True
    ${response}    Call Soap Method    createOrder    something
    Log    ${response}

Итог

Быстрое и простое решение возникшей проблемы с помощью logging и декораторов в python, которым я хотел с вами поделиться.

От автора: очень жду похожих заметок. Просто создаем пост в нужном разделе с нужным контентом. Можно даже просто показать какой-то кусок кода, который Вы считаете делает что-то полезное и описать его немного.


[Заметка] О Заметках и отделении обсуждений от базы знаний
[Заметка] Как считать емейл по imap c mail.ru в Python? Или что такое quoted printable encoding?
Дайджест полезных ссылок для тестировщиков-автоматизаторов #005
Обновление портала automated-testing.info
Code snippets или небольшие заметки автоматизаторов. Давайте накопим базу знаний!