Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppressing Stan optimizer printing in Python #223

Open
StuartGordonReid opened this issue Jun 13, 2017 · 39 comments
Open

Suppressing Stan optimizer printing in Python #223

StuartGordonReid opened this issue Jun 13, 2017 · 39 comments

Comments

@StuartGordonReid
Copy link

Hi,

I recently started using Prophet in Python, and whilst it is working (quite well), it is printing out messages which appear to be generated by PySTAN and suppressing output from stdout, stderr, and stdin doesn't seem to stop it. Could you add an option to suppress output from PySTAN. I've attached screenshots of the output and the function I am trying to use to suppress the output which is not working:

output from prophet

suppress output

Kind regards,
Stuart Reid

@bletham
Copy link
Contributor

bletham commented Jun 14, 2017

I worked on this a bit for #170 and didn't make it very far. Help here is greatly appreciated :-) We may need an upstream change.

@randlet
Copy link
Contributor

randlet commented Jun 22, 2017

Here's a decorator that DOES suppress output:

# from https://stackoverflow.com/questions/11130156/suppress-stdout-stderr-print-from-python-functions
class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in
    Python, i.e. will suppress all print, even if the print originates in a
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = (os.dup(1), os.dup(2))

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close the null files
        os.close(self.null_fds[0])
        os.close(self.null_fds[1])

# used like
 with suppress_stdout_stderr():
     p = Propet(*kwargs).fit(training_data)

@jimanvlad
Copy link

@randlet Awesome. Has this been pulled?

@randlet
Copy link
Contributor

randlet commented Jun 23, 2017

Not yet, but I would be happy to work up a PR this week if @bletham is ok with using a context manager to (optionally) suppress output.

@randlet
Copy link
Contributor

randlet commented Jun 26, 2017

Actually, I don't know how the copyright/FB contributors agreement would work for this. I didn't write the code and AFAIK Stack Overflow snippets (prior to 2016) are licensed with CC Share Alike license. @bletham do you know if it's possible for you to accept PR's for code that has a CC-SA license?

@bletham
Copy link
Contributor

bletham commented Jun 27, 2017

@randlet thanks for posting this, this is really great and should be immediately useful to people.

I'm not sure about the license thing. I think I'm inclined to just leave this issue open so that people can find and use the decorator, and then wait a bit longer on the Stan team for an upstream fix. They're definitely aware of the issue (stan-dev/rstan#49) and say as of April that it will be suppressed soon, then it'll just be a matter of adding the verbose arg to prophet.

@randlet
Copy link
Contributor

randlet commented Jun 27, 2017

That approach makes good sense to me. Thanks!

@bletham bletham changed the title Where are outputs from stan being written to? Suppressing Stan optimizer printing in Python Jul 4, 2017
@randlet
Copy link
Contributor

randlet commented Sep 1, 2017

Just wanted to post an update to this. We are running Prophet in a long running process doing lots of forecasting and found we were running out of file descriptors pretty quickly causing issues on the server. I finally traced it back to the decorator posted above which leaks 2 file descriptors every time it is used.

I have modified it to the following with success:

class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in
    Python, i.e. will suppress all print, even if the print originates in a
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = [os.dup(1), os.dup(2)]

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close the null files
        for fd in self.null_fds + self.save_fds:
            os.close(fd)

@eeilon79
Copy link

Hi. I've got here from issue #219, yet this solution definitely does not work with Prophet.fit logger.INFO messages like "Disabling yearly seasonality..." and "Disabling daily seasonality...". I'm quite at a loss of how to get rid of them, actually. Nothing works.

@eeilon79
Copy link

Nevermind. Turns out that if you specify "daily_seasonality=False" etc. the messages disappear. Although why on earth an unspecified parameter should generate a console output, Facebook knows.

@bletham
Copy link
Contributor

bletham commented Oct 18, 2017

Those messages can be disabled by setting the logging level for fbprophet to something above INFO:

import logging
logging.getLogger('fbprophet').setLevel(logging.WARNING)

The reason for them is to avoid a scenario where a new user fits a time series with default values, seasonality is disabled e.g. due to too short a time series, but the user doesn't know this and is left wondering why the seasonality doesn't show up in the plot like in the documentation. With the info message they will know that it was on purpose and not a bug.

@eeilon79
Copy link

eeilon79 commented Nov 3, 2017

Hello Ben,
Thank you for your answer. Sorry about the earlier disgruntled post and about the delay in my response.
I've tried logging before, probably with the wrong syntax.
However, now it works.
Thanks.

This was referenced Dec 1, 2017
@smontanaro
Copy link

Any progress on this? I just started to experiment with Prophet in an existing application which spits out HTML to stdout. All this business about log joing probabilities and Hessians is wreaking havoc with that. I finally had to wrap this sort of business around my prophet calls:

stdout = os.dup(1)
stderr = os.dup(2)
null = os.open(os.devnull, os.O_RDWR)
os.dup2(null, 1)
os.dup2(null, 2)
try:
    ... prophet calls here ...
finally:
    os.dup2(stdout, 1)
    os.dup2(stderr, 2)

I will eventually turn that into a decorator. Still would be nice to have an official way to do that suppression, one which didn't require so many system calls... (I'm guessing Stan has no clean way to tell it to shut up?)

I'm using 0.2.1 (package from Conda forge). Maybe a later version has this problem solved?

@bletham
Copy link
Contributor

bletham commented Jun 11, 2018

The upstream issue in Stan is still open, there is nothing in the later version of prophet to fix this. I was hoping for an upstream fix to avoid having to mess around with redirecting stderr, but we may have to just do it for the next version. In the meantime a decorator like that is the way to go.

@drcrook1
Copy link

Is there a link to the docs on how to do this? I'm running thousands of simulations and this is killing me...

@eeilon79
Copy link

Hi drcrook1,

I'm not sure what did the job for me, but it was either logging or suppress_stdout_stderr above.
I had a class which had:
a. suppress_stdout_stderr as a sub class
b. a fit function that called for suppress_stdout_stderr:
The relevant parts of the code are:

import logging
from fbprophet import Prophet

logging.getLogger('fbprophet').setLevel(logging.WARNING)

model = Prophet()
with self.suppress_stdout_stderr():
    model.fit(fit_data)

Hopes it helps.

@eeilon79
Copy link

Errr.. There was a question by shaidams64 which I don't see now, but I would answer anyway:
suppress_stdout_stderr wasn't a sub class of a fit class. I had a parent class which managed the entire simulation which had suppress_stdout_stderr as a sub_class and also had a fit function that used that class in the way I've quoted above. The model.fit class which was used on the fit function was imported from another library.
The fit function and suppress_stdout_stderr class are both children of the same parent class.

@eromoe
Copy link

eromoe commented Apr 4, 2019

I found that :
1 . suppress_stdout_stderr works if you run it in main process .
2. Didn't work when I used joblib or multiprocessing , it would still print the log, which seem slow down training .

@wuxiyu
Copy link

wuxiyu commented Apr 27, 2019

@eromoe hi, it fixes using this. This will help you silence logger

@mattc-eostar
Copy link

import logging
from fbprophet import Prophet

logging.getLogger('fbprophet').setLevel(logging.WARNING)

model = Prophet()
with self.suppress_stdout_stderr():
    model.fit(fit_data)

@eromoe, I have this successfully blocking the output across multiple processes.

@ShreyasJothish
Copy link

Using the wrapper code, I was able to suppress INFO logs but is there a way to stop WARNING logs too?

I tried setting the log level to ERROR but still WARNING logs appear.

logging.getLogger('fbprophet').setLevel(logging.ERROR)

@sammo
Copy link

sammo commented Oct 24, 2019

@ShreyasJothish this should do it.

# Turn off pystan warnings
warnings.simplefilter("ignore", DeprecationWarning)
warnings.simplefilter("ignore", FutureWarning)

# Turn off fbprophet stdout logger
logging.getLogger('fbprophet').setLevel(logging.ERROR)

@RonanFelipe
Copy link

I have tried the solution from @bletham and the one from @sammo but nothing, still having those Iteration messages.

@mattc-eostar
Copy link

@RonanFelipe Use the suppression class given above from @randlet #223 (comment)

Then use:

import logging
from fbprophet import Prophet

logging.getLogger('fbprophet').setLevel(logging.WARNING)

model = Prophet()
with self.suppress_stdout_stderr():
    model.fit(fit_data)

@RonanFelipe
Copy link

Thanks @mattc-eostar, it worked.

@CrashLaker
Copy link

Running the code proposed by @randlet solved it for me thank you so much.

However running this on AWS lambda sometimes gives me this error multiple times:
OSError: [Errno 24] Too many open files: '/dev/null'

Can anyone help?
python3.7
pystan==2.18
fbprophet==0.5

@randlet
Copy link
Contributor

randlet commented Nov 6, 2020

@CrashLaker did you see my revised code which addresses running out of file descriptors here: #223 (comment)

@decloedt
Copy link

Is there a workaround to suppress stan output in R?

@sahilee26
Copy link

@ShreyasJothish this should do it.

# Turn off pystan warnings
warnings.simplefilter("ignore", DeprecationWarning)
warnings.simplefilter("ignore", FutureWarning)

# Turn off fbprophet stdout logger
logging.getLogger('fbprophet').setLevel(logging.ERROR)

thanks it worked

@chinmaydas96
Copy link

chinmaydas96 commented Jan 21, 2022

It didn't work for me while I am using the cross_validation while model fitting using dask, Does anyone know a workaround for that thing?

I was using this like this.

with self.suppress_stdout_stderr(): df_cv = cross_validation(m, horizon='7 days', period='7 days', initial='500 days', parallel='dask')

@mraxilus
Copy link

Any progress on getting a module level option?

@Ne-oL
Copy link

Ne-oL commented May 27, 2022

import logging
from fbprophet import Prophet

logging.getLogger('fbprophet').setLevel(logging.WARNING)

model = Prophet()
with self.suppress_stdout_stderr():
    model.fit(fit_data)

@eromoe, I have this successfully blocking the output across multiple processes.

just wanted to point out that Facebook has since changed the naming of the module, so the above code isn't working properly anymore, i have taken liberty to modify it to my own use (I'm calling prophet inside a function on multiple cores using multiprocessing module), below is my code:

import warnings
import logging
import os
from prophet import Prophet

def func():
    warnings.simplefilter("ignore", DeprecationWarning)
    warnings.simplefilter("ignore", FutureWarning)
    logging.getLogger('prophet').setLevel(logging.ERROR) #Notice that i had modified the name from 'fbprophet' to just 'prophet'
    .......
    model = Prophet()
    with suppress_stdout_stderr():
                model.fit(fit_data)

i hope it helps anyone who wonders here for the same issue.

@maffei2443
Copy link

import logging
from fbprophet import Prophet

logging.getLogger('fbprophet').setLevel(logging.WARNING)

model = Prophet()
with self.suppress_stdout_stderr():
    model.fit(fit_data)

@eromoe, I have this successfully blocking the output across multiple processes.

just wanted to point out that Facebook has since changed the naming of the module, so the above code isn't working properly anymore, i have taken liberty to modify it to my own use (I'm calling prophet inside a function on multiple cores using multiprocessing module), below is my code:

import warnings
import logging
import os
from prophet import Prophet

def func():
    warnings.simplefilter("ignore", DeprecationWarning)
    warnings.simplefilter("ignore", FutureWarning)
    logging.getLogger('prophet').setLevel(logging.ERROR) #Notice that i had modified the name from 'fbprophet' to just 'prophet'
    .......
    model = Prophet()
    with suppress_stdout_stderr():
                model.fit(fit_data)

i hope it helps anyone who wonders here for the same issue.
Didn't work for me, unfortunately. I'm using prophet on IBM Cloud.

@seanslma
Copy link

seanslma commented Nov 10, 2022

FYI, the previous class suppress_stdout_stderr will not work appropriately in multi-thread tasks

  • the output from some threads out of the with block might be suppressed
  • the output from some other threads within the with block might still be printed out

@mn047
Copy link

mn047 commented Dec 16, 2022

Hi, I have class suppress_stdout_stderr and worked for me, but I would like to stop it.
anyone can help with this

`class suppress_stdout_stderr(object):
def init(self):
# Open a pair of null files
self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
# Save the actual stdout (1) and stderr (2) file descriptors.
self.save_fds = (os.dup(1), os.dup(2))

def __enter__(self):
    # Assign the null pointers to stdout and stderr.
    os.dup2(self.null_fds[0], 1)
    os.dup2(self.null_fds[1], 2)

def __exit__(self, *_):
    # Re-assign the real stdout/stderr back to (1) and (2)
    os.dup2(self.save_fds[0], 1)
    os.dup2(self.save_fds[1], 2)
    # Close the null files
    os.close(self.null_fds[0])
    os.close(self.null_fds[1])`

@samposm
Copy link

samposm commented Mar 4, 2023

class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in
    Python, i.e. will suppress all print, even if the print originates in a
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = [os.dup(1), os.dup(2)]

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close the null files
        for fd in self.null_fds + self.save_fds:
            os.close(fd)

I don't think you need to open /dev/null twice. And for this kind of simple context manager use, you don't necessarily need an __init__ separate from __enter__. Here is a modified version:

from types import TracebackType
from typing import Union

class SilentStdoutStderr(object):
    """
    Context manager to temporarily silence stdout and stderr. Use with `with`.
    """
    stdout, stderr = sys.__stdout__.fileno(), sys.__stderr__.fileno()

    def __enter__(self) -> None:
        self.devnull = os.open(os.devnull, os.O_RDWR)
        self.orig_stdout, self.orig_stderr = os.dup(self.stdout), os.dup(self.stderr)
        # point stdout, stderr to /dev/null
        os.dup2(self.devnull, self.stdout)
        os.dup2(self.devnull, self.stderr)

    def __exit__(
        self,
        exc_type: Union[type[BaseException], None],
        exc_val: Union[BaseException, None],
        exc_tb: Union[TracebackType, None],
    ) -> None:
        print(flush=True)
        # restore stdout, stderr back
        os.dup2(self.orig_stdout, self.stdout)
        os.dup2(self.orig_stderr, self.stderr)
        # close all file descriptors
        for file in [self.devnull, self.orig_stdout, self.orig_stderr]:
            os.close(file)

But this is not important, both work anyway.

However, I managed to silence logging with just:

import logging

logging.getLogger("prophet").setLevel(logging.WARNING)
logging.getLogger("cmdstanpy").disabled=True

@mn047
Copy link

mn047 commented Mar 7, 2023

I want to revert it back to see log, how can I do that?

@piccolomo
Copy link

piccolomo commented Mar 13, 2023

Thank you @samposm what you quoted worked for me as well:

import logging

logging.getLogger("prophet").setLevel(logging.WARNING)
logging.getLogger("cmdstanpy").disabled=True

facebook-github-bot pushed a commit to facebookresearch/Kats that referenced this issue Nov 15, 2023
Summary:
this is local fixing of prophet issue facebook/prophet#223 (comment)

as we are struggling of unneceassary stan logs.

I've added parameter to turn these stan logs off with default as True, with this logs debugablity is struggling

Differential Revision: D51322580

fbshipit-source-id: 5039b91b3fa4baa4a6aa5b7bba17bcc71032fa18
@SinghJivjot
Copy link

class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in
    Python, i.e. will suppress all print, even if the print originates in a
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = [os.dup(1), os.dup(2)]

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close the null files
        for fd in self.null_fds + self.save_fds:
            os.close(fd)

I don't think you need to open /dev/null twice. And for this kind of simple context manager use, you don't necessarily need an __init__ separate from __enter__. Here is a modified version:

from types import TracebackType
from typing import Union

class SilentStdoutStderr(object):
    """
    Context manager to temporarily silence stdout and stderr. Use with `with`.
    """
    stdout, stderr = sys.__stdout__.fileno(), sys.__stderr__.fileno()

    def __enter__(self) -> None:
        self.devnull = os.open(os.devnull, os.O_RDWR)
        self.orig_stdout, self.orig_stderr = os.dup(self.stdout), os.dup(self.stderr)
        # point stdout, stderr to /dev/null
        os.dup2(self.devnull, self.stdout)
        os.dup2(self.devnull, self.stderr)

    def __exit__(
        self,
        exc_type: Union[type[BaseException], None],
        exc_val: Union[BaseException, None],
        exc_tb: Union[TracebackType, None],
    ) -> None:
        print(flush=True)
        # restore stdout, stderr back
        os.dup2(self.orig_stdout, self.stdout)
        os.dup2(self.orig_stderr, self.stderr)
        # close all file descriptors
        for file in [self.devnull, self.orig_stdout, self.orig_stderr]:
            os.close(file)

But this is not important, both work anyway.

However, I managed to silence logging with just:

import logging

logging.getLogger("prophet").setLevel(logging.WARNING)
logging.getLogger("cmdstanpy").disabled=True

Thanks sir, saved a lot of time and headache looking at the cmdstanpy logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests