How to avoid multiple print/log messages in console

Hello,

Iโ€™m running locally streamlit==1.30.0 on python==3.11.5.

My Home.py, for what concerns this topic, can be reduced to the following configuration:

import streamlit as st
from page_init import page_init

def page_script():
    st.write("This is Home.py")
    print("This is a test Home.py print message")

page_init(page_script)

With page_init function defined as:

import streamlit as st

def page_init(page_script):
    st.write("This is page_init.py")
    print("This is a test page_init.py print message")
    page_script()

If I run the app: streamlit run Home.py
What I can see in the browser is the result Iโ€™m expecting, that is the two messages:

This is page_init.py
This is Home.py

However, in the console I find multiple print statements:

  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501
  Network URL: http://10.66.87.163:8501

This is a test page_init.py print message
This is a test Home.py print message
This is a test page_init.py print message
This is a test Home.py print message
This is a test page_init.py print message
This is a test Home.py print message
This is a test page_init.py print message
This is a test Home.py print message
This is a test page_init.py print message
This is a test Home.py print message
This is a test page_init.py print message
This is a test Home.py print message

This is especially annoying using the logging module instead of print statements, because I get multiple log statements in stdout for the same log message. I canโ€™t figure out how to avoid this behaviour.

Any help will be very much appreciated.

Thank you!

Iโ€™m having the same issue with a slightly different config. My setup can be reduced to:

with open('src/utils/config.yml', 'r', encoding='utf-8') as file:
    config = yaml.load(file, Loader=SafeLoader)
    
st.logger = CustomLogger(name='APP', environment=config['application']['environment']).logger


authenticator = stauth.Authenticate(
    config['credentials'],
    config['cookie']['name'],
    config['cookie']['key'],
    config['cookie']['expiry_days'],
    config['preauthorized']
)


# Login Logic ========================================
name, auth_status, other = authenticator.login(location='main')
if st.session_state["authentication_status"] == False:
    st.logger.warning(f'Failed login attempt for {name}, {auth_status}, {other}')
    st.error('Username/password is incorrect')
    show_pages([Page('app.py', 'Home')])
elif st.session_state["authentication_status"] == None:
    st.logger.warning(f'Failed login attempt without credentials')
    st.warning('Please enter your username and password')
    show_pages([Page('app.py', 'Home')])
    
# Successful login
elif st.session_state["authentication_status"]:
    st.logger.info(f'Login successful for {name}')
    st.success(f'Welcome {name}, please wait for the app to load before proceeding.')
    show_pages(
        [
         ...
        ]
    )

the Custom logger is also used by modules imported into the app. Here it is:

class CustomLogger(logging.logger)
    def __init__(self, name, environment):
        """Return a logger instance with the given name."""
        self.logger = logging.getLogger(name)

        # TODO: Set the log level based on the environment parameter
        if environment == 'prod':
            log_level = logging.INFO
        else:
            log_level = logging.DEBUG
            
        self.logger.setLevel(log_level)

        # Create a console handler with a higher log level
        info_handler = logging.StreamHandler()
        info_handler.setLevel(log_level)

        # Create a file handler with a lower log level
        debug_handler = logging.FileHandler('debug.log')
        debug_handler.setLevel(log_level)

        # Create a formatter and add it to the handlers
        file_formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(name)s:%(message)s')
        stream_formatter = logging.Formatter('[%(levelname)s]\t%(name)s :: %(message)s')
        if name == 'APP': # Then we are getting a logger to use in the streamlit app, so we will use the custom formatter.
            # Defined below in the User behavior and timing section
            formatter = logging.Formatter('%(asctime)s:%(levelname)s:%(name)s:%(message)s')
            info_handler.setFormatter(stream_formatter)
            debug_handler.setFormatter(self.TimeToCodeFormatter())
        else: # Use the format below
            info_handler.setFormatter(stream_formatter)
            debug_handler.setFormatter(file_formatter)

        # Add the handlers to the logger
        self.logger.addHandler(info_handler)
        self.logger.addHandler(debug_handler)

On a successful login, I get tons of messages from the streamlit logger and the appropriate amount from modules imported into the app. Hereโ€™s an example:

[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  APP :: Login successful for Ethan Walker
[INFO]  DROPBOX :: Dropbox (prod) service connected!
[INFO]  DOCUMENT_INTELLIGENCE :: Document Intelligence (prod) service connected!
SQLAlchemy engine connected!

The amount of messages I get seems to increase the longer the app is running. Please help, my azure app service logs are becoming unruly!!