Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
Trevor Cappallo
OperationalLogging
Commits
da0030e7
Commit
da0030e7
authored
Aug 14, 2015
by
Trevor Cappallo
Browse files
add options, change names
parent
d6103181
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
96 additions
and
96 deletions
+96
-96
lib/OperationalLogging.py
lib/OperationalLogging.py
+96
-96
No files found.
lib/OperationalLogging.py
View file @
da0030e7
...
...
@@ -13,6 +13,7 @@ import atexit
import
datetime
import
StringIO
import
inspect
import
json
import
OperationalMail
...
...
@@ -30,55 +31,51 @@ def __exit_handler(logger, addresses, critical_addresses):
logger
.
email_log
(
addresses
)
#class MultiLineWrapper(logging.StreamHandler):
# def __init__(self, stream_handler):
# super(logging.StreamHandler, self).__init__(stream_handler)
# self.stream_handler = stream_handler
#
# def emit(self, record):
# print "-------", record
# super(MultiLineWrapper, self).emit(record)
#for line in record.split('\n'):
# self.stream_handler.emit(line)
# def flush(self):
# self.stream_handler.flush()
class
OperationalLogger
:
"""Main class for Operational Logging."""
def
__init__
(
self
,
log_file
=
'log.txt'
,
log_level
=
logging
.
DEBUG
,
log_format
=
None
,
date_format
=
None
,
from_address
=
None
,
logger_name
=
''
,
console_level
=
logging
.
DEBUG
):
"""Initialize logging handlers, formats, etc."""
self
.
__file_name
=
log_file
if
logger_name
is
None
else
logger_name
# logger = OperationalLogger(
# log_filename=os.path.join(log_directory, log_filename), logger_name=logger_name,
# from_address=from_address, notify=notify, log_level=log_level, console_level=console_level,
# log_format_file=log_format_file, date_format_file=date_format_file,
# log_format_console=log_format_console, date_format_console=date_format_console
# )
def
__init__
(
self
,
log_filename
,
logger_name
,
from_address
,
notify
,
log_level
,
console_level
,
log_format_file
,
date_format_file
,
log_format_console
,
date_format_console
,
**
kwargs
):
"""Initialize logging handlers, formats, etc.
Should not be called directly; use setup().
"""
self
.
__server_name
=
socket
.
getfqdn
()
self
.
__
full
_buffer
=
StringIO
.
StringIO
()
self
.
__
debug
_buffer
=
StringIO
.
StringIO
()
self
.
__error_buffer
=
StringIO
.
StringIO
()
self
.
__critical_buffer
=
StringIO
.
StringIO
()
if
log_format
is
None
:
log_format
=
'[%(asctime)s][%(levelname)5s][p%(process)d][%(filename)s:%(lineno)d] %(message)s'
self
.
__log_format
=
log_format
if
date_format
is
None
:
date_format
=
'%Y/%m/%d %H:%M:%S'
self
.
__date_format
=
date_format
if
log_format_file
is
None
:
log_format_file
=
'[%(asctime)s][%(levelname)5s][p%(process)d][%(filename)s:%(lineno)d] %(message)s'
if
log_format_console
is
None
:
log_format_console
=
'%(asctime)s (%(levelname)5s) [%(filename)s:%(lineno)d] %(message)s'
if
date_format_file
is
None
:
date_format_file
=
'%Y-%m-%d %H:%M:%S'
if
date_format_console
is
None
:
date_format_console
=
'%H:%M:%S'
if
from_address
is
None
:
from_address
=
'no-reply@aer.com'
self
.
__from_address
=
from_address
logging
.
basicConfig
(
filename
=
log_file
,
format
=
self
.
__
log_format
,
datefmt
=
self
.
__
date_format
,
level
=
log
_level
)
formatter
=
logging
.
Formatter
(
fmt
=
self
.
__
log_format
,
datefmt
=
'%m/%d %H:%M:%S'
)
logging
.
basicConfig
(
filename
=
log_file
name
,
format
=
log_format
_file
,
datefmt
=
date_format
_file
,
level
=
log
ging
.
DEBUG
)
formatter
=
logging
.
Formatter
(
fmt
=
log_format
_console
,
datefmt
=
date_format_console
)
formatter_sub
=
logging
.
Formatter
(
fmt
=
'[%(asctime)s][%(levelname)5s][p%(process)d][%(parentname)s:%(parentline)d] %(message)s'
,
#fmt='%(asctime)s [%(parentname)s:%(parentline)d] :: %(message)s',
fmt
=
'%(asctime)s |- %(message)s'
,
datefmt
=
'%H:%M:%S'
)
sub_handler
=
logging
.
handlers
.
WatchedFileHandler
(
log_file
)
sub_handler
=
logging
.
handlers
.
WatchedFileHandler
(
log_file
name
)
sub_handler
.
setLevel
(
logging
.
INFO
)
sub_handler
.
setFormatter
(
formatter_sub
)
logging
.
getLogger
(
logger_name
+
'_sub'
).
addHandler
(
sub_handler
)
logging
.
getLogger
(
logger_name
+
'_sub'
).
propagate
=
False
logging
.
getLogger
(
'_sub'
).
addHandler
(
sub_handler
)
logging
.
getLogger
(
'_sub'
).
propagate
=
False
full_trigger
=
logging
.
StreamHandler
(
self
.
__
full
_buffer
)
full_trigger
=
logging
.
StreamHandler
(
self
.
__
debug
_buffer
)
full_trigger
.
setLevel
(
logging
.
DEBUG
)
full_trigger
.
setFormatter
(
formatter
)
error_trigger
=
logging
.
StreamHandler
(
self
.
__error_buffer
)
...
...
@@ -97,15 +94,15 @@ class OperationalLogger:
console
=
logging
.
StreamHandler
(
sys
.
stdout
)
console
.
setLevel
(
console_level
)
console
.
setFormatter
(
logging
.
Formatter
(
fmt
=
'[%(asctime)s][%(levelname)5s][%(filename)s:%(lineno)d] %(message)s'
,
datefmt
=
'%m/%d %H:%M:%S'
fmt
=
log_format_console
,
datefmt
=
date_format_console
,
))
logging
.
getLogger
(
logger_name
).
addHandler
(
console
)
sub_handler_console
=
logging
.
StreamHandler
(
sys
.
stdout
)
sub_handler_console
.
setLevel
(
logging
.
INFO
)
sub_handler_console
.
setFormatter
(
formatter_sub
)
logging
.
getLogger
(
logger_name
+
'_sub'
).
addHandler
(
sub_handler_console
)
logging
.
getLogger
(
'_sub'
).
addHandler
(
sub_handler_console
)
def
has_errors
(
self
):
"""Return whether any ERROR-level log statements in buffer."""
...
...
@@ -115,14 +112,14 @@ class OperationalLogger:
"""Return whether any CRITICAL-level log statements in buffer."""
return
len
(
self
.
__critical_buffer
.
getvalue
())
>
0
def
email_log
(
self
,
addresses
):
def
email_log
(
self
,
addresses
,
from_address
):
"""Send email to addresses with current buffered errors."""
print
"sending email to"
,
addresses
message
=
"<br />
\n
"
.
join
([
OperationalLogger
.
__colorize
(
l
)
for
l
in
self
.
__error_buffer
.
getvalue
().
split
(
'
\n
'
)])
message
=
"<br />
\n
"
.
join
([
__class__
.
__colorize
(
l
ine
)
for
l
ine
in
self
.
__error_buffer
.
getvalue
().
split
(
'
\n
'
)])
OperationalMail
.
send_status
(
subject
=
'Errors detected {}:{}'
.
format
(
self
.
__server_name
,
os
.
path
.
basename
(
sys
.
argv
[
0
])),
message
=
message
,
to_list
=
addresses
,
from_address
=
self
.
__
from_address
,
text_attachment
=
self
.
__
full
_buffer
.
getvalue
())
message
=
message
,
to_list
=
addresses
,
from_address
=
from_address
,
text_attachment
=
self
.
__
debug
_buffer
.
getvalue
())
@
staticmethod
def
__colorize
(
line
):
...
...
@@ -143,37 +140,60 @@ class OperationalLogger:
##########################################################################################
def
default_logging
(
log_file_name
=
None
,
log_root_dir
=
None
,
addresses
=
[],
critical_addresses
=
[],
from_address
=
None
,
**
kwargs
):
"""Set an intelligent,context-dependent default logging scheme for the current script.
log_root_dir, log_file_name: log will be created as log_root_dir/log/YYYY-MM-DD/LOG_FILE_NAME.log
log_file_name defaults to script name
addresses, critical_address: email addresses to be notified on respective level errors
def
setup
(
config_file
=
None
,
logger_name
=
None
,
log_filename
=
None
,
log_root_dir
=
None
,
from_address
=
None
,
notify
=
None
,
log_level
=
None
,
console_level
=
None
,
log_format_file
=
None
,
date_format_file
=
None
,
log_format_console
=
None
,
date_format_console
=
None
):
"""Configure logger and begin logging.
config_file: filename to load for logging config as JSON using these keywords, overwritten by args
logger_name: name to pass to logger
log_root_dir, log_filename: log will be created as log_root_dir/log/YYYY-MM-DD/LOG_FILENAME.log
log_root_dir defaults to '.'
log_filename defaults to script name
from_address: email 'From' address for notifications
notify: dict containing 'LOG_LEVEL':['list', 'of', 'addresses']
log_level: cutoff level for overall/file logging
console_level: cutoff level for stdout
log_format_file, date_format_file: formats to use for file logger
log_format_console, date_format_console: formats to use for stdout logger
"""
if
not
log_file
_
name
:
log_file
_
name
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
if
not
log_filename
:
log_filename
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
.
split
(
'.'
)[
0
]
+
'.log'
if
not
log_root_dir
:
log_root_dir
=
os
.
getcwd
()
log_directory
=
os
.
path
.
join
(
log_root_dir
,
'log'
,
datetime
.
datetime
.
utcnow
().
strftime
(
'%Y-%m-%d'
))
if
not
os
.
path
.
exists
(
log_directory
):
try
:
os
.
makedirs
(
log_directory
,
0755
)
except
Exception
as
err
:
# Potential race condition. Warn, but do not error
logging
.
warning
(
'Unable to create log directory {}:{}'
.
format
(
log_directory
,
str
(
err
)))
logger
=
OperationalLogger
(
os
.
path
.
join
(
log_directory
,
'{}.log'
.
format
(
log_file_name
)),
logging
.
DEBUG
,
from_address
=
from_address
,
**
kwargs
)
except
OSError
:
logging
.
warn
(
'Unable to create log directory %s'
,
log_directory
)
if
notify
is
None
:
notify
=
{}
if
logger_name
is
None
:
logger_name
=
''
if
log_level
is
None
:
log_level
=
logging
.
DEBUG
if
console_level
is
None
:
console_level
=
logging
.
INFO
if
config_file
:
with
open
(
config_file
,
'r'
)
as
f
:
config
=
json
.
load
(
f
)
for
key
,
val
in
config
.
items
():
if
key
in
vars
()
and
vars
()[
key
]
is
None
:
vars
()[
key
]
=
val
logger
=
OperationalLogger
(
log_filename
=
os
.
path
.
join
(
log_directory
,
log_filename
),
logger_name
=
logger_name
,
from_address
=
from_address
,
notify
=
notify
,
log_level
=
log_level
,
console_level
=
console_level
,
log_format_file
=
log_format_file
,
date_format_file
=
date_format_file
,
log_format_console
=
log_format_console
,
date_format_console
=
date_format_console
)
logging
.
info
(
'Logging output from: {}'
.
format
(
' '
.
join
(
sys
.
argv
)))
logging
.
info
(
'Log writing to: %s'
,
os
.
path
.
join
(
log_directory
,
log_file
_
name
+
'.log'
))
logging
.
info
(
'Log writing to: %s'
,
os
.
path
.
join
(
log_directory
,
log_filename
))
logging
.
info
(
'Process id {}, parent process id {}'
.
format
(
os
.
getpid
(),
os
.
getppid
()))
logging
.
info
(
'-'
*
60
)
if
addresses
:
# and not sys.stdout.isatty():
atexit
.
register
(
__exit_handler
,
logger
,
addresses
,
critical_addresses
)
return
if
notify
:
atexit
.
register
(
__exit_handler
,
logger
,
notify
)
def
long_log
(
logging_level
=
logging
.
INFO
,
message
=
''
):
...
...
@@ -183,7 +203,7 @@ def long_log(logging_level=logging.INFO, message=''):
"""
message_list
=
message
.
split
(
"
\n
"
)
for
msg
in
message_list
:
logging
.
log
(
logging_level
,
' :: '
+
msg
)
logging
.
log
(
logging_level
,
' ::
'
+
msg
)
def
long_critical
(
message
):
...
...
@@ -211,31 +231,11 @@ def long_debug(message):
long_log
(
logging
.
DEBUG
,
message
)
def
log
ged_subprocess
(
arg_list
):
def
log
_shell
(
arg_list
,
**
kwargs
):
"""Pass arg_list to a subprocess call and log all output."""
logging
.
info
(
'
C
hild process of
[
p{}
]
'
.
format
(
os
.
getppid
()))
logging
.
info
(
'
Attemp
ting {}'
.
format
(
' '
.
join
(
arg_list
)))
logging
.
info
(
'
Starting c
hild process of p
pid=
{}'
.
format
(
os
.
getppid
()))
logging
.
info
(
'
Execu
ting
:
{}'
.
format
(
' '
.
join
(
arg_list
)))
try
:
results
=
subprocess
.
check_output
(
arg_list
,
stderr
=
subprocess
.
STDOUT
)
except
subprocess
.
CalledProcessError
as
cpe
:
logging
.
error
(
'Call {} failed with exit code {}'
.
format
(
' '
.
join
(
arg_list
),
str
(
cpe
.
returncode
)))
long_error
(
cpe
.
output
)
except
Exception
as
err
:
logging
.
error
(
'Unknown exception resulting from {}:{}'
.
format
(
' '
.
join
(
arg_list
),
str
(
err
)))
else
:
logging
.
info
(
'Successfully completed {}'
.
format
(
' '
.
join
(
arg_list
)))
long_info
(
results
)
return
0
logging
.
warning
(
'Failure on {}'
.
format
(
' '
.
join
(
arg_list
)))
return
-
1
def
logged_sub
(
arg_list
,
logging_level
=
logging
.
INFO
,
**
kwargs
):
"""Pass arg_list to a subprocess call and log all output."""
# TODO: respect logging_level
logging
.
info
(
'Child process of [p{}]'
.
format
(
os
.
getppid
()))
logging
.
info
(
'Attempting {}'
.
format
(
' '
.
join
(
arg_list
)))
try
:
#results = ''
my_name
=
os
.
path
.
basename
(
inspect
.
getfile
(
inspect
.
currentframe
()))
for
f
in
inspect
.
stack
():
if
my_name
!=
os
.
path
.
basename
(
f
[
1
]):
...
...
@@ -246,30 +246,30 @@ def logged_sub(arg_list, logging_level=logging.INFO, **kwargs):
for
line
in
iter
(
proc
.
stdout
.
readline
,
b
''
):
if
not
line
:
break
logging
.
getLogger
(
'_sub'
).
info
(
" :: "
+
line
.
rstrip
(),
extra
=
{
'parentname'
:
parent_name
,
'parentline'
:
line_num
})
#results += line
logging
.
getLogger
(
'_sub'
).
info
(
line
.
rstrip
(),
extra
=
{
'parentname'
:
parent_name
,
'parentline'
:
line_num
})
return_code
=
proc
.
wait
()
if
return_code
:
logging
.
warning
(
'{} failed with exit code {}'
.
format
(
' '
.
join
(
arg_list
),
return_code
))
return
return_code
except
OSError
as
err
:
logging
.
error
(
'Error resulting from {}:{}'
.
format
(
' '
.
join
(
arg_list
),
str
(
err
)))
logging
.
error
(
'Error while executing %s: %s'
,
' '
.
join
(
arg_list
),
str
(
err
))
return
err
else
:
#long_info(results)
logging
.
info
(
'Successfully completed {}'
.
format
(
' '
.
join
(
arg_list
)))
logging
.
info
(
'Successfully finished: %s'
,
' '
.
join
(
arg_list
))
return
0
def
stack_trace
(
logging_level
=
logging
.
ERROR
):
def
stack_trace
(
log_level
=
logging
.
ERROR
):
"""Log a stack trace at the given level."""
import
traceback
tb
=
traceback
.
format_exc
()
long_log
(
log
ging
_level
,
tb
)
long_log
(
log_level
,
tb
)
if
__name__
==
'__main__'
:
#default_logging(addresses=['tcappallo@veriskclimate.com'], from_address='tcappallo@veriskclimate.com')
#default_logging(addresses=['msze@veriskclimate.com', 'msze@aer.com'], from_address='tcappallo@veriskclimate.com')
default_logging
()
setup
()
test
=
"""Blah
foo
bar
...
...
@@ -285,9 +285,9 @@ if __name__ == '__main__':
#c = ['find', '.', '-name', '*.py']
c
=
[
'diff'
,
'a'
,
'b'
]
#logged_subprocess(c)
log
ged_sub
(
c
)
log
_shell
(
c
)
try
:
raise
Exception
(
"I AM ERROR."
)
except
Exception
as
err
:
logging
.
debug
(
str
(
err
)
)
stack_trace
(
logging
.
ERROR
)
logging
.
debug
(
err
)
stack_trace
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment