Do you support APM link tracking for Websocket?

Kibana version:
v7.4.2

Elasticsearch version:
v7.4.2

APM Server version:
v7.4.2

APM Agent language and version:
apm-agent-go v1.5.0

Question:
I am using net/http and github.com/gorilla/websocket for websocket development, but I find that as long as I upgrade websocket, I cannot collect data and link information of the API interface on APM.

Why is that, thank you

@axw :grinning:Can you help me, thank you.

Hi @EDDYCJY!

The Go agent does not support WebSockets, so you will have to add some custom code.

How are you using WebSockets? What are you expecting the agent to do?

@axw This demo is probably consistent with the address: https://github.com/gorilla/websocket/blob/master/examples/echo/server.go#L22-L42.

I hope to see at least every link invocation in APM.

So you want to trace from ReadRequest to WriteMessage? e.g. you might have a connection session that goes like this:

  1. Client connects
  2. Client sends message, waits for response
    2.1. Server receives message
    2.2. Server does some work...
    2.3. Server sends response
  3. Repeat step 2
  4. Client disconnects

If that's the case then you could trace it using apm.StartTransaction, like this:

func echo(w http.ResponseWriter, r *http.Request) {
    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        return
    }
    defer c.Close()

    for {
        if err := handleMessage(c); err != nil {
            break
        }
    }
}

func handleMessage(c *websocket.Conn) error {
    mt, message, err := c.ReadMessage()
    if err != nil {
        break
    }
    tx := apm.DefaultTracer.StartTransaction("echo", "request")
    defer tx.End()
    // Do some work...
    return c.WriteMessage(mt, message)
}

If you want distributed tracing, originating from the WebSocket client, that's more difficult. You can't rely on the HTTP headers because those are for the original HTTP request, and not related to each individual message sent to the WebSocket. Instead you would have to add the trace context to the messages you send over the WebSocket.

@axw I also thought about this solution, and it can be solved, but the problem is that I can't find a place where apm.DefaultTracer can set the context. In this way, how can I ensure that it can be connected to other tracing (HTTP, MySQL ...) in the request?

--

I might make other HTTP, SQL calls in the handleMessage method.

You will need to create a new context object when you start a transaction (receive a message), and add the transaction to it using apm.ContextWithTransaction. Like this:

func handleMessage(c *websocket.Conn) error {
    mt, message, err := c.ReadMessage()
    if err != nil {
        break
    }
    tx := apm.DefaultTracer.StartTransaction("echo", "request")
    defer tx.End()
    // Create a new context for the transaction
    ctx := apm.ContextWithTransaction(context.Background(), tx)
    // Do some work...
    rows, err := db.QueryContext(ctx, "SELECT ...")
    // Do some more work...
    return c.WriteMessage(mt, message)
}
1 Like

Thanks @axw for the reply, I think I need to take a closer look at godoc and redesign my program ...

I had been looking at the documentation https://www.elastic.co/guide/en/apm/agent/go/current/instrumenting-source.html before, but I found that he didn't seem to be complete enough that I understood it wrong.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.