Thursday, 26 September 2024

Low-Code and No-Code Test Automation Tools

 


Introduction

We talk to humans in different languages. But talking to computers is numeric and specifically binary. But for humans its hard to talk in binaries for giving each instructions, so engineers built higher level languages wrapping the low level. Speaking of languages C is actually built as higher level programming level language. But then it still works with hardware and kernel and its deals with memory and bit level programming. To make things much more easy-to-use Java, Python, Perl, etc languages were written, of that some are wrappers of C. 

In today's world low-code and no-code software are present all around. Such initiatives become even more important as companies have to build applications to work across a wider range of devices, including smartphones.

Understanding the market and the implications of no-code and low-code software will be important. If you're developer, you'll need to shift focus to adapt to some significant changes. If you're running a business, new opportunities will open up that will allow you to move faster and be more innovative than ever before.

We will be talking about the technologies and techniques that are expected to accelerate test automation easily.

Low-code ( LC )

Low-code is a visual development approach to application development or configuration driven development. Low-code enables developers of different experience to create applications for backend, web and mobile, using drag-and-drop components and model driven logic through a graphic user interface. Also the low-code platforms enables the non-technical developers to write code and build applications. 

No-code ( NC )

No-code software, on the other hand, takes the low-code concept to another high level, allowing literally anyone to create, modify an application to their needs without any programming knowledge.

LC/NC platform

Components

Description

Continuous Integration

Established framework to merge model changes into a version control repository upon task completion. Committing changes triggers the automated build system to grab the latest version from the repository, create build, run all the automations test and validate if its suitable for release and finally deploy to the master branch (last step could be manual or automated once provided extra approvals).

Reusability


As LC/NC platforms are majorly generalized, hence reusability makes it easy for developers to ramp from one project to another with ease or create from templates. At times its only the configuration changes to be build several systems. The underlying system does all the work and making the users of LC/NC easy to develop applications.

Omni-channel


Rather than developing and support independent code and tooling for each incompatible target, developers are looking for ways to unify development activities and serve many targets from a single, modular code base. And only make cosmetic changes at high level. 

Rules


RPA (Robotic Process Automation) can be said as fastest growing LC/NC platform, by performing tasks which mimics what user does using rules and each developer framing their own decisions. Developers can automate their tasks without writing source code and allow to run whenever needed.

Performance


Built-in automated testing, proactive quality monitoring, and real-time performance management. But at times this can be the bottle neck for the developers if they fail to understand the underlying architecture.

Cloud-based


Usually NC/LC provides hosting services and take advantage of a public cloud that automatically manages application reliability and scalability with simple configuration, reducing cost and effort to maintain infrastructure. Or provide the solution it self in cloud, can be said as SAAS software as a service.



Seeing the different solutions provided by LC/NC platforms, lets see how this can help in enhancing the quality of the software.



Role of Low Code or No Code platforms in Agile testing

With the rapid increase in interest for adopting agile methodologies, devops taking the center stage and faster release cycles, there is an imperative need of evolving the software testing process to match up footprints.

Agile testing starts at the beginning of a software development phase and includes the progressing incorporation among testing and development. But usually, testing process was done after the coding stage; but in agile methodology, testing is persistent, putting testers between owners and developers. This makes a progressive process in improving the quality of code and making it resilient.

Through improved communication and collaboration, careful delivery and automation of processes results in :

  • Delivering high quality code in faster time

  • Reduced chances of failure in new releases

  • Improved time for fixes and better recovery

Fig1: CI CD Pipeline

Credits: https://www.synopsys.com/glossary/what-is-cicd.html


This raises the need of creation and execution of automated testing, using the efficient tools will save the time and bring in more quality.



Problem Statement in Continuous Testing

Labor Force

There are many firms which have bulk of manual software professionals and they face challenges to meet up the testing needs of agile development cycles due to not being sufficiently skilled to support hand writing code line by line. Learning new technologies might need a lot of trainings which will add to cost and time for firms along with compromising with quality of current build cycles.

