Zope 5 Logging To Error Log

I have a python script that returns a json response. If there is an issue where Zope would normally log to the SiteErrorLog (i.e. error_log) it no longer does so because in order for the json to return a response of failed, I needed to write the script with a try/except block. Now the json returns properly when there is an error, however I still want to be able to have the error_log log the reason for the error as it normally would do.

I have searched everywhere I could think of and while I understand the need to use SiteErrorLog (which is installed) and use
from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
in my script, I can't seem to get zope to log the error.

If I write the try except block like this

try:
    #my code
    def returnjson():
        value = {"output": "success"}
        return json.dumps(value)
    return returnjson()

except:
    def returnjson():
        value = {"output": "failed"}
        return json.dumps(value)
    return returnjson()

the scripts returns the proper json response when there is an error, however, nothing get logged to the error_log.

If I use raise the normal error_log logging occurs but of course the script halts execution and nothing else gets run, even when using a finally block. So then I get the error_log but I don't get the returned json. I need both.

Anyone know what I need to write in my python script to do this?

kittonian via Plone Community wrote at 2024-1-31 19:34 +0000:

I have a python script that returns a json response. If there is an issue where Zope would normally log to the SiteErrorLog (i.e. error_log) it no longer does so because in order for the json to return a response of failed, I needed to write the script with a try/except block. Now the json returns properly when there is an error, however I still want to be able to have the error_log log the reason for the error as it normally would do.

In general, this is very dangerous: if no exception reaches the framework
(above your application), then the transaction is committed and all
modifications are made persistent. However, the exception might have
prevented some modifications; in this case, you would get
an inconsistent persistent state -- very difficult to analyse.

Instead of letting your script return successfully after
an exception, you should wrap the exception in another easily recognizable
exception, raise the wrapping exception and register an "error view" for
its class.
The "error view" would decide about the response; it may generate a JSON
response.
Because an exception reaches the framework, the transaction is aborted
(no modification of the persistent state) and normally logged.

Appreciated but it's not dangerous. This is a python script and has nothing to do with any views. It performs some actions and if there is an error performing those actions it throws an exception (hence the except block).

I am always returning a json response to my iOS app and handling it without issue. I just need want zope to also log to the error_log as normal. That's all I'm asking.

kittonian via Plone Community wrote at 2024-1-31 21:45 +0000:

Appreciated but it's not dangerous. This is a python script and has nothing to do with any views.

The important thing is not whether it is a script or a view but
whether it changes persistent state or not.
When it changes persistent state -- and you can not guarantee that
the state remains consistent despite the exception, it is dangerous.

Apart from that, following my advice would give you the normal
exception logging.

The error_log integrates with the exception processing via
a ZPublisher.interfaces.IPubFailure event handler.
Such an event is notified when a request fails (more precisely
when an exception arrives at the framework).
You might look at the SiteErrorLog implementation to find out
how this handler is implemented and use the SiteErrorLog API yourself to
register your own failures.

If you do not require error_log integration but only want
a log record in your log file, you could instantiate a logger
(from logging import getLogger; logger = getLogger(__name__))
and then use logger.exception(....) in your except block
to get the exception logged to your log file.

I have been looking through SiteErrorLog and you use SiteErrorLog.raising(self, info) where info is apparently a tuple but it is requiring me to pass self and nothing I've tried works.

Also, trying your example of

from logging import getLogger
logger = getLogger(__name__)
logger.exception(e)

does not work for a few reasons:

  1. you cannot use underscores in a zope python script
  2. restrictedPython doesn't allow the use of exception

I am not trying to log to a file. I am just trying to get the same error log result as would normally occur.

Also, I tried using raising in an external method but I can't get that to work either. I am using an external method because I could not get raising authorized within the ZMI no matter what I tried. For example, in my __init__.py I have

allow_module("Products.SiteErrorLog.SiteErrorLog")
from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog
allow_class(SiteErrorLog)

but it refuses to allow me to use raising within the python script in the ZMI.

My external method looks like this:

from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog

def logError(error):

       errorType = type(error).__name__
       errorStr = str(error)
       error_info = (errorType, errorStr, None)
       SiteErrorLog.raising(None, error_info)

Can you provide an actual example of the code that will properly log the exception as zope normally does if I did not have an except block? That's what I am trying to accomplish.

Hi!

As @dieter said, you can use python logging:

from logging import getLogger

logger = getLogger("TESTING LOGS")

logger.info("This is a info log entry")
logger.warning("This is a warning")
logger.error("This is an error")
logger.critical("This is a critical error")
2024-02-01 08:36:37,263 INFO    [TESTING LOGS:5][waitress-2] This is a info log entry
2024-02-01 08:36:37,271 WARNING [TESTING LOGS:6][waitress-2] This is a warning
2024-02-01 08:36:37,273 ERROR   [TESTING LOGS:7][waitress-2] This is an error
2024-02-01 08:36:37,274 CRITICA [TESTING LOGS:8][waitress-2] This is a critical error

Please, note the name of the logger I've used. Don't use "__name__" because underscore is restricted in python scripts. Set your own name.

kittonian via Plone Community wrote at 2024-1-31 23:19 +0000:

...
Can you provide an actual example of the code that will properly log the exception as zope normally does if I did not have an except block? That's what I am trying to accomplish.

No! I always follow my advice and let the exception logging happen in the "normal" way.

You could try to debug the SiteErrorLog IPubFailure event handler
(putting a code breakpoint (import pdb; pdb.set_trace()) into its code --
do not forget to run Zope in a console window in forground mode)
to learn how the call is prepared.

I got it all working. Here's how to do it (it's actually quite simple).

  1. Create an external method called log_error like this:
import sys
from Products.SiteErrorLog.SiteErrorLog import SiteErrorLog

def logError(error_context):
        SiteErrorLog.raising(error_context, sys.exc_info())
  1. In your ZMI based python script, inside the except block write this (assuming log_error resides in the same directory as your python script):
    context.log_error(context.error_log)

That will log the error to the error_log just like it always does and still provide you a way to return whatever you wish from the python script.