Skip to content

Commit c15f6ff

Browse files
authored
Merge pull request #8 from CLSFramework/improve_logger_and_debug
Improve logger and debug
2 parents 0594534 + c9f1215 commit c15f6ff

File tree

4 files changed

+164
-45
lines changed

4 files changed

+164
-45
lines changed

server.py

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,33 @@
44
import service_pb2 as pb2
55
from typing import Union
66
from multiprocessing import Manager, Lock
7+
from utils.logger_utils import setup_logger
78
import logging
89
import grpc
910
import argparse
11+
import datetime
1012

1113

12-
logging.basicConfig(level=logging.DEBUG)
14+
console_logging_level = logging.INFO
15+
file_logging_level = logging.DEBUG
16+
17+
main_logger = None
18+
log_dir = None
19+
1320

1421
class GrpcAgent:
15-
def __init__(self, agent_type, uniform_number) -> None:
22+
def __init__(self, agent_type, uniform_number, logger) -> None:
1623
self.agent_type: pb2.AgentType = agent_type
1724
self.uniform_number: int = uniform_number
1825
self.server_params: Union[pb2.ServerParam, None] = None
1926
self.player_params: Union[pb2.PlayerParam, None] = None
2027
self.player_types: dict[int, pb2.PlayerType] = {}
2128
self.debug_mode: bool = False
29+
self.logger: logging.Logger = logger
2230

2331
def GetAction(self, state: pb2.State):
32+
self.logger.debug(f"================================= cycle={state.world_model.cycle}.{state.world_model.stoped_cycle} =================================")
33+
# self.logger.debug(f"State: {state}")
2434
if self.agent_type == pb2.AgentType.PlayerT:
2535
return self.GetPlayerActions(state)
2636
elif self.agent_type == pb2.AgentType.CoachT:
@@ -50,8 +60,19 @@ def GetPlayerActions(self, state: pb2.State):
5060
actions.append(pb2.PlayerAction(helios_basic_move=pb2.HeliosBasicMove()))
5161
else:
5262
actions.append(pb2.PlayerAction(helios_set_play=pb2.HeliosSetPlay()))
63+
64+
self.logger.debug(f"Actions: {actions}")
5365
return pb2.PlayerActions(actions=actions)
5466

67+
def GetBestPlannerAction(self, pairs: pb2.BestPlannerActionRequest):
68+
self.logger.debug(f"GetBestPlannerAction cycle:{pairs.state.world_model.cycle} pairs:{len(pairs.pairs)} unum:{pairs.state.register_response.uniform_number}")
69+
pairs_list: list[int, pb2.RpcActionState] = [(k, v) for k, v in pairs.pairs.items()]
70+
pairs_list.sort(key=lambda x: x[0])
71+
best_action = max(pairs_list, key=lambda x: -1000 if x[1].action.parent_index != -1 else x[1].predict_state.ball_position.x)
72+
self.logger.debug(f"Best action: {best_action[0]} {best_action[1].action.description} to {best_action[1].action.target_unum} in ({round(best_action[1].action.target_point.x, 2)},{round(best_action[1].action.target_point.y, 2)}) e:{round(best_action[1].evaluation,2)}")
73+
res = pb2.BestPlannerActionResponse(index=best_action[0])
74+
return res
75+
5576
def GetCoachActions(self, state: pb2.State):
5677
actions = []
5778
actions.append(pb2.CoachAction(do_helios_substitute=pb2.DoHeliosSubstitute()))
@@ -74,6 +95,21 @@ def GetTrainerActions(self, state: pb2.State):
7495
)
7596
)
7697
return pb2.TrainerActions(actions=actions)
98+
99+
def SetServerParams(self, server_params: pb2.ServerParam):
100+
self.logger.debug(f"Server params received unum {server_params.register_response.uniform_number}")
101+
# self.logger.debug(f"Server params: {server_params}")
102+
self.server_params = server_params
103+
104+
def SetPlayerParams(self, player_params: pb2.PlayerParam):
105+
self.logger.debug(f"Player params received unum {player_params.register_response.uniform_number}")
106+
# self.logger.debug(f"Player params: {player_params}")
107+
self.player_params = player_params
108+
109+
def SetPlayerType(self, player_type: pb2.PlayerType):
110+
self.logger.debug(f"Player type received unum {player_type.register_response.uniform_number}")
111+
# self.logger.debug(f"Player type: {player_type}")
112+
self.player_types[player_type.id] = player_type
77113

