Issue
I am trying to add a new websocket connection to my app (I already have a few and none of them had this issue).
The idea is to send logs to my spring boot application from my reactjs frontend.
Here's my client's code:
connectToLoggingWebsocket = ip => {
if (this.props.loggingWebsocketConnected === false) {
loggingStompClient = Stomp.client(`${this.props.environment.webSocketUrl}/logging`);
loggingStompClient.connect({ name: ip }, frame => this.loggingStompSuccessCallBack(frame, loggingStompClient), err => this.loggingStompClientFailureCallBack(err, ip));
}
}
loggingStompSuccessCallBack = (frame, stompClient) => {
console.log(`Connected: ${frame}`);
this.props.setLoggingWebsocketConnected(true);
stompClient.subscribe(LOGGING_NODE, data => {
console.log(data);
});
}
loggingStompClientFailureCallBack = (error, ip) => {
console.log('Exception: ', error);
console.log('Trying to reconnect in 10 seconds');
this.props.setLoggingWebsocketConnected(false);
setTimeout(() => this.connectToLoggingWebsocket(ip), 10000);
}
/* THIS IS OUTSIDE THE CLASS SCOPE */
export const sendLog = log => {
console.log("TRYING TO SEND")
console.log(loggingStompClient);
if (loggingStompClient) {
loggingStompClient.send("/app/logging", log.message);
}
};
And I am using winston -> it calls the sendLog method via a custom Transport
class CustomTransport extends Transport {
log = (info) => {
console.log("SENDING...")
sendToWebsocket(info);
}
}
// Using transport
const transport = new CustomTransport({});
// Create a logger and consume an instance of your transport
export const logger = winston.createLogger({
level: 'info',
transports: [ transport,
new winston.transports.Console() ]
});
const sendToWebsocket = message => {
console.log(message);
sendLog(message);
}
My sendLog
method is NOT in my React class because I have to export it for winston. loggingStompClient
is also not in my class since sendLog
is calling send
off of it.
var loggingStompClient = null;
class Key extends React.Component {
componentDidMount = () => { ... }
...
}
My service looks like this - I am basically sending something from the browser that invokes receivedLog
-> it then sends a message to the browser - this works exactly ONCE
@MessageMapping("/logging")
public void receivedLog(String log) {
this.template.convertAndSend("/topic/logging", "logging");
logger.info("Received: {}", log);
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/logging").setAllowedOrigins("*");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay(MESSAGE_PREFIX, "queue", "/amq/").setRelayHost(rabbitmqUri)
.setSystemLogin(rabbitmqUsername).setClientLogin(rabbitmqUsername).setClientPasscode(rabbitmqPassword)
.setSystemPasscode(rabbitmqPassword).setAutoStartup(true)
.setSystemHeartbeatReceiveInterval(10000)
.setSystemHeartbeatSendInterval(10000);
registry.setApplicationDestinationPrefixes("/app");
}
Here's my browser's output:
CONNECT
name:127.0.0.1
accept-version:1.0
heart-beat:10000,10000
----------------------
CONNECTED
server:RabbitMQ/3.7.13
session:session-9l1yyKAGsRZlWwKfOFAqKg
heart-beat:10000,10000
version:1.0
user-name:127.0.0.1
----------------------
SUBSCRIBE
id:sub-1661872200224-169
destination:/topic/logging
----------------------
SEND
destination:/app/logging
content-length:63
ADDING TO ITEM: 6a5dd06d-7a66-4048-91db-a74525f87252
----------------------
MESSAGE
subscription:sub-1661872200224-169
destination:/topic/logging
message-id:T_sub-1661872200224-169@@session-9l1yyKAGsRZlWwKfOFAqKg@@1
redelivered:false
content-type:text/plain;charset=UTF-8
content-length:7
logging
-----------------------
ERROR
message:Connection to broker closed.
content-length:0
It also sends a bunch of PONG
messages and never receives a PING
I've been sitting on this all day and I can't figure it out. Also turned everything off and on again.
Thanks!
EDIT
if I set the following the websocket doesn't disconnect. I am also able to continuously send messages to my frontend, but I still can only send ONCE from my frontend to the service.
loggingStompClient.heartbeat.outgoing = 0;
loggingStompClient.heartbeat.incoming = 0;
This method somehow gets called only ONCE.
export const sendLog = log => {
console.log("TRYING TO SEND")
console.log(loggingStompClient);
if (loggingStompClient) {
loggingStompClient.send("/app/logging", log.message);
}
};
Could it be that my winston logger works only once? I tried binding my websocket send method to a button and spring boot does register the messages.
So am I using winston incorrectly? It does log to console but my custom transport's callback is not being called.
const sendToWebsocket = message => {
console.error('SEND TO WEBSOCKET')
sendLog(message);
};
class CustomTransport extends Transport {
log = info => sendToWebsocket(info);
}
// Using transport
const transport = new CustomTransport({
name: 'custom-transport',
handleExceptions: true
});
// Create a logger and consume an instance of your transport
export const logger = createLogger({
exitOnError: false,
level: 'info',
transports: [ transport,
new transports.Console({
name: 'console',
handleExceptions: true
}) ]
});
Solution
In the documentation there is this callback()
method which I thought was a placeholder for my own callback. I removed it and added my callback -> this was the problem.
Do NOT omit callback()
Now my code looks like this and it works.
Answered By - Claim
Answer Checked By - Gilberto Lyons (JavaFixing Admin)