Skip to content

Commit

Permalink
Add ColoredLoggingHandler (#3198)
Browse files Browse the repository at this point in the history
  • Loading branch information
eevee-github authored and douglascamata committed Aug 10, 2016
1 parent b94d369 commit 9f9146c
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 3 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@
* nikhil-pandey
* thebigjc
* JaapMoolenaar
* eevee-github
1 change: 1 addition & 0 deletions configs/config.json.cluster.example
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"evolve_captured": "NONE",
"catch_randomize_reticle_factor": 1.0,
"catch_randomize_spin_factor": 1.0,
"logging_color": true,
"catch": {
"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"},
"// Example of always catching Rattata:": {},
Expand Down
1 change: 1 addition & 0 deletions configs/config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"evolve_captured": "NONE",
"catch_randomize_reticle_factor": 1.0,
"catch_randomize_spin_factor": 1.0,
"logging_color": true,
"catch": {
"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"},
"// Example of always catching Rattata:": {},
Expand Down
1 change: 1 addition & 0 deletions configs/config.json.map.example
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@
"evolve_captured": "NONE",
"catch_randomize_reticle_factor": 1.0,
"catch_randomize_spin_factor": 1.0,
"logging_color": true,
"catch": {
"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"},
"// Example of always catching Rattata:": {},
Expand Down
1 change: 1 addition & 0 deletions configs/config.json.path.example
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"evolve_captured": "NONE",
"catch_randomize_reticle_factor": 1.0,
"catch_randomize_spin_factor": 1.0,
"logging_color": true,
"catch": {
"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or"},
"// Example of always catching Rattata:": {},
Expand Down
1 change: 1 addition & 0 deletions configs/config.json.pokemon.example
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
"evolve_captured": "NONE",
"catch_randomize_reticle_factor": 1.0,
"catch_randomize_spin_factor": 1.0,
"logging_color": true,
"catch": {
"any": {"catch_above_cp": 0, "catch_above_iv": 0, "logic": "or" },

Expand Down
8 changes: 8 additions & 0 deletions pokecli.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,14 @@ def _json_loader(filename):
type=float,
default=5.0
)
add_config(
parser,
load,
long_flag="--logging_color",
help="If logging_color is set to true, colorized logging handler will be used",
type=bool,
default=True
)

# Start to parse other attrs
config = parser.parse_args()
Expand Down
10 changes: 7 additions & 3 deletions pokemongo_bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from human_behaviour import sleep
from item_list import Item
from metrics import Metrics
from pokemongo_bot.event_handlers import LoggingHandler, SocketIoHandler
from pokemongo_bot.event_handlers import LoggingHandler, SocketIoHandler, ColoredLoggingHandler
from pokemongo_bot.socketio_server.runner import SocketIoRunner
from pokemongo_bot.websocket_remote_control import WebsocketRemoteControl
from pokemongo_bot.base_dir import _base_dir
Expand Down Expand Up @@ -88,7 +88,12 @@ def start(self):
random.seed()

def _setup_event_system(self):
handlers = [LoggingHandler()]
handlers = []
if self.config.logging_color:
handlers.append(ColoredLoggingHandler())
else:
handlers.append(LoggingHandler())

if self.config.websocket_server_url:
if self.config.websocket_start_embedded_server:
self.sio_runner = SocketIoRunner(self.config.websocket_server_url)
Expand All @@ -103,7 +108,6 @@ def _setup_event_system(self):
if self.config.websocket_remote_control:
remote_control = WebsocketRemoteControl(self).start()


self.event_manager = EventManager(*handlers)
self._register_events()
if self.config.show_events:
Expand Down
1 change: 1 addition & 0 deletions pokemongo_bot/event_handlers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from logging_handler import LoggingHandler
from socketio_handler import SocketIoHandler
from colored_logging_handler import ColoredLoggingHandler
172 changes: 172 additions & 0 deletions pokemongo_bot/event_handlers/colored_logging_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import sys
import struct

from pokemongo_bot.event_manager import EventHandler

class ColoredLoggingHandler(EventHandler):
EVENT_COLOR_MAP = {
'api_error': 'red',
'bot_exit': 'red',
'bot_start': 'green',
'config_error': 'red',
'egg_already_incubating': 'yellow',
'egg_hatched': 'green',
'future_pokemon_release': 'yellow',
'incubate': 'green',
'incubator_already_used': 'yellow',
'inventory_full': 'yellow',
'item_discard_fail': 'red',
'item_discarded': 'green',
'keep_best_release': 'green',
'level_up': 'green',
'level_up_reward': 'green',
'location_cache_error': 'yellow',
'location_cache_ignored': 'yellow',
'login_failed': 'red',
'login_successful': 'green',
'lucky_egg_error': 'red',
'move_to_map_pokemon_encounter': 'green',
'move_to_map_pokemon_fail': 'red',
'next_egg_incubates': 'yellow',
'next_sleep': 'green',
'no_pokeballs': 'red',
'pokemon_appeared': 'yellow',
'pokemon_capture_failed': 'red',
'pokemon_caught': 'blue',
'pokemon_evolved': 'green',
'pokemon_fled': 'red',
'pokemon_inventory_full': 'red',
'pokemon_nickname_invalid': 'red',
'pokemon_not_in_range': 'yellow',
'pokemon_release': 'green',
'pokemon_vanished': 'red',
'pokestop_empty': 'yellow',
'pokestop_searching_too_often': 'yellow',
'rename_pokemon': 'green',
'skip_evolve': 'yellow',
'softban': 'red',
'spun_pokestop': 'cyan',
'threw_berry_failed': 'red',
'unknown_spin_result': 'red',
'unset_pokemon_nickname': 'red',
'vip_pokemon': 'red',

# event names for 'white' still here to remember that these events are already determined its color.
'arrived_at_cluster': 'white',
'arrived_at_fort': 'white',
'bot_sleep': 'white',
'catchable_pokemon': 'white',
'found_cluster': 'white',
'incubate_try': 'white',
'load_cached_location': 'white',
'location_found': 'white',
'login_started': 'white',
'lured_pokemon_found': 'white',
'move_to_map_pokemon_move_towards': 'white',
'move_to_map_pokemon_teleport_back': 'white',
'move_to_map_pokemon_updated_map': 'white',
'moving_to_fort': 'white',
'moving_to_lured_fort': 'white',
'pokemon_catch_rate': 'white',
'pokemon_evolve_fail': 'white',
'pokestop_on_cooldown': 'white',
'pokestop_out_of_range': 'white',
'polyline_request': 'white',
'position_update': 'white',
'set_start_location': 'white',
'softban_fix': 'white',
'softban_fix_done': 'white',
'spun_fort': 'white',
'threw_berry': 'white',
'threw_pokeball': 'white',
'used_lucky_egg': 'white'
}
CONTINUOUS_EVENT_NAMES = [
'catchable_pokemon',
'moving_to_lured_fort',
'spun_fort'
]
COLOR_CODE = {
'red': '91',
'green': '92',
'yellow': '93',
'blue': '94',
'cyan': '96'
}

def __init__(self):
self._last_event = None
try:
# this `try ... except` is for ImportError on Windows
import fcntl
import termios
self._ioctl = fcntl.ioctl
self._TIOCGWINSZ = termios.TIOCGWINSZ
except ImportError:
self._ioctl = None
self._TIOCGWINSZ = None

def handle_event(self, event, sender, level, formatted_msg, data):
# Prepare message string
message = None
if formatted_msg:
try:
message = formatted_msg.decode('utf-8')
except UnicodeEncodeError:
message = formatted_msg
else:
message = '{}'.format(str(data))

# Replace message if necessary
if event == 'catchable_pokemon':
message = 'Something rustles nearby!'

# Truncate previous line if same event continues
if event in ColoredLoggingHandler.CONTINUOUS_EVENT_NAMES and self._last_event == event:
# Filling with "' ' * terminal_width" in order to completely clear last line
terminal_width = self._terminal_width()
if terminal_width:
sys.stdout.write('\r{}\r'.format(' ' * terminal_width))
else:
sys.stdout.write('\r')
else:
sys.stdout.write("\n")

color_name = None
if event in ColoredLoggingHandler.EVENT_COLOR_MAP:
color_name = ColoredLoggingHandler.EVENT_COLOR_MAP[event]

# Change color if necessary
if event == 'egg_hatched' and data.get('pokemon', 'error') == 'error':
# `egg_hatched` event will be dispatched in both cases: hatched pokemon info is successfully taken or not.
# change color from 'green' to 'red' in case of error.
color_name = 'red'

if color_name in ColoredLoggingHandler.COLOR_CODE:
sys.stdout.write(
'[{time}] \033[{color}m{message}\033[0m'.format(
time=time.strftime("%H:%M:%S"),
color=ColoredLoggingHandler.COLOR_CODE[color_name],
message=message
)
)
else:
sys.stdout.write('[{time}] {message}'.format(
time=time.strftime("%H:%M:%S"),
message=message
))

sys.stdout.flush()
self._last_event = event

def _terminal_width(self):
if self._ioctl is None or self._TIOCGWINSZ is None:
return None

h, w, hp, wp = struct.unpack('HHHH',
self._ioctl(0, self._TIOCGWINSZ,
struct.pack('HHHH', 0, 0, 0, 0)))
return w

8 comments on commit 9f9146c

@binaryn3xus
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fantastic. Like the feature! Good work!

@refast
Copy link

@refast refast commented on 9f9146c Aug 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally! Thanks a lot!

@ljieyao
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@tadejv
Copy link

@tadejv tadejv commented on 9f9146c Aug 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to have a color 'none' which would result in the event not being printed? Having all of these in a config file, so each can personalize based on his priorities (I for example don't care about moving or each time a ball is thrown, but am interested in when there is an error, a VIP pokemon, or a pokemon is captured). This is a very small effort for a very useful functionality :)

@douglascamata
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tadejv per task log configuration is coming later, this is way too hacky.

@tadejv
Copy link

@tadejv tadejv commented on 9f9146c Aug 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is this more hacky than the config that we are using now?
A hacky feature (2 lines of code: one to load EVENT_COLOR_MAP from file, and one if that checks - i just did this locally) while waiting for proper implementation is better than nothing. I could even argue that this is less hacky than hard-coding the colors in code.
About hackyness:

  • did you notice that the old handler is used at start and end, and this is used in between?
  • It also keeps writing this line: "[20:32:15] {'distance_unit': '', 'distance': '', 'current_position': "

Feels like double standards if you refuse a 2 line edit that makes the log much more readable and personalisable, but you accept a hack that is the current color handler :)

@douglascamata
Copy link
Member

@douglascamata douglascamata commented on 9f9146c Aug 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tadejv I never use this handler and if it depended only on my opinion, we wouldn't have this handler in the code at all, because I think the implementation is really bad. This is a desperate cry for people to have story-like logs, but logs aren't supposed to be a story that even a 7 years old can read. But this is a community, not my personal project.

Adding a 2 line edit here won't work for every single event handler and will make us repeat code. Then people will see such specific code in the handler and will start to code more like that. Also when the time comes for me to implement per task logging configuration there will be so much hacky code that I'll lose lots of time to migrate it.

So, YES, we will delay the feature to avoid hacky code and to improve the LoggingHandler as much as possible. As I said, the user will be able to set in config.json if he wants a task's log to be ignored or to log to a file. This will work using Python's very powerful and customizable logging module, which this handler doesn't use at all, and it will be wonderful and beautifully implemented to avoid any problems with code quality.

So this handler will be always lacking features and having bugs because it implements thing in its own way instead of using the standard way of doing stuff.

I'll make things better for you, with good code, don't worry.

@Vvkmnn
Copy link

@Vvkmnn Vvkmnn commented on 9f9146c Aug 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thanks for bringing this back bud.

Please sign in to comment.