Search code examples
reactjsspring-bootwebsocketstompwinston

Winston logger's CustomTransport's callback is called only once


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

enter image description here

It also sends a bunch of PONG messages and never receives a PING enter image description here

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
    }) ]
});

enter image description here enter image description here


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()

    enter image description here

    Now my code looks like this and it works.

    enter image description here