Constant Testing

Due to agile development, the progress of the automation tests are needs to be revisited, it's continuous action that starts before the advancement stage and requires continuous efforts. This requires significant time and since testers are relied upon to begin creating tests before coding has even begun, or while coding is in development.

Repetitive Regression Cycles

Developers most of the time has be used to add new features to the item. This can cause relapses in past highlights. Testers use regression tests to distinguish this issue and conquer it, however, manual regression testing is illogical in a quick-paced agile methodology.

Another test is the cutting edge web applications carry on contrastingly when seen on various gadgets or programs. This makes very complicated for performing the compatibility testing situations, which should be tried to guarantee that the application capacities accurately for all clients.

Frequently Changing Requirements

Many times what happens is that upper management changes prerequisites or drops stories during a sprint cycle, despite the fact that this isn't empowered in an Agile/Scrum system and no scrum master can facilitate such a request. This means that work effectively half-done should be disposed of or altered, which changes the extent of testing out of the blue.



Automated testing

Automated testing is a Software testing technique to test and compare the actual outcome with the expected outcome. This can be achieved by creating test scripts or using any automation testing tool to create tests. Test automation can be used to automate the repetitive tasks and other testing tasks which would be difficult to perform manually.

This approach we can use to take a greater speed in automating, executing and having a rapid feedback of the tests in the application.



Figure 2: Types of Software Testing Pyramid



Low or No Code Automation Solutions

Low or No code will help us to overcome the current issues across different platforms, let's see how and which solutions:

Note: Provided high level information about the tools for awareness, please look into the documentation link for further details.

Component Testing

Product

Features

Supported Platforms

Ratings

Price

Documentation

Behave

BDD Testing

Python

2.5K Stars in Github

Open source

https://behave.readthedocs.io/en/stable/index.html

  • BDD abbreviated as Behaviour-driven development, is an agile software development technique. This brings is common language between developers, QA and business participants in the development of software. Includes user acceptance test or customer test driven development practices while building solution.

  • Behave test cases are created in a natural language style (given, when), backed up by Python code.

  • Its is technique, which has been incorporated in many tools, we can say this LC framework.

Hypothesis

Property based testing

Python

5.6K Stars in Github

Open source

https://hypothesis.readthedocs.io/en/latest/

  • Hypothesis is a testing library which enables in creating parametrized test cases by a source of examples. 

  • A Hypothesis generation examples of different values for the given tests and tries to make them fail.

  • This simplifies writing your tests and makes them more powerful at the same time, by letting software automate the boring bits and do them to a higher standard than a human would, freeing you to focus on the higher level test logic.



API Testing

Product

Features

Supported Platforms

Ratings

Price

Documentation

SoapUI

API Test Automation Framework for Soap & Rest

Windows, Linux, Mac OS

1.1K Stars Github

Open Source

https://www.soapui.org/getting-started/

  • SoapUI is the leading Functional Testing tool for SOAP and REST testing.

  • SoapUI allows you to create and execute automated functional, regression and load tests from its graphical interface very easily and rapidly.

  • SoapUI provides complete test coverage, in a single test environment, such as SOAP and REST – based Web services, JMS enterprise messaging layers, databases, Rich Internet Applications and much more.

Rest Assured

Testing REST API

Windows, Linux, Mac OS – Needs to be Java enabled

4.8K Stars Github

Open Source

https://rest-assured.io

  • Rest Assured makes testing of REST services in the Java domain easy. It is an open-source tool. XML and JSON Requests/Responses are supported by Rest-Assured.

  • It provides some baked-in functionalities. It supports the syntax of BDD Given/When/Then. To use the tool, it is not necessary to be a HTTP Request.

  • We can write tests for the application written in any languages such as Python, Ruby, Java, .Net, etc.

UI Testing

Product

Features

Supported Platforms

Ratings

Price

Documentation

Selenium

Web automation tools

Windows, Linux, Mac OS

18.1K Stars Github

Open source

