Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F86252565
wrappers.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Oct 5, 07:19
Size
18 KB
Mime Type
text/x-python
Expires
Mon, Oct 7, 07:19 (2 d)
Engine
blob
Format
Raw Data
Handle
21382453
Attached To
R3600 invenio-infoscience
wrappers.py
View Options
# -*- coding: utf-8 -*-
##
## This file is part of Invenio.
## Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 CERN.
##
## Invenio is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of the
## License, or (at your option) any later version.
##
## Invenio is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Invenio; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
""" Error handling library """
__revision__
=
"$Id$"
import
traceback
import
os
import
sys
import
time
import
datetime
import
re
import
inspect
from
cStringIO
import
StringIO
from
flask
import
current_app
from
invenio.base.globals
import
cfg
from
.models
import
HstEXCEPTION
## Regular expression to match possible password related variable that should
## be disclosed in frame analysis.
RE_PWD
=
re
.
compile
(
r"pwd|pass|p_pw"
,
re
.
I
)
def
get_pretty_wide_client_info
(
req
):
"""Return in a pretty way all the avilable information about the current
user/client"""
if
req
:
from
invenio.legacy.webuser
import
collect_user_info
user_info
=
collect_user_info
(
req
)
keys
=
user_info
.
keys
()
keys
.
sort
()
max_key
=
max
([
len
(
key
)
for
key
in
keys
])
ret
=
""
fmt
=
"
%%
%i
s:
%%
s
\n
"
%
max_key
for
key
in
keys
:
if
RE_PWD
.
search
(
key
):
continue
if
key
in
(
'uri'
,
'referer'
):
ret
+=
fmt
%
(
key
,
"<
%s
>"
%
user_info
[
key
])
else
:
ret
+=
fmt
%
(
key
,
user_info
[
key
])
if
ret
.
endswith
(
'
\n
'
):
return
ret
[:
-
1
]
else
:
return
ret
else
:
return
"No client information available"
def
get_tracestack
():
"""
If an exception has been caught, return the system tracestack or else
return tracestack of what is currently in the stack
"""
if
traceback
.
format_tb
(
sys
.
exc_info
()[
2
]):
delimiter
=
"
\n
"
tracestack_pretty
=
"Traceback:
\n
%s
"
%
\
delimiter
.
join
(
traceback
.
format_tb
(
sys
.
exc_info
()[
2
]))
else
:
## force traceback except for this call
tracestack
=
traceback
.
extract_stack
()[:
-
1
]
tracestack_pretty
=
"
%s
Forced traceback (most recent call last)"
%
\
(
' '
*
4
,
)
for
trace_tuple
in
tracestack
:
tracestack_pretty
+=
"""
File "%(file)s", line %(line)s, in %(function)s
%(text)s"""
%
{
'file'
:
trace_tuple
[
0
],
'line'
:
trace_tuple
[
1
],
'function'
:
trace_tuple
[
2
],
'text'
:
trace_tuple
[
3
]
is
not
None
and
\
str
(
trace_tuple
[
3
])
or
""
}
return
tracestack_pretty
def
register_emergency
(
msg
,
recipients
=
None
):
"""Launch an emergency. This means to send email messages to each
address in 'recipients'. By default recipients will be obtained via
get_emergency_recipients() which loads settings from
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES
"""
from
invenio.ext.email
import
send_email
if
not
recipients
:
recipients
=
get_emergency_recipients
()
recipients
=
set
(
recipients
)
recipients
.
add
(
cfg
[
'CFG_SITE_ADMIN_EMAIL'
])
for
address_str
in
recipients
:
send_email
(
cfg
[
'CFG_SITE_SUPPORT_EMAIL'
],
address_str
,
"Emergency notification"
,
msg
)
def
get_emergency_recipients
(
recipient_cfg
=
None
):
"""Parse a list of appropriate emergency email recipients from
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES, or from a provided dictionary
comprised of 'time constraint' => 'comma separated list of addresses'
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES format example:
CFG_SITE_EMERGENCY_EMAIL_ADDRESSES = {
'Sunday 22:00-06:00': '0041761111111@email2sms.foo.com',
'06:00-18:00': 'team-in-europe@foo.com,0041762222222@email2sms.foo.com',
'18:00-06:00': 'team-in-usa@foo.com',
'*': 'john.doe.phone@foo.com'}
"""
from
invenio.utils.date
import
parse_runtime_limit
if
recipient_cfg
is
None
:
recipient_cfg
=
cfg
[
'CFG_SITE_EMERGENCY_EMAIL_ADDRESSES'
]
recipients
=
set
()
for
time_condition
,
address_str
in
recipient_cfg
.
items
():
if
time_condition
and
time_condition
is
not
'*'
:
(
current_range
,
future_range
)
=
parse_runtime_limit
(
time_condition
)
if
not
current_range
[
0
]
<=
datetime
.
datetime
.
now
()
<=
current_range
[
1
]:
continue
recipients
.
update
([
address_str
])
return
list
(
recipients
)
def
find_all_values_to_hide
(
local_variables
,
analyzed_stack
=
None
):
"""Return all the potential password to hyde."""
## Let's add at least the DB password.
if
analyzed_stack
is
None
:
ret
=
set
([
cfg
[
'CFG_DATABASE_PASS'
]])
analyzed_stack
=
set
()
else
:
ret
=
set
()
for
key
,
value
in
local_variables
.
iteritems
():
if
id
(
value
)
in
analyzed_stack
:
## Let's avoid loops
continue
analyzed_stack
.
add
(
id
(
value
))
if
RE_PWD
.
search
(
key
):
ret
.
add
(
str
(
value
))
if
isinstance
(
value
,
dict
):
ret
|=
find_all_values_to_hide
(
value
,
analyzed_stack
)
if
''
in
ret
:
## Let's discard the empty string in case there is an empty password,
## or otherwise anything will be separated by '<*****>' in the output
## :-)
ret
.
remove
(
''
)
return
ret
def
get_pretty_traceback
(
req
=
None
,
exc_info
=
None
,
skip_frames
=
0
):
"""
Given an optional request object and an optional exc_info,
returns a text string representing many details about an exception.
"""
if
exc_info
is
None
:
exc_info
=
sys
.
exc_info
()
if
exc_info
[
0
]:
## We found an exception.
## We want to extract the name of the Exception
exc_name
=
exc_info
[
0
]
.
__name__
exc_value
=
str
(
exc_info
[
1
])
filename
,
line_no
,
function_name
=
_get_filename_and_line
(
exc_info
)
## Let's record when and where and what
www_data
=
"
%(time)s
->
%(name)s
:
%(value)s
(
%(file)s
:
%(line)s
:
%(function)s
)"
%
{
'time'
:
time
.
strftime
(
"%Y-%m-
%d
%H:%M:%S"
),
'name'
:
exc_name
,
'value'
:
exc_value
,
'file'
:
filename
,
'line'
:
line_no
,
'function'
:
function_name
}
## Let's retrieve contextual user related info, if any
try
:
client_data
=
get_pretty_wide_client_info
(
req
)
except
Exception
,
err
:
client_data
=
"Error in retrieving "
\
"contextual information:
%s
"
%
err
## Let's extract the traceback:
tracestack_data_stream
=
StringIO
()
print
>>
tracestack_data_stream
,
\
"
\n
** Traceback details
\n
"
traceback
.
print_exc
(
file
=
tracestack_data_stream
)
stack
=
[
frame
[
0
]
for
frame
in
inspect
.
trace
()]
#stack = [frame[0] for frame in inspect.getouterframes(exc_info[2])][skip_frames:]
try
:
stack
.
reverse
()
print
>>
tracestack_data_stream
,
\
"
\n
** Stack frame details"
values_to_hide
=
set
()
for
frame
in
stack
:
try
:
print
>>
tracestack_data_stream
print
>>
tracestack_data_stream
,
\
"Frame
%s
in
%s
at line
%s
"
%
(
frame
.
f_code
.
co_name
,
frame
.
f_code
.
co_filename
,
frame
.
f_lineno
)
## Dereferencing f_locals
## See: http://utcc.utoronto.ca/~cks/space/blog/python/FLocalsAndTraceFunctions
local_values
=
frame
.
f_locals
try
:
values_to_hide
|=
find_all_values_to_hide
(
local_values
)
code
=
open
(
frame
.
f_code
.
co_filename
)
.
readlines
()
first_line
=
max
(
1
,
frame
.
f_lineno
-
3
)
last_line
=
min
(
len
(
code
),
frame
.
f_lineno
+
3
)
print
>>
tracestack_data_stream
,
"-"
*
79
for
line
in
xrange
(
first_line
,
last_line
+
1
):
code_line
=
code
[
line
-
1
]
.
rstrip
()
if
line
==
frame
.
f_lineno
:
print
>>
tracestack_data_stream
,
\
"---->
%4i
%s
"
%
(
line
,
code_line
)
else
:
print
>>
tracestack_data_stream
,
\
"
%4i
%s
"
%
(
line
,
code_line
)
print
>>
tracestack_data_stream
,
"-"
*
79
except
:
pass
for
key
,
value
in
local_values
.
items
():
print
>>
tracestack_data_stream
,
"
\t
%20s
= "
%
key
,
try
:
value
=
repr
(
value
)
except
Exception
,
err
:
## We shall gracefully accept errors when repr() of
## a value fails (e.g. when we are trying to repr() a
## variable that was not fully initialized as the
## exception was raised during its __init__ call).
value
=
"ERROR: when representing the value:
%s
"
%
(
err
)
try
:
print
>>
tracestack_data_stream
,
\
_truncate_dynamic_string
(
value
)
except
:
print
>>
tracestack_data_stream
,
\
"<ERROR WHILE PRINTING VALUE>"
finally
:
del
frame
finally
:
del
stack
tracestack_data
=
tracestack_data_stream
.
getvalue
()
for
to_hide
in
values_to_hide
:
## Let's hide passwords
tracestack_data
=
tracestack_data
.
replace
(
to_hide
,
'<*****>'
)
## Okay, start printing:
output
=
StringIO
()
print
>>
output
,
"*
%s
"
%
www_data
print
>>
output
,
"
\n
** User details"
print
>>
output
,
client_data
if
tracestack_data
:
print
>>
output
,
tracestack_data
return
output
.
getvalue
()
else
:
return
""
def
register_exception
(
stream
=
'error'
,
req
=
None
,
prefix
=
''
,
suffix
=
''
,
alert_admin
=
False
,
subject
=
''
):
"""
Log error exception to invenio.err and warning exception to invenio.log.
Errors will be logged together with client information (if req is
given).
Note: For sanity reasons, dynamic params such as PREFIX, SUFFIX and
local stack variables are checked for length, and only first 500
chars of their values are printed.
@param stream: 'error' or 'warning'
@param req: mod_python request
@param prefix: a message to be printed before the exception in
the log
@param suffix: a message to be printed before the exception in
the log
@param alert_admin: wethever to send the exception to the administrator via
email. Note this parameter is bypassed when
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS is set to a value different than 1
@param subject: overrides the email subject
@return: 1 if successfully wrote to stream, 0 if not
"""
try
:
## Let's extract exception information
exc_info
=
sys
.
exc_info
()
exc_name
=
exc_info
[
0
]
.
__name__
output
=
get_pretty_traceback
(
req
=
req
,
exc_info
=
exc_info
,
skip_frames
=
2
)
if
output
:
## Okay, start printing:
log_stream
=
StringIO
()
email_stream
=
StringIO
()
print
>>
email_stream
,
'
\n
'
,
## If a prefix was requested let's print it
if
prefix
:
#prefix = _truncate_dynamic_string(prefix)
print
>>
log_stream
,
prefix
+
'
\n
'
print
>>
email_stream
,
prefix
+
'
\n
'
print
>>
log_stream
,
output
print
>>
email_stream
,
output
## If a suffix was requested let's print it
if
suffix
:
#suffix = _truncate_dynamic_string(suffix)
print
>>
log_stream
,
suffix
print
>>
email_stream
,
suffix
log_text
=
log_stream
.
getvalue
()
email_text
=
email_stream
.
getvalue
()
if
email_text
.
endswith
(
'
\n
'
):
email_text
=
email_text
[:
-
1
]
## Preparing the exception dump
if
stream
==
'error'
:
logger_method
=
current_app
.
logger
.
error
else
:
logger_method
=
current_app
.
logger
.
info
## We now have the whole trace
written_to_log
=
False
try
:
## Let's try to write into the log.
logger_method
(
log_text
)
written_to_log
=
True
except
:
written_to_log
=
False
filename
,
line_no
,
function_name
=
_get_filename_and_line
(
exc_info
)
## let's log the exception and see whether we should report it.
log
=
HstEXCEPTION
.
get_or_create
(
exc_name
,
filename
,
line_no
)
if
log
.
exception_should_be_notified
and
(
cfg
[
'CFG_SITE_ADMIN_EMAIL_EXCEPTIONS'
]
>
1
or
(
alert_admin
and
cfg
[
'CFG_SITE_ADMIN_EMAIL_EXCEPTIONS'
]
>
0
)
or
not
written_to_log
):
## If requested or if it's impossible to write in the log
from
invenio.ext.email
import
send_email
if
not
subject
:
subject
=
'Exception (
%s
:
%s
:
%s
)'
%
(
filename
,
line_no
,
function_name
)
subject
=
'
%s
at
%s
'
%
(
subject
,
cfg
[
'CFG_SITE_URL'
])
email_text
=
"
\n
%s
\n
%s
"
%
(
log
.
pretty_notification_info
,
email_text
)
if
not
written_to_log
:
email_text
+=
"""\
Note that this email was sent to you because it has been impossible to log
this exception into %s"""
%
os
.
path
.
join
(
cfg
[
'CFG_LOGDIR'
],
'invenio.'
+
stream
)
send_email
(
cfg
[
'CFG_SITE_ADMIN_EMAIL'
],
cfg
[
'CFG_SITE_ADMIN_EMAIL'
],
subject
=
subject
,
content
=
email_text
)
return
1
else
:
return
0
except
Exception
,
err
:
print
>>
sys
.
stderr
,
"Error in registering exception to '
%s
': '
%s
'"
%
(
cfg
[
'CFG_LOGDIR'
]
+
'/invenio.'
+
stream
,
err
)
return
0
def
raise_exception
(
exception_type
=
Exception
,
msg
=
''
,
stream
=
'error'
,
req
=
None
,
prefix
=
''
,
suffix
=
''
,
alert_admin
=
False
,
subject
=
''
):
"""
Log error exception to invenio.err and warning exception to invenio.log.
Errors will be logged together with client information (if req is
given).
It does not require a previously risen exception.
Note: For sanity reasons, dynamic params such as PREFIX, SUFFIX and
local stack variables are checked for length, and only first 500
chars of their values are printed.
@param exception_type: exception type to be used internally
@param msg: error message
@param stream: 'error' or 'warning'
@param req: mod_python request
@param prefix: a message to be printed before the exception in
the log
@param suffix: a message to be printed before the exception in
the log
@param alert_admin: wethever to send the exception to the administrator via
email. Note this parameter is bypassed when
CFG_SITE_ADMIN_EMAIL_EXCEPTIONS is set to a value different than 1
@param subject: overrides the email subject
@return: 1 if successfully wrote to stream, 0 if not
"""
try
:
raise
exception_type
(
msg
)
except
:
return
register_exception
(
stream
=
stream
,
req
=
req
,
prefix
=
prefix
,
suffix
=
suffix
,
alert_admin
=
alert_admin
,
subject
=
subject
)
def
send_error_report_to_admin
(
header
,
url
,
time_msg
,
browser
,
client
,
error
,
sys_error
,
traceback_msg
):
"""
Sends an email to the admin with client info and tracestack
"""
from_addr
=
'
%s
Alert Engine <
%s
>'
%
(
cfg
[
'CFG_SITE_NAME'
],
cfg
[
'CFG_WEBALERT_ALERT_ENGINE_EMAIL'
])
to_addr
=
cfg
[
'CFG_SITE_ADMIN_EMAIL'
]
body
=
"""
The following error was seen by a user and sent to you.
%(contact)s
%(header)s
%(url)s
%(time)s
%(browser)s
%(client)s
%(error)s
%(sys_error)s
%(traceback)s
Please see the %(logdir)s/invenio.err for traceback details."""
%
{
'header'
:
header
,
'url'
:
url
,
'time'
:
time_msg
,
'browser'
:
browser
,
'client'
:
client
,
'error'
:
error
,
'sys_error'
:
sys_error
,
'traceback'
:
traceback_msg
,
'logdir'
:
cfg
[
'CFG_LOGDIR'
],
'contact'
:
"Please contact
%s
quoting the following information:"
%
(
cfg
[
'CFG_SITE_SUPPORT_EMAIL'
],
)}
from
invenio.ext.email
import
send_email
send_email
(
from_addr
,
to_addr
,
subject
=
"Error notification"
,
content
=
body
)
def
_get_filename_and_line
(
exc_info
):
"""Return the filename, the line and the function_name where
the exception happened."""
tb
=
exc_info
[
2
]
exception_info
=
traceback
.
extract_tb
(
tb
)[
-
1
]
filename
=
os
.
path
.
basename
(
exception_info
[
0
])
line_no
=
exception_info
[
1
]
function_name
=
exception_info
[
2
]
return
filename
,
line_no
,
function_name
def
_truncate_dynamic_string
(
val
,
maxlength
=
500
):
"""
Return at most MAXLENGTH characters of VAL. Useful for
sanitizing dynamic variable values in the output.
"""
out
=
repr
(
val
)
if
len
(
out
)
>
maxlength
:
out
=
out
[:
maxlength
]
+
' [...]'
return
out
def
wrap_warn
():
import
warnings
from
functools
import
wraps
def
wrapper
(
showwarning
):
@wraps
(
showwarning
)
def
new_showwarning
(
message
=
None
,
category
=
None
,
filename
=
None
,
lineno
=
None
,
file
=
None
,
line
=
None
):
invenio_err
=
open
(
os
.
path
.
join
(
cfg
[
'CFG_LOGDIR'
],
'invenio.err'
),
"a"
)
print
>>
invenio_err
,
"*
%(time)s
-> WARNING:
%(category)s
:
%(message)s
(
%(file)s
:
%(line)s
)
\n
"
%
{
'time'
:
time
.
strftime
(
"%Y-%m-
%d
%H:%M:%S"
),
'category'
:
category
,
'message'
:
message
,
'file'
:
filename
,
'line'
:
lineno
}
print
>>
invenio_err
,
"** Traceback details
\n
"
traceback
.
print_stack
(
file
=
invenio_err
)
print
>>
invenio_err
,
"
\n
"
return
new_showwarning
warnings
.
showwarning
=
wrapper
(
warnings
.
showwarning
)
Event Timeline
Log In to Comment