78114
class GameHandler(pb2_grpc.GameServicer):
79115
def __init__(self, shared_lock, shared_number_of_connections) -> None:
@@ -82,97 +118,101 @@ def __init__(self, shared_lock, shared_number_of_connections) -> None:
82118
self.shared_number_of_connections = shared_number_of_connections
83119

84120
def GetPlayerActions(self, state: pb2.State, context):
85-
logging.debug(f"GetPlayerActions unum {state.register_response.uniform_number} at {state.world_model.cycle}")
121+
main_logger.debug(f"GetPlayerActions unum {state.register_response.uniform_number} at {state.world_model.cycle}")
86122
res = self.agents[state.register_response.client_id].GetAction(state)
87-
logging.debug(f"GetPlayerActions Done unum {res}")
88123
return res
89124

90125
def GetCoachActions(self, state: pb2.State, context):
91-
logging.debug(f"GetCoachActions coach at {state.world_model.cycle}")
126+
main_logger.debug(f"GetCoachActions coach at {state.world_model.cycle}")
92127
res = self.agents[state.register_response.client_id].GetAction(state)
93128
return res
94129

95130
def GetTrainerActions(self, state: pb2.State, context):
96-
logging.debug(f"GetTrainerActions trainer at {state.world_model.cycle}")
131+
main_logger.debug(f"GetTrainerActions trainer at {state.world_model.cycle}")
97132
res = self.agents[state.register_response.client_id].GetAction(state)
98133
return res
99134

100135
def SendServerParams(self, serverParams: pb2.ServerParam, context):
101-
logging.debug(f"Server params received unum {serverParams.register_response.uniform_number}")
102-
self.agents[serverParams.register_response.client_id].server_params = serverParams
136+
main_logger.debug(f"Server params received unum {serverParams.register_response.uniform_number}")
137+
self.agents[serverParams.register_response.client_id].SetServerParams(serverParams)
103138
res = pb2.Empty()
104139
return res
105140

106141
def SendPlayerParams(self, playerParams: pb2.PlayerParam, context):
107-
logging.debug(f"Player params received unum {playerParams.register_response.uniform_number}")
108-
self.agents[playerParams.register_response.client_id].player_params = playerParams
142+
main_logger.debug(f"Player params received unum {playerParams.register_response.uniform_number}")
143+
self.agents[playerParams.register_response.client_id].SetPlayerParams(playerParams)
109144
res = pb2.Empty()
110145
return res
111146

112147
def SendPlayerType(self, playerType: pb2.PlayerType, context):
113-
logging.debug(f"Player type received unum {playerType.register_response.uniform_number}")
114-
self.agents[playerType.register_response.client_id].player_types[playerType.id] = playerType
148+
main_logger.debug(f"Player type received unum {playerType.register_response.uniform_number}")
149+
self.agents[playerType.register_response.client_id].SetPlayerType(playerType)
115150
res = pb2.Empty()
116151
return res
117152

118153
def SendInitMessage(self, initMessage: pb2.InitMessage, context):
119-
logging.debug(f"Init message received unum {initMessage.register_response.uniform_number}")
154+
main_logger.debug(f"Init message received unum {initMessage.register_response.uniform_number}")
120155
self.agents[initMessage.register_response.client_id].debug_mode = initMessage.debug_mode
121156
res = pb2.Empty()
122157
return res
123158

124159
def Register(self, register_request: pb2.RegisterRequest, context):
125-
logging.debug(f"received register request from team_name: {register_request.team_name} "
126-
f"unum: {register_request.uniform_number} "
127-
f"agent_type: {register_request.agent_type}")
128160
with self.shared_lock:
161+
main_logger.info(f"received register request from team_name: {register_request.team_name} "
162+
f"unum: {register_request.uniform_number} "
163+
f"agent_type: {register_request.agent_type}")
129164
self.shared_number_of_connections.value += 1
130-
logging.debug(f"Number of connections {self.shared_number_of_connections.value}")
165+
main_logger.info(f"Number of connections {self.shared_number_of_connections.value}")
131166
team_name = register_request.team_name
132167
uniform_number = register_request.uniform_number
133168
agent_type = register_request.agent_type
134-
self.agents[self.shared_number_of_connections.value] = GrpcAgent(agent_type, uniform_number)
135-
res = pb2.RegisterResponse(client_id=self.shared_number_of_connections.value,
169+
register_response = pb2.RegisterResponse(client_id=self.shared_number_of_connections.value,
136170
team_name=team_name,
137171
uniform_number=uniform_number,
138172
agent_type=agent_type)
139-
return res
173+
logger = setup_logger(f"agent{register_response.uniform_number}_{register_response.client_id}", log_dir)
174+
self.agents[self.shared_number_of_connections.value] = GrpcAgent(agent_type, uniform_number, logger)
175+
return register_response
140176

141177
def SendByeCommand(self, register_response: pb2.RegisterResponse, context):
142-
logging.debug(f"Bye command received unum {register_response.uniform_number}")
178+
main_logger.debug(f"Bye command received unum {register_response.uniform_number}")
143179
# with shared_lock:
144180
self.agents.pop(register_response.client_id)
145181

146182
res = pb2.Empty()
147183
return res
148184

149185
def GetBestPlannerAction(self, pairs: pb2.BestPlannerActionRequest, context):
150-
logging.debug(f"GetBestPlannerAction cycle:{pairs.state.world_model.cycle} pairs:{len(pairs.pairs)} unum:{pairs.state.register_response.uniform_number}")
151-
pairs_list: list[int, pb2.RpcActionState] = [(k, v) for k, v in pairs.pairs.items()]
152-
pairs_list.sort(key=lambda x: x[0])
153-
best_action = max(pairs_list, key=lambda x: -1000 if x[1].action.parent_index != -1 else x[1].predict_state.ball_position.x)
154-
logging.debug(f"Best action: {best_action[0]} {best_action[1].action.description} to {best_action[1].action.target_unum} in ({round(best_action[1].action.target_point.x, 2)},{round(best_action[1].action.target_point.y, 2)}) e:{round(best_action[1].evaluation,2)}")
155-
res = pb2.BestPlannerActionResponse(index=best_action[0])
186+
main_logger.debug(f"GetBestPlannerAction cycle:{pairs.state.world_model.cycle} pairs:{len(pairs.pairs)} unum:{pairs.state.register_response.uniform_number}")
187+
res = self.agents[pairs.state.register_response.client_id].GetBestPlannerAction(pairs)
156188
return res
189+
157190

158191
def serve(port, shared_lock, shared_number_of_connections):
159192
server = grpc.server(futures.ThreadPoolExecutor(max_workers=22))
160193
game_service = GameHandler(shared_lock, shared_number_of_connections)
161194
pb2_grpc.add_GameServicer_to_server(game_service, server)
162195
server.add_insecure_port(f'[::]:{port}')
163196
server.start()
164-
logging.info(f"Starting server on port {port}")
197+
main_logger.info(f"Starting server on port {port}")
165198

166199
server.wait_for_termination()
167200

168201

169202
def main():
170-
manager = Manager()
171-
shared_lock = Lock() # Create a Lock for synchronization
172-
shared_number_of_connections = manager.Value('i', 0)
203+
global main_logger, log_dir
173204
parser = argparse.ArgumentParser(description='Run play maker server')
174205
parser.add_argument('-p', '--rpc-port', required=False, help='The port of the server', default=50051)
206+
parser.add_argument('-l', '--log-dir', required=False, help='The directory of the log file',
207+
default=f'logs/{datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}')
175208
args = parser.parse_args()
209+
log_dir = args.log_dir
210+
main_logger = setup_logger("pmservice", log_dir, console_level=console_logging_level, file_level=file_logging_level)
211+
main_logger.info("Starting server")
212+
manager = Manager()
213+
shared_lock = Lock() # Create a Lock for synchronization
214+
shared_number_of_connections = manager.Value('i', 0)
215+
176216
serve(args.rpc_port, shared_lock, shared_number_of_connections)
177217

178218
if __name__ == '__main__':

start-team.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,19 @@
55
import logging
66
import argparse
77
import check_requirements
8+
from utils.logger_utils import setup_logger
9+
import datetime
810

911

1012
# Set up logging
11-
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
13+
log_dir = os.path.join(os.getcwd(), 'logs', datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))
14+
start_team_logger = setup_logger('start-team', log_dir, console_level=logging.DEBUG, file_level=logging.DEBUG, console_format_str='%(message)s')
15+
1216

