Cannot print the terminal output in Streamlit?

Hello @Charly_Wargnier,
So instead of appending the text, you want it to be overriden.

You could try something like this:

# Before
def new_write(b):
    if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
        buffer.write(b)
        output_func(buffer.getvalue())
    else:
        old_write(b)

# After
def new_write(b):
    if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
        buffer.write(b)
        output_func(b)  # Changed here
    else:
        old_write(b)
1 Like

Thanks Synode! I’ll have a proper look asap. :slight_smile:

Did you get a solution?

Hey Filip!

If you could send some kind of notification in a few hours when i will be near the PC, I will provied you with a solution I’ve managed to create using some of the code here, which works great, not exactly a console, but you are able to see the output if it’s comming from ‘logging’, and you can see it on the screen :slight_smile:

Best regards.

1 Like

This is my problem. I’m utilizing a library that prints this things in my Console output.

I would like to print the same messages in Streamlit page.

Can you help me?

Hello @BugzTheBunny ! I’m interested if you could provide a solution for the output coming from “logging” :slight_smile:

This code is great, thank you @okld for your work and @Charly_Wargnier for asking the question :slight_smile:

Have a nice day!

Hi guys, ive made a demo of the samples of the code here, you may view it in this repo:

@Ejoz @filipespacheco

1 Like

Thank you so much @BugzTheBunny, it works like a charm! (And I’m really sorry I haven’t seen your message sooner).

1 Like

Not a problem, glad it works and can be helpfull!

the codes is work, but mix the output into one line, so i add “\r\n” in the new_write(), just FYI.

    def new_write(b):
        if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
            buffer.write(b+"\r\n")
            output_func(buffer.getvalue())
        else:
            old_write(b)
1 Like

Is there a way to limit the number of lines that are printed?

I have an app that has a long running process with many steps. Each step prints to the console as part of its progress. I only want to display the last 10 lines of stderr. Is this possible?

Thanks in advance

If you’re using the (excellent) loguru as logging manager, I manage to redirect console logs to streamlit apps by adding a related sink to a logger.

For example, if you want st.warning to catch logger.warning (and nothing else) and st.error to catch logger.error (and nothing else), the following snippet should work

from loguru import logger
import streamlit as st

def redirect_loguru_to_streamlit():
    def _filter_warning(record):
        return record["level"].no == logger.level("WARNING").no    
    if 'warning_logger' not in st.session_state:
        st.session_state['warning_logger'] = logger.add(st.warning, filter=_filter_warning, level='INFO')
    if 'error_logger' not in st.session_state:
        st.session_state['error_logger'] = logger.add(st.error, level='ERROR')

redirect_loguru_to_streamlit()

def main():
    logger.info('This should not be printed in app.')
    logger.warning('This should be printed as `st.warning`.')
    logger.error('This should be printed as `st.error`.')
    if st.button('Rerun'):
        st.write('You should not see duplicated logs.')

if __name__ == '__main__':
    main()
2 Likes

Doesn’t seem to work. Instead of overwriting, just shows an empty placeholder. Anybody managed to succeed with it?

def new_write(b):
    if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
    buffer.write(b)  
    output_func(b) # Changed here

Edit

Solution found:

def new_write(b):
            if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
                buffer.write(b)
                sleep(1)
                buffer.seek(0) # returns pointer to 0 position
                output_func(b)
1 Like

This workaround broke in recent versions of streamlit, as REPORT_CONTEXT_ATTR_NAME was removed. Anyone have a new approach?

2 Likes

EDIT: the counter example works on streamlit 1.7 if you swap out the import and context constant:

# from streamlit.report_thread import REPORT_CONTEXT_ATTR_NAME
from streamlit.script_run_context import SCRIPT_RUN_CONTEXT_ATTR_NAME
...
# if getattr(current_thread(), REPORT_CONTEXT_ATTR_NAME, None):
if getattr(current_thread(), SCRIPT_RUN_CONTEXT_ATTR_NAME, None):

Got something working I think similar using contextlib. I’ll check later if there was extra logs gotten from REPORT_CONTEXT_ATTR_NAME it misses.

Examples:

  • print
  • df.info()
  • output of success and error subprocess
  • traceback of python error

cloud (py39)

github

Code:

from contextlib import redirect_stdout, redirect_stderr
import io
import sys
import subprocess
import traceback
import streamlit as st
import pandas as pd
st.set_page_config(layout='wide')

st.header("Left: Body, Middle: Std Out, Right: Std Err")
body, stdout, stderr = st.columns(3)

with redirect_stdout(io.StringIO()) as stdout_f, redirect_stderr(io.StringIO()) as stderr_f:
    try:
        print('Hello World!')
        df = pd.DataFrame({"test": [1,2,3]})
        df.info()
        good_process = subprocess.run(["ls", "-lah", "."], capture_output=True, text=True)
        stdout_f.write(good_process.stdout)
        stderr_f.write(good_process.stderr)
        bad_process = subprocess.run(["ls", "wtf"], capture_output=True, text=True)  # Throws stderr error
        stdout_f.write(bad_process.stdout)
        stdout_f.write(bad_process.stderr) # Also print in in middle column (most of my usecases i like stdout and stderr)
        stderr_f.write(bad_process.stderr)
        x = 1 / 0  # Throws Python Error
    except Exception as e:
        traceback.print_exc()
        traceback.print_exc(file=sys.stdout) # or sys.stdout
button = body.button('wtf')
if button:
    # Outisde of context, doesn't display in streamlit
    print('BUTTON')
body.write(df)
stdout_text = stdout_f.getvalue()
stdout.text(stdout_text)
stderr_text = stderr_f.getvalue()
stderr.text(stderr_text)

Cheers! :beers:

3 Likes

Is there a way to redirect output image as well?

For streamlit 1.16.0, it works wtih:

from streamlit.runtime.scriptrunner.script_run_context import SCRIPT_RUN_CONTEXT_ATTR_NAME
...
if getattr(current_thread(), SCRIPT_RUN_CONTEXT_ATTR_NAME, None):

When I use the code below, the current output is printed as intended:

@contextmanager
def st_redirect(src, dst):
    placeholder = st.empty()
    output_func = getattr(placeholder, dst)

    with StringIO() as buffer:
        old_write = src.write

        def new_write(b):
            if getattr(current_thread(), SCRIPT_RUN_CONTEXT_ATTR_NAME, None):
                buffer.write(b)
                output_func(b)
            else:
                old_write(b)

        try:
            src.write = new_write
            yield
        finally:
            src.write = old_write

@contextmanager
def st_stdout(dst):
    "this will show the prints"
    with st_redirect(sys.stdout, dst):
        yield

@contextmanager
def st_stderr(dst):
    "This will show the logging"
    with st_redirect(sys.stderr, dst):
        yield

However, when a user changes the input widget(s) value(s) whilst the terminal output is printed the app will crash.

Is there a way to check if the user has changed the intial value, and if so, change the redirect to the correct one (i believe is done in getarr?)

Hello, can you help, I am a begginer with streamlit, how to use this pass in my code. I am running a job, and have an output to terminal and to info.log. How can I print it to my streamlit page?

1 Like

I had to say: disabled the “print to stdout” is really stupid default setting. As a glue tools , the streamlit had to corporate with other lib to complete real jobs. no stdout, we lost all info from these core lib.