Repository navigation
Debugging
- A handler never fires, or fires with the wrong event name, and you cannot tell why just from reading your own code.
-
AskorConnectseems to hang. - You want to see what neffos actually does when it builds your handlers, before shipping.
func EnableDebug(printer Printer)
func Debugf(format string, args ...any)
type Printer interface {
Printf(format string, args ...any)
}
type PrinterFunc func(format string, args ...any)EnableDebug turns on debug logging for the whole process; there is no way to turn it back off at runtime. printer is anything with a Printf(string, ...any) method: a standard *log.Logger and a kataras/golog logger both qualify. Pass nil and neffos falls back to its own log.Logger prefixed "| neffos |". For a logger with another method name, or for log/slog, wrap a function in neffos.PrinterFunc:
import "github.com/kataras/neffos"
func main() {
neffos.EnableDebug(nil) // or EnableDebug(log.Default()), or your own logger
// slog has no Printf; adapt it.
neffos.EnableDebug(neffos.PrinterFunc(func(format string, args ...any) {
slog.Debug(fmt.Sprintf(format, args...))
}))
// ...
}Before v0.1.0 EnableDebug took an any and looked for Debugf, Logf or Printf at runtime; a value with none of them silently fell back to the default logger. The typed parameter makes that a compile error instead.
EnableDebug is meant to cover the build state of event handlers: the one-time reflection work NewStruct does when it turns your controller's methods into events. That happens once, when the Struct (or ConnHandler) is built, not per connection or per message, so turning on debug has no runtime cost for a plain Events/Namespaces handler. You will see lines like:
| neffos | Event ["Chat"] is handled by [main.lobby.OnChat] method
| neffos | Set namespace ["chat"] from method [main.lobby.Namespace]
| neffos | Field [main.lobby.Topic] marked as static on value [be kind, no spoilers]
One exception, worth knowing about if you use Scale out: the bundled redis and nats StackExchange implementations also call neffos.Debugf at runtime, for subscribe/unsubscribe failures, publish failures and reconnect events on the broker connection. If you enable debug on a server that uses either backend, you will see occasional log lines from live traffic too, not only from startup.
Most "why didn't this handler fire" questions are answered faster by knowing what actually went out, not by more logging. See Architecture for the full frame layout (<wait>;<namespace>;<room>;<event>;<isError>;<isNoOp>;<body>) and the ack handshake; a packet capture or a fmt.Printf("%q", payload) in a wrapped Socket (see Upgraders and Dialers, "Wrapping a socket") next to this reference usually settles it.
| Symptom | Likely cause | Where to look |
|---|---|---|
A handler under OnNamespaceConnected never runs |
The other side never called Connect for that namespace; OnNamespaceConnected only fires after a successful connect handshake, it is not fired for free. |
Namespaces |
msg.Err (or the returned error) is neffos.ErrBadNamespace
|
You sent to, or tried to connect to, a namespace the other side never declared in its Events/Namespaces/WithTimeout. Check both sides' handler maps for a typo in the namespace string. |
Namespaces, Errors |
Ask (or Connect, JoinRoom, ...) never returns |
A deadline-less context (context.TODO(), context.Background()) and a remote that never replies. It does return as soon as the connection closes, it just never does on its own without one of those. Always pass a context from context.WithTimeout. |
[[The ask method |
A connection closes with neffos.ErrMessageTooBig on the receiver (CloseStatus is -1 there; the sender sees a real close frame, CloseStatus 1009) |
WithTimeout.MaxMessageSize (or Struct.SetMaxMessageSize) is set lower than a legitimate message. Raise it, or reject the oversized payload in your own handler before it gets that big. |
Timeouts, [[Close codes and shutdown |
| A connection closes on its own after a period of inactivity, with a timeout error, even though both sides are alive |
WithTimeout.PingInterval (or Struct.SetPingInterval) is shorter than the network's real round-trip time, so a pong cannot arrive before the next ping's deadline. Raise the interval. |
Timeouts |
- Architecture, the wire format and the dispatch/reader goroutines this all runs on
-
Errors, the full error-classification helpers (
IsTimeoutError,IsDisconnectError,IsCloseError) -
Timeouts,
ReadTimeout/WriteTimeout/PingInterval/MaxMessageSizein full
Home | About | Project | Getting Started | Technical Docs | Copyright © 2019-2026 Gerasimos Maropoulos. Documentation terms of use.
Getting started
Concepts
Messaging
Production
Scale out