https://selenium-python.readthedocs.io

  • Selenium IDE is a browser extension. Currently, both Chrome and Firefox are supported.

  • To create tests with little or prior knowledge in programming.

  • Easily create tests via record and playback test automation for the web.

  • It helps you to create very effective test scripts for regression testing, exploratory testing and quick bug reproduction.

Katalon

Web, API, mobile and desktop app test automation. 

Windows, Linux, Mac OS

152 Stars Github

Free license, with paid support services

https://docs.katalon.com/katalon-studio/docs/index.html

  • Katalon Studio is an all-in-one solution that supports web, API, mobile, and desktop app test automation.

  • Its is powerful in enabling cross functional operations for product development teams at scale.

  • As a codeless solution, Katalon Studio is easy-to-use, robust to expand, yet contains the necessary components for advanced needs with built-in keywords and project templates.

  • Recognized by Gartner Peer Insights Customer's Choice in 2020 and is trusted by over 65,000+ companies worldwide. 

Appium

Mobile automation testing tool

Windows, Linux, Mac OS

11.7K Stars Github

Open source

https://appium.io/docs/en/about-appium/intro/

  • Appium open source test automation framework is primarily envisioned for mobile apps.

  • Built on client/server architecture, Appium automates the applications that are created for iOS and Andriod.

  • It is a well liked module automation testing tool attributable to it easy installation and usage.

Performance Testing

Product

Features

Supported Platforms

Ratings

Price

Documentation

JMeter

Load and Performance Testing.

Windows, Linux, Mac OS

4.3K Stars Github

Open source

https://jmeter.apache.org/

usermanual/index.html

  • Apache JMeter is open source Java desktop app which is intended mainly for web application's load testing.

  • It also supports unit testing and limited functional testing.

  • It has lots of good features like dynamic reporting, portability, powerful Test IDE, etc and supports different types of applications, protocols, shell scripts, Java objects and databases.

Locust

Load Testing Tool

Windows, Linux, Mac OS

2.3K Stars Github

Open source

http://docs.locust.io/en/stable/

  • Locust is a scalable load testing framework written in Python.

  • Locust is an easy to use, scriptable and scalable performance testing tool.

  • We can call this low code, because all the functions are defined we can just use them.

  • Also as this can be expandable.



Security Testing

Security Testing is to ensure the web applications and information systems remains secure.

Product

Features

Supported Platforms

Ratings

Price

Documentation

ZAP

Security testing tool

Windows, Linux, Mac OS

7.4K Stars Github

Open source

https://www.zaproxy.org/docs/

  • Its a open source tool that is specifically designed to help the security professionals to find out the security vulnerabilities present in the web applications.

  • It can be used as a scanner/filter of a web page.

  • The security testing tool supports command line access for advanced users. In addition to being one of the most famous OWASP projects, it is awarded the flagship status. 

w3af



Allows testers to find over 200 types of security issues in web applications

Linux, Mac OS

3.2K Stars Github

Open source

http://docs.w3af.org/en/latest/

  • Web Application Attack and Audit Framework(w3af) is an open source web application security scanner.

  • The project provides a vulnerability scanner and exploitation tool for Web applications.

  • It provides information about security vulnerabilities for use in penetration testing engagements.

  • The scanner offers a graphical user interface and a command-line interface.


Future of SQA

The process of continuous change will remain in the future, we can expect the changes go live to be there tomorrow or possibly ever sooner.

Artificial Intelligence will change the software development industry completely. And it is expected AI will, in time, be a part of about every piece of software.

Below are 10 AI Driven Testing Tools, which makes testing more simple, without writing much of source code.

AI Driven Testing (AI-DT) Tools

Solutions

Features

Documentation

Applitools Eyes





  • Visual Testing with Automation.

  • Applications developed for different browsers, mobiles and tabs, it becomes difficult to make sure it provides same features in all.

  • Applitools Eyes is a powerful platform for automated visual testing for an application finding the differences in two different systems, avoid writing testcases for each scenario.

https://applitools.com/tutorials/

SauceLabs



  • Sauce Labs provides a testing platform, for web and mobile applications. 

  • Sauce Labs runs Selenium tests from a cloud-based server.

  • Sauce Labs has introduced low-code automated web testing which makes non developers creating quick testcases.

  • Having low or no programming languages, testers can write fully automate test suites, as Sauce Labs is AI powered.

https://docs.saucelabs.com

Testim



  • Testim is the AI-based functional test automation solution, by creating using with code or codeless, or both.

  • Record tests and export the code, this makes it very easy to use even by the non developers.

https://help.testim.io/docs/testim-overview

Sealights



  • SeaLights’ technology automatically identifies, analyzes, and communicates every perceivable Quality Risk across the entire delivery pipeline by continuously collecting telemetry data from all stages of the SDLC. 

  • Its machine learning-like technology examines both your code and the tests that run against it, letting you know exactly what your tests cover and what they don’t. 

  • Sealights does not just evaluate unit tests; also makes sure the other testing types are also be evaluated to make software clean, such as functional to manual to performance.

https://www.sealights.io

test.ai



  • Test.ai creates testcases for UI based applications.

  • Using AIML in identification of elements, also provides simple drag-and-drop codeless interface. 

https://www.test.ai

mabl



  • mabl is a low code and no code cross-browser testing.

  • UI and AIML based adding testcase, assertion and executions. Also provides headless solution.

https://help.mabl.com

retest



  • AI-based test generator can automatically automate your tests. 

  • retest is used to detect functional differences, and also visual differences.

https://docs.retest.de

ReportPortal




  • ReportPortal AI-powered Test Automation Dashboard acquire, aggregate and analyze test reports across all environments.

  • Provides visibility of the test execution results throughout the pipeline and fast feedback.


https://reportportal.io

AppvanceIQ (AIQ)



  • Appvance IQ is an AI driven autonomous test creation and continuous testing system.

  • Creates several test scripts and data driven tests faster using AI.

https://www.appvance.com/docs/UserGuide/

functionize




  • Functionize is a cloud-based automated testing tool for functional, performance, and load testing – a one-stop-shop for all of the above testing.

  • Functionize is the most powerful corporate AI-powered testing tool/platform in the industry.

  • It assists teams in breaking down testing barriers, allowing enterprises to release more quickly.

https://www.functionize.com/



Challenges with No Code or Low Code

As you could see the glorious features, but every solution comes with some hidden problems.

In NC/LC as we below are some potential issues:

  • Black box, hard to debug and get to lower level

  • Less control on creating scenarios

  • Integration with other tools (not all tools though, would be supported)

  • Customization would be hard

  • Vendor lock if using proprietary tools

  • Security risk


Conclusion

We should keep investing in test automation, it can only be expected test automation is something that will stay and it will keep improving. Using these kinds of tools in the pipeline enables the process towards Agile CICD with quality.

We should make the best use of No Code and Low Code tools, for faster automation and release. Along with exploring the AI-DT tools.

The software can change according to the needs of people using it and it might even become to test itself and fix possible bugs it detects in its own lines of code. However, we will still be needing engineers to act as a quality conscience.


Credits and References

https://www.techaheadcorp.com/blog/how-ci-cd-save-app-development-time/

https://www.mendix.com/low-code-guide/

https://dzone.com/articles/top-5-challenges-in-agile-testing

https://www.softwaretestinghelp.com/open-source-testing-tools/

https://qainfotech.com/top-ai-powered-test-automation-tools/

https://sapphireventures.com/wp-content/uploads/2021/01/low-code-no-code-image-scaled.jpeg [front page image credits]

Original Blog Posted in OSFY

https://www.opensourceforu.com/2022/04/an-introduction-to-low-code-and-no-code-test-automation-tools/

For further research and updates maintaining the blog here.

Thursday, 12 September 2024

Want to Write Quality Code? Try Logging in Python

 


Contents

Introduction

Logging Module

Configurations

Basic Configuration

File Configuration

Logging from Multiple Modules

Logging from Multiple Threads

Context Logging and Filter

Best Practices

Conclusion

References


Introduction

Logging is an important part of software development, which can help endless hours of debugging. If used it right we can use it for different flow analysis, how and what to scale, health checks, issue analysis, performance checks, security scan, audit and so on. 

Calling log calls from the program, indicates certain events has occurred during the timestamp, from where, may be IP address and other additional information. Event will hold the descriptive message, eg: dynamic or static values, importance of message (debug, info, warn, error). 

Python provides a logging system as a part of its standard library, so you can quickly add logging to your application.


Logging Module

The logging module readily available, adding logs to your module is as easy as below.

Code:

import logging

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

Output:

WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical message


As seen above by default, there are 5 levels of logging, which indicate the severity of events. Each levels have corresponding severity associated. Below is the levels defined based on the severity in increasing order:

Level

Used For

DEBUG

Detailed messages, usually for diagnosing problems

INFO

Confirmation or Facts of working as expected

WARNING

Alarming for future breaks, but until now the application is working

ERROR

Due to serious problem in the function

CRITICAL

Due to serious problem, the program failed to continue to execute

Table 1: Usage of each Log Levels

From the above you have noticed the debug and info messages are not logged. Because the default severity is set to WARNING. This will have messages printed of the level WARNING and above.

We will see below how to change the severity levels by changing the configurations. Also later how to change in format of the output.


Configurations

There are can be several configurations done. Such as, 

Parameter

Description

level

Root logger will be set to the specified severity level

filename

Name of the file logs should be stored

filemode

Mode in which the log file should be opened, default is “a” append mode

format

Pattern the log message to be written

datefmt

Format in which date should be written

Table 2: Commonly used log parameters


Basic Configuration

We can use the below method to configure the logging:

Code:

import logging
logging.basicConfig(level=logging.DEBUG, filename='/project/log/app.log', filemode='w', format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')

logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

Output: (from app.log)

27-Nov-21 07:28:09 - This is a debug message
27-Nov-21 07:28:09 - This is an info message
27-Nov-21 07:28:09 - This is a warning message
27-Nov-21 07:28:09 - This is an error message
27-Nov-21 07:28:09 - This is a critical message

Here we could see below points:

  • Debug and info message is logged, as the logging level is DEBUG and above

  • Log contents are not displayed in stdout, rather saved in the file called app.log

  • Message format is different from before, it works as per the format given by us in the format parameter

  • Same applies to the date format


File Configuration

Before we get to know about file configuration, lets look how logging works:

Logging Flow:

So far, we have seen the default logger named root, which is used by the logging module whenever its functions are called directly like this: logging.debug(). But we can or should define your own logger.

The logging module, follows the modular approach and has split the functionalities in below components:

  • Logger: 

    • This is exposed to application code to directly use.

    • Logging is performed by calling methods on instances of the Logger class.

    • Logger class performs three duties

      • Expose several methods to application code so that applications can log messages at runtime. A good convention is using the module name to name the loggers, as intuitively obvious where events are logged just from the logger name.

        • logger = logging.getLogger(__name__)

      • Logger objects determine which log messages to act upon based upon severity or filter objects.

        • Logger.setLevel()

        • Logger.addFilter()and Logger.removeFilter()

      • Logger objects pass along relevant log messages to all interested log handlers.

        • Logger.addFilter()and Logger.removeFilter()

  • LogRecord: 

    • Loggers automatically create LogRecord objects that have all the information related to the event being logged, like the name of the logger, the function, the line number, the message, and more.

  • Filter:

    • Provide a finer grained facility for determining which log records to output. Later we will see an example using Filters

  • Handler: 

    • Sends the LogRecords (created by loggers) to the appropriate destination.

    • For example it could be file, stdout, http, etc. 

    • For each destination Handler is have sub class: such as file: FileHandler, stdout: StreamHandler, http: HTTPHandler.

    • Logger objects can add zero or more handler objects to themselves with an addHandler() method.

  • Formatter:

    • Specify the format of LogRecord, by mentioning in the list attributes needs to be logged to be written in the final output.




Fig1: Logging Flow

Credits: https://docs.python.org/3/howto/logging.html

We can configure logging in three ways:

  • Creating loggers, handlers, and formatters explicitly using Python code that calls the configuration methods.

    • In the below example:

      • Created a logger with the same module name.

      • Create a console handler with the debug level set

      • Create formatter with time, module name, level and message

Code:

import logging

# create logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warning('warn message')
logger.error('error message')
logger.critical('critical message')

Output:

$ python handlerconfig.py
2021-11-28 14:45:45,096 - __main__ - DEBUG - debug message
2021-11-28 14:45:45,097 - __main__ - INFO - info message
2021-11-28 14:45:45,097 - __main__ - WARNING - warn message
2021-11-28 14:45:45,097 - __main__ - ERROR - error message
2021-11-28 14:45:45,097 - __main__ - CRITICAL - critical message
  • Creating a logging config file and reading it using the fileConfig() function.

    • In the below example:

      • Created a logger with the 'simpleExample' logger name.

      • Create a console handler with the debug level set, and file handler with info level set hence the debug log will not be printed

      • Both the configurations are taken from the logging.conf file

      • Its important to have the handler and logger set for 'root'

      • In file handler:

        • The propagate entry is set to 1 to indicate that messages must propagate to handlers higher up the logger hierarchy from this logger, or 0 to indicate that messages are not propagated to handlers up the hierarchy. 

        • The qualname entry is the hierarchical channel name of the logger, that is to say the name used by the application to get the logger.

      • FileRotateHandler

        • Here we have used file rotate handler, to avoid logging all the content in a single huge file

        • We have configured maxBytes=10, about 10bytes written the will be rotated

        • backupcount=5, it says how many rotated files needs to be kept rest will be cleaned.

      • Create formatter with time, module name, level and message 

Code:

fileconfig.py

import logging
import logging.config

logging.config.fileConfig('logging.conf')

# create logger
logger1 = logging.getLogger('simpleExample')
logger = logging.getLogger()

# 'application' code
logger.debug('debug message')
logger1.info('info message')
logger1.warning('warn message')
logger1.error('error message')
logger1.critical('critical message')


loggin.conf

[loggers]
keys=root,simpleExample

[handlers]
keys=consoleHandler,simpleFileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_simpleExample]
level=INFO
handlers=simpleFileHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
formatter=simpleFormatter
args=(sys.stdout,)

[handler_simpleFileHandler]
class=handlers.RotatingFileHandler
formatter=simpleFormatter
maxBytes=10
args=('test.log','a',10,5)

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s

Output:

$ python fileconfig.py
2021-11-28 17:47:26,858 - root - DEBUG - debug message
$ tail test.log*

==> test.log <==

2021-11-28 18:48:38,520 - simpleExample - CRITICAL - critical message


==> test.log.1 <==

2021-11-28 18:48:38,519 - simpleExample - ERROR - error message


==> test.log.2 <==

2021-11-28 18:48:38,518 - simpleExample - WARNING - warn message


==> test.log.3 <==

2021-11-28 18:48:38,517 - simpleExample - INFO - info message


  • Creating a dictionary of configuration information and passing it to the dictConfig() function.

    • This is the simple example by using yaml format as configuration

    • The dictConfig expects in input to be in the dict object, so we can have dict defined and call the logger method.

    • Below example creates a logger with the same module name.

    • Create a console handler with the debug level set

    • Create formatter with time, module name, level and message

Code:

dict_filelog.py

import logging

import logging.config

import yaml


with open('dict_config.yaml', 'r') as f:

config = yaml.safe_load(f.read())

logging.config.dictConfig(config)


logger = logging.getLogger(__name__)


logger.debug('This is a debug message')

logger.info('This is a info message')

logger.error('This is a error message')


dict_config.yaml

version: 1

formatters:

simple:

format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'

handlers:

console:

