I'd suggest breaking up your design into pieces as follows
First, build a communication contract between the clients and the servers. The client should use an abstraction of this contract and not worry that it is actually communicating over a websocket.
Next, build an implementation of the contract(s) to communicate over a websocket. This could use a decorator pattern, and decorate the implementation with two items.
- Step 1: log the call
- Step 2: call to the web socket
This way, you can test and design the call to the web socket separately from how the client uses the contract. Further, you can build only one "log the call" decorator for every one of your contract implementations. More use of less code is a good thing :).
While you could build a logging decorator in any language, here is a link to a python logging decorator library to give you an idea of what I mean if you're curious.
More on Step 2
If you have some sort of handshake protocol where one contract will require several websocket calls and receives, then there is another question to consider around flow control.
- Blocking? Should the contract be blocking until all calls are completed, or
- Asynchronous? Should the client be able to access the contract in an asynchronous way, or
- Promise based? Should the contract is promise based? (popular for web-based clients)
If you log the call with the state at each step of your protocol, you can easily track the state of your handshake.