1317
def run_server_script(args):
1418
# Start the server.py script as a new process group
1519
process = subprocess.Popen(
16-
['python3', 'server.py', '--rpc-port', args.rpc_port],
20+
['python3', 'server.py', '--rpc-port', args.rpc_port, '--log-dir', log_dir],
1721
preexec_fn=os.setsid, # Create a new session and set the process group ID
1822
stdout=subprocess.PIPE,
1923
stderr=subprocess.STDOUT # Capture stderr and redirect it to stdout
@@ -23,20 +27,28 @@ def run_server_script(args):
2327
def run_start_script(args):
2428
# Start the start.sh script in its own directory as a new process group
2529
process = subprocess.Popen(
26-
['bash', 'start.sh', '-t', args.team_name, '--rpc-port', args.rpc_port, '--rpc-type', 'grpc'],
30+
['bash', 'start.sh' if not args.debug else 'start-debug.sh', '-t', args.team_name, '--rpc-port', args.rpc_port, '--rpc-type', 'grpc'],
2731
cwd='scripts/proxy', # Corrected directory to where start.sh is located
2832
preexec_fn=os.setsid, # Create a new session and set the process group ID
2933
stdout=subprocess.PIPE,
3034
stderr=subprocess.STDOUT # Capture stderr and redirect it to stdout
3135
)
3236
return process
3337

34-
def stream_output(process, prefix):
38+
def stream_output_to_console(process, prefix):
3539
# Stream output from the process and log it with a prefix
3640
for line in iter(process.stdout.readline, b''):
37-
logging.debug(f'{prefix} {line.decode().strip()}')
41+
start_team_logger.debug(f'{prefix} {line.decode().strip()}')
3842
process.stdout.close()
3943

44+
def stream_output_to_file(process, prefix):
45+
# Stream output from the process and log it with a prefix
46+
logger = setup_logger(prefix, log_dir, console_level=None, file_level=logging.DEBUG)
47+
for line in iter(process.stdout.readline, b''):
48+
logger.info(line.decode().strip())
49+
pass
50+
process.stdout.close()
51+
4052
def kill_process_group(process):
4153
try:
4254
os.killpg(os.getpgid(process.pid), signal.SIGTERM) # Send SIGTERM to the process group
@@ -48,24 +60,25 @@ def kill_process_group(process):
4860
parser = argparse.ArgumentParser(description='Run server and team scripts.')
4961
parser.add_argument('-t', '--team_name', required=False, help='The name of the team', default='CLS')
5062
parser.add_argument('--rpc-port', required=False, help='The port of the server', default='50051')
63+
parser.add_argument('-d', '--debug', required=False, help='Enable debug mode', default=False, action='store_true')
5164
args = parser.parse_args()
52-
65+
5366
try:
5467
# Check Python requirements
55-
logging.debug("Checking Python requirements...")
68+
start_team_logger.debug("Checking Python requirements...")
5669
check_requirements.check_requirements()
5770

5871
# Run the server.py script first
5972
server_process = run_server_script(args)
60-
logging.debug(f"Started server.py process with PID: {server_process.pid}")
73+
start_team_logger.debug(f"Started server.py process with PID: {server_process.pid}")
6174

6275
# Run the start.sh script after server.py with the given arguments
6376
start_process = run_start_script(args)
64-
logging.debug(f"Started start.sh process with PID: {start_process.pid} with team name {args=}")
77+
start_team_logger.debug(f"Started start.sh process with PID: {start_process.pid} with team name {args=}")
6578

6679
# Monitor both processes and log their outputs
67-
server_thread = threading.Thread(target=stream_output, args=(server_process, 'server:'))
68-
start_thread = threading.Thread(target=stream_output, args=(start_process, 'team:'))
80+
server_thread = threading.Thread(target=stream_output_to_console, args=(server_process, 'server'))
81+
start_thread = threading.Thread(target=stream_output_to_file, args=(start_process, 'proxy'))
6982

7083
server_thread.start()
7184
start_thread.start()
@@ -75,7 +88,7 @@ def kill_process_group(process):
7588
start_thread.join()
7689

7790
except KeyboardInterrupt:
78-
logging.debug("Interrupted! Killing all processes.")
91+
start_team_logger.debug("Interrupted! Killing all processes.")
7992
kill_process_group(server_process)
8093
kill_process_group(start_process)
8194

start-team.sh

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
#!/bin/bash
22

3+
# create a log directory with the current date and time
4+
log_dir="logs/$(date +'%Y-%m-%d_%H-%M-%S')"
5+
if [ ! -d $log_dir ]; then
6+
mkdir -p $log_dir
7+
fi
8+
9+
abs_log_dir_path=$(realpath $log_dir)
10+
311
# Ensure the script exits if any command fails
412
set -e
513
# check scripts/proxy directory does not exist, raise error
@@ -10,6 +18,7 @@ fi
1018

1119
team_name="CLS"
1220
rpc_port=50051
21+
debug=false
1322

1423
# help function
1524
usage() {
@@ -31,6 +40,9 @@ do
3140
rpc_port=$2
3241
shift
3342
;;
43+
-d|--debug)
44+
debug=true
45+
;;
3446
*)
3547
echo 1>&2
3648
echo "invalid option \"${1}\"." 1>&2
@@ -49,7 +61,7 @@ python3 check_requirements.py
4961

5062
# Start server.py in the background
5163
echo "Starting server.py..."
52-
python3 server.py --rpc-port $rpc_port &
64+
python3 server.py --rpc-port $rpc_port --log-dir $abs_log_dir_path &
5365
server_pid=$!
5466

5567
# Function to kill server and team processes on exit
@@ -67,8 +79,14 @@ sleep 2
6779

6880
# Start start.sh script in the correct directory with arguments
6981
echo "Starting start.sh with team name: $team_name and ..."
82+
83+
start_log_path="$abs_log_dir_path/proxy.log"
7084
cd scripts/proxy
71-
bash start.sh -t "$team_name" --rpc-port $rpc_port --rpc-type grpc &
85+
if [ "$debug" = true ]; then
86+
bash start-debug.sh -t "$team_name" --rpc-port $rpc_port --rpc-type grpc >> $start_log_path 2>&1 &
87+
else
88+
bash start.sh -t "$team_name" --rpc-port $rpc_port --rpc-type grpc >> $start_log_path 2>&1 &
89+
fi
7290
start_pid=$!
7391

7492
# Wait for both background processes to finish

0 commit comments

Comments
 (0)