class: logging.StreamHandler

level: INFO

formatter: simple

stream: ext://sys.stdout

loggers:

sampleLogger:

level: DEBUG

handlers: [console]

propagate: no

root:

level: DEBUG

handlers: [console]

Output:

2021-11-28 19:25:00,508 - __main__ - DEBUG - This is a debug message

2021-11-28 19:25:00,508 - __main__ - INFO - This is a info message

2021-11-28 19:25:00,509 - __main__ - ERROR - This is a error message

Note:

  • When no configuration is provided, the events are output using a handler of last resport, which is stored in logging.lastResort.

  • This acts as StreamHandler, which writes the messages to sys.stderr.

  • Handler's level is set to WARNING 

  • The behaviour of the logging package in these circumstances is dependent on the Python version. We are seeing here for python 3.2 and above.


Logging from Multiple Modules

  • Calling multiple times same getLogger(“NAME”) will return a reference to the same object.

  • Can be used across modules to get the same object but it requires to be same Python interpreter process. 

  • Also the application code can define and configure a parent logger in one module and create (but not configure) a child logger in a separate module, and all logger calls to the child will pass up to the parent.

  • The parent and child mapping is done by the name of the logger, please find the example below in the getLogger method

    • parent: getLogger('test_app')

    • child: getLogger('test_app.sub_app') # here all the configuration is inherited from the parent

Code:

File: main_app.py

import sys

import logging

import sub_app


# Create a custom logger with "test_app"

logger = logging.getLogger("test_app")

logger.setLevel(logging.DEBUG)


# Create handlers

# create console handler with debug log level

c_handler = logging.StreamHandler(sys.stdout)


# create file handler with error log level

f_handler = logging.FileHandler('file.log', 'w')

f_handler.setLevel(logging.WARNING)


# Create formatters and add it to handlers

c_format = logging.Formatter('%(name)s - %(levelname)s - %(message)s')

f_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

c_handler.setFormatter(c_format)

f_handler.setFormatter(f_format)


# Add handlers to the logger

logger.addHandler(c_handler)

logger.addHandler(f_handler)


print(c_handler)

print(logger)


logger.debug('This is a debug from main module')

logger.info('This is a info from main module')

logger.warning('This is a warning from main module')

logger.error('This is an error from main module')


logger.info("Before calling the sub_app_function")

sub_app.sub_app_function()

logger.info("After calling the sub_app_function")


File: sub_app.py

import logging


# create logger

module_logger = logging.getLogger('test_app.sub_app')


def sub_app_function():

module_logger.error("Debug message from sub_app")


Output:

% python multiple_modules_logger.py

<StreamHandler <stdout> (NOTSET)>

<Logger test_app (DEBUG)>

test_app - DEBUG - This is a debug from main module

test_app - INFO - This is a info from main module

test_app - WARNING - This is a warning from main module

test_app - ERROR - This is an error from main module

test_app - INFO - Before calling the sub_app_function

test_app.sub_app - ERROR - Debug message from sub_app

test_app - INFO - After calling the sub_app_function

% cat file.log

2021-11-28 23:26:04,332 - test_app - WARNING - This is a warning from main module

2021-11-28 23:26:04,332 - test_app - ERROR - This is an error from main module

2021-11-28 23:26:04,332 - test_app.sub_app - ERROR - Debug message from sub_app


Logging from Multiple Threads

  • Logging from multiple threads is as simple as normal process

  • To differentiate the logs generated from each thread, threadName can be used

  • Below example shows the logging from the main thread and another one more thread

Code:

import logging

import threading

import time


def worker(arg):

while not arg['stop']:

logging.debug('Hi from thread 2')

time.sleep(0.5)


def main():

logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(threadName)s - %(message)s')

info = {'stop': False}

thread = threading.Thread(target=worker, args=(info,))

thread.start()

while True:

try:

logging.debug('Hello from main thread')

time.sleep(1.0)

except KeyboardInterrupt:

info['stop'] = True

break

thread.join()


if __name__ == '__main__':

main()


Output:

% python threadlogger.py 

2021-11-28 22:38:54,207 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:54,207 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:54,712 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:55,212 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:55,214 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:55,716 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:56,217 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:56,220 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:56,725 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:57,221 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:57,229 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:57,734 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:58,223 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:58,236 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:58,738 - root - DEBUG - Thread-1 - Hi from thread 2

2021-11-28 22:38:59,226 - root - DEBUG - MainThread - Hello from main thread

2021-11-28 22:38:59,239 - root - DEBUG - Thread-1 - Hi from thread 


Context Logging and Filter

  • LoggerAdapter

    • Sometimes you want logging output to contain contextual information in addition to the parameters passed to the logging call.

    • For example, in a networked application, it may be desirable to log client-specific information in the log (e.g. remote client’s username, or IP address). 

    • An easy way in which you can pass contextual information to be output along with logging event information is to use the LoggerAdapter class. 

    • In the below example we are adding the host_ip, to all the log events.

  • Filter

    • Filter can be used for both at the logger level or by handler level.

    • Such as they can stop certain information to be passed to be logger

    • Also inject additional information into the LogRecord which will be logged

    • In our below example, we are using filter to allow only certain levels to be logged.

      • Though the log level is set to DEBUG, we are only allowing INFO and ERROR to be logged.

    • Note: These are cooked up examples, in real life the usecases varies.


Code:

import sys

import logging


host_ip = "localhost"


class StdoutFilter(logging.Filter):

def filter(self, record):

return record.levelno in (logging.INFO, logging.ERROR)


logger = logging.getLogger('sampleApp')

logger.setLevel(logging.DEBUG)


handler = logging.StreamHandler(sys.stdout)

handler.addFilter(StdoutFilter())

formatter = logging.Formatter('%(host_ip)s - %(asctime)s - %(name)s - %(levelname)s - %(message)s')

handler.setFormatter(formatter)

logger.addHandler(handler)


logger = logging.LoggerAdapter(logger, {'host_ip': host_ip})

print(logger)


logger.debug('This is a debug from main module')

logger.info('This is a info from main module')

logger.warning('This is a warning from main module')

logger.error('This is an error from main module')


Output:

<LoggerAdapter sampleApp (DEBUG)>

localhost - 2021-11-29 00:26:18,566 - sampleApp - INFO - This is a info from main module

localhost - 2021-11-29 00:26:18,567 - sampleApp - ERROR - This is an error from main module


Best Practices

  • Create loggers using getlogger function

  • Using appropriate log levels in different regions

  • Create module level loggers

  • Use rotating file handler

  • Include timestamp in logs, preferred to use standard format exists, and it’s called ISO-8601.

    • Eg:

import logging

logging.basicConfig(format='%(asctime)s %(message)s')

logging.info('Example of logging with ISO-8601 timestamp')

  • Avoid opening same log file multiple times

  • Do not pass logger as parameter in class or methods


Conclusion

As seen the logging module is very flexible, customisable and ease to use. There are much more features such as,

  • context based logging

  • customisable log records

  • logging to multiple destination and servers

  • multiple processes logging to same file log

  • speaking logging messages

And many more, readymade code is available in the python cookbook URL: https://docs.python.org/3/howto/logging-cookbook.html.

It would be great to start using if you haven’t been using logging in your applications. You will see the difference and increase the code quality by adding this. Happy Logging!



References

https://realpython.com/python-logging/

https://docs.python.org/3/howto/logging.html

https://docs.python.org/3/howto/logging-cookbook.html

https://www.thepythoncode.com/media/articles/logging-in-python.PNG [credits for the header image]


Original Blog Posted in OSFY

https://www.opensourceforu.com/2022/04/an-introduction-to-low-code-and-no-code-test-automation-tools/

For further research and updates maintaining the blog here.


Scarcity Brings Efficiency: Python RAM Optimization

  In today’s world, with the abundance of RAM available, we rarely think about optimizing our code. But sooner or later, we hit the limits a...