Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
P
pydevutils
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Art
pydevutils
Commits
117de29d
Commit
117de29d
authored
7 years ago
by
Art
Browse files
Options
Downloads
Patches
Plain Diff
Make the colored logger best friends with celery :]
parent
0ca15364
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
colored_logger.py
+39
-27
39 additions, 27 deletions
colored_logger.py
with
39 additions
and
27 deletions
colored_logger.py
+
39
−
27
View file @
117de29d
...
@@ -20,7 +20,8 @@ intro_words = ["magic", "MaGiC", "voodoo", "sorcery", "wizardry", "witchery", "f
...
@@ -20,7 +20,8 @@ intro_words = ["magic", "MaGiC", "voodoo", "sorcery", "wizardry", "witchery", "f
intro_adjectives
=
[
"
colourful
"
,
"
evil
"
,
"
fairy
"
,
"
random
"
]
intro_adjectives
=
[
"
colourful
"
,
"
evil
"
,
"
fairy
"
,
"
random
"
]
intro_colors
=
[
34
,
36
,
32
,
33
,
35
]
intro_colors
=
[
34
,
36
,
32
,
33
,
35
]
decorate_info
=
{
logger_methods_colors
=
{
# method names and their desired highlight colors
# method names and their desired highlight colors
"
debug
"
:
"
1;90
"
,
"
debug
"
:
"
1;90
"
,
"
info
"
:
"
1;32
"
,
"
info
"
:
"
1;32
"
,
...
@@ -32,7 +33,10 @@ decorate_info = {
...
@@ -32,7 +33,10 @@ decorate_info = {
# Adjusting handlers: Python logging.StreamHandler targets stderr by default.
# Adjusting handlers: Python logging.StreamHandler targets stderr by default.
# Django default logging configuration doesn't change it, too.
# Django default logging configuration doesn't change it, too.
# Existing loggers first.
# New loggers (they will be initially created with the proper stream).
original_constructor
=
logging
.
StreamHandler
.
__init__
# must grab a reference outside of the lambda
logging
.
StreamHandler
.
__init__
=
lambda
zelf
,
stream
=
None
:
original_constructor
(
zelf
,
stream
if
stream
!=
sys
.
stderr
else
sys
.
stdout
)
# Existing loggers
patched_objects
=
set
()
patched_objects
=
set
()
for
logger
in
logging
.
Logger
.
manager
.
loggerDict
.
values
():
for
logger
in
logging
.
Logger
.
manager
.
loggerDict
.
values
():
for
handler
in
getattr
(
logger
,
"
handlers
"
,
list
()):
for
handler
in
getattr
(
logger
,
"
handlers
"
,
list
()):
...
@@ -40,45 +44,51 @@ for logger in logging.Logger.manager.loggerDict.values():
...
@@ -40,45 +44,51 @@ for logger in logging.Logger.manager.loggerDict.values():
if
handler
.
stream
==
sys
.
stderr
:
if
handler
.
stream
==
sys
.
stderr
:
handler
.
stream
=
sys
.
stdout
handler
.
stream
=
sys
.
stdout
patched_objects
.
add
(
handler
)
patched_objects
.
add
(
handler
)
# Future loggers (they should be initially created with the proper stream).
original_constructor
=
logging
.
StreamHandler
.
__init__
# must grab a reference outside of the lambda
logging
.
StreamHandler
.
__init__
=
lambda
zelf
,
stream
=
None
:
original_constructor
(
zelf
,
stream
or
sys
.
stdout
)
# the isatty() stuff below solves the problem with IDE streams not being recognized as TTY
# the isatty() stuff below solved some problems in the past, I'm not sure anymore which exactly
# normally sys.stdout is sufficient, but some stubborn stuff like Celery explicitly checks sys.stderr even if it doesn't write there
for
stream
in
[
sys
.
stdout
,
sys
.
stderr
]:
try
:
try
:
stdout_tty
=
sys
.
stdout
.
isatty
()
isatty
=
stream
.
isatty
()
except
Exception
:
except
Exception
:
stdout_
tty
=
False
isa
tty
=
False
if
not
stdout_
tty
:
if
not
isa
tty
:
patched_objects
.
add
(
s
ys
.
stdout
.
isatty
)
patched_objects
.
add
(
s
tream
)
sys
.
stdout
.
isatty
=
lambda
:
True
# stdout is now a TTY no matter what
stream
.
isatty
=
lambda
:
True
# stdout is now a TTY no matter what
def
_apply_color_to_msg
(
msg
,
color
,
extras
):
return
""
.
join
([
color_set
.
format
(
color
),
extras
,
str
(
msg
),
color_reset
])
def
colorize_string
(
msg
,
color
=
"
1;32
"
,
extras
=
""
):
return
str
().
join
([
color_set
.
format
(
color
),
extras
,
str
(
msg
),
color_reset
])
def
decorate_for_tty
(
method
,
color
=
"
1;39
"
,
extras
=
""
):
def
decorate_for_tty
(
method
,
color
=
"
1;39
"
,
extras
=
""
):
"""
it also knows how to decorate bound methods, but it is not used anymore
"""
"""
decorate a method, replacing its first argument (must be str!) with the same, but colorized
(it also knows how to decorate bound methods)
"""
method_is_bound
=
bool
(
getattr
(
method
,
"
__self__
"
,
None
))
# existing loggers cause bound methods
method_is_bound
=
bool
(
getattr
(
method
,
"
__self__
"
,
None
))
# existing loggers cause bound methods
msg_index
=
0
if
method_is_bound
else
1
# message argument is located there
msg_
arg_
index
=
0
if
method_is_bound
else
1
# message argument is located there
def
wrapper
(
*
args
,
**
kwargs
):
def
wrapper
(
*
args
,
**
kwargs
):
args
=
list
(
args
)
# it's an immutable tuple when received
args
=
list
(
args
)
# it's an immutable tuple when received
args
[
msg_index
]
=
_apply_color_to_ms
g
(
args
[
msg_index
],
color
,
extras
)
args
[
msg_
arg_
index
]
=
colorize_strin
g
(
args
[
msg_
arg_
index
],
color
,
extras
)
return
method
(
*
args
,
**
kwargs
)
return
method
(
*
args
,
**
kwargs
)
return
wrapper
return
wrapper
# decorate classes
for
entity
in
[
logging
.
Logger
]:
for
entity
in
[
logging
.
Logger
]:
for
method_name
,
color_str
in
decorate_info
.
items
():
for
method_name
,
color_str
in
logger_methods_colors
.
items
():
if
isinstance
(
entity
,
logging
.
Logger
)
or
entity
is
logging
.
Logger
:
if
isinstance
(
entity
,
logging
.
PlaceHolder
):
continue
# ignore the PlaceHolder object, it's not functional and not a public API
new_method
=
decorate_for_tty
(
getattr
(
entity
,
method_name
),
color_str
)
new_method
=
decorate_for_tty
(
getattr
(
entity
,
method_name
),
color_str
)
setattr
(
entity
,
method_name
,
new_method
)
setattr
(
entity
,
method_name
,
new_method
)
patched_objects
.
add
(
entity
)
patched_objects
.
add
(
entity
)
else
:
pass
# that should be a placeholder object (non-public API), ignoring it
# useless
foo
#
fancy
useless
stuff
intro_seq
=
zip
(
itertools
.
cycle
(
intro_colors
),
random
.
choice
(
intro_words
))
intro_seq
=
zip
(
itertools
.
cycle
(
intro_colors
),
random
.
choice
(
intro_words
))
intro_msg
=
"
{adj} {word}{reset}
"
.
format
(
intro_msg
=
"
{adj} {word}{reset}
"
.
format
(
adj
=
random
.
choice
(
intro_adjectives
).
capitalize
(),
adj
=
random
.
choice
(
intro_adjectives
).
capitalize
(),
...
@@ -94,6 +104,8 @@ sys.stdout.write("{intro_msg}{c}: patched {n} object{s}{cr}\n".format(
...
@@ -94,6 +104,8 @@ sys.stdout.write("{intro_msg}{c}: patched {n} object{s}{cr}\n".format(
s
=
""
if
len
(
patched_objects
)
is
1
else
"
s
"
,
s
=
""
if
len
(
patched_objects
)
is
1
else
"
s
"
,
cr
=
color_reset
,
cr
=
color_reset
,
))
))
logging
.
getLogger
(
"
pydevutils
"
).
debug
(
"
Logging patches: {}.
"
.
format
(
"
,
"
.
join
(
str
(
e
)
for
e
in
patched_objects
)
or
"
none
"
))
# debug with the following line:
# sys.stdout.write("Patched objects: {}\n".format(", ".join(str(e) for e in patched_objects) or "none"))
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment