Skip to content

Commit

Permalink
Added '/status' endpoint for exposing debug data.
Browse files Browse the repository at this point in the history
  • Loading branch information
aforge committed Jun 22, 2021
1 parent 85e0ad1 commit 55e8030
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 98 deletions.
60 changes: 60 additions & 0 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"sort"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -366,3 +368,61 @@ func authHttpRequest(req *http.Request) (types.Uid, []byte, error) {
}
return uid, nil, nil
}

func serveStatus(wrt http.ResponseWriter, req *http.Request) {
// Disable caching.
wrt.Header().Set("Cache-Control", "no-cache, no-store, private, max-age=0")
wrt.Header().Set("Expires", time.Unix(0, 0).Format(http.TimeFormat))
wrt.Header().Set("Pragma", "no-cache")
wrt.Header().Set("X-Accel-Expires", "0")

fmt.Fprintf(wrt, "<html><head>")
fmt.Fprintf(wrt, "<title>Server Status</title>")
fmt.Fprintf(wrt, "<style>tr:nth-child(even) { background: #f2f2f2 }</style>")
fmt.Fprintf(wrt, "</head>")

now := time.Now()
fmt.Fprintf(wrt, "<body>")
fmt.Fprintf(wrt, "<h1>%s</h1><div>Version: %s, build: %s. Present time: %s</div>", "Tinode Server", currentVersion, buildstamp, now.Format(time.RFC1123))
fmt.Fprintf(wrt, "<a href='/status'>Update</a>")
fmt.Fprintf(wrt, "<br><hr>")

fmt.Fprintf(wrt, "<div>Sessions: %d</div>", len(globals.sessionStore.sessCache))
fmt.Fprintf(wrt, "<table>")
fmt.Fprintf(wrt, "<tr><th>remoteAddr</th><th>UA</th><th>uid</th><th>sid</th><th>clnode</th><th>subs</th></tr>")
globals.sessionStore.Range(func(sid string, s *Session) bool {
keys := make([]string, 0, len(s.subs))
for tn := range s.subs {
keys = append(keys, tn)
}
sort.Strings(keys)
l := strings.Join(keys, ",")
fmt.Fprintf(wrt, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%+v</td><td>%s</td></tr>", s.remoteAddr, s.userAgent, s.uid.String(), sid, s.clnode, l)
return true
})
fmt.Fprintf(wrt, "</table>")
// If this server is a part of a cluster.
if globals.cluster != nil {
fmt.Fprintf(wrt, "<div>Cluster node: %s", globals.cluster.thisNodeName)
fmt.Fprintf(wrt, "</div>")
}

fmt.Fprint(wrt, "<br><hr>")
fmt.Fprintf(wrt, "<div>")
fmt.Fprintf(wrt, "<div>Topics: %d</div>", globals.hub.numTopics)
fmt.Fprintf(wrt, "<table>")

fmt.Fprintf(wrt, "<tr><th>topicId</th><th>xorig</th><th>isProxy</th><th>cat</th><th>perUser</th><th>perSubs</th><th>sessions</th></tr>")
globals.hub.topics.Range(func(_, t interface{}) bool {
topic := t.(*Topic)
var sd []string
for s, psd := range topic.sessions {
sd = append(sd, fmt.Sprintf("%s: %+v", s.sid, psd))
}
fmt.Fprintf(wrt, "<tr><td>%s</td><td>%s</td><td>%t</td><td>%d</td><td>%+v</td><td>%+v</td><td>%s</td></tr>", topic.name, topic.xoriginal, topic.isProxy, topic.cat, topic.perUser, topic.perSubs, strings.Join(sd, " "))
return true
})
fmt.Fprintf(wrt, "</div>")

fmt.Fprintf(wrt, "</body></html>")
}
5 changes: 5 additions & 0 deletions server/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ type Hub struct {
// Topics must be indexed by name
topics *sync.Map

// Current number of loaded topics
numTopics int

// Channel for routing messages between topics, buffered at 4096
route chan *ServerComMessage

Expand Down Expand Up @@ -105,10 +108,12 @@ func (h *Hub) topicGet(name string) *Topic {
}

func (h *Hub) topicPut(name string, t *Topic) {
h.numTopics++
h.topics.Store(name, t)
}

func (h *Hub) topicDel(name string) {
h.numTopics--
h.topics.Delete(name)
}

Expand Down
84 changes: 1 addition & 83 deletions server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"os"
"runtime"
"runtime/pprof"
"sort"
"strings"
"time"

Expand Down Expand Up @@ -253,88 +252,6 @@ type configType struct {
Media *mediaConfig `json:"media"`
}

// Session debug info.
type DebugSession struct {
RemoteAddr string `json:"remote_addr,omitempty"`
Ua string `json:"ua,omitempty"`
Uid string `json:"uid,omitempty"`
Sid string `json:"sid,omitempty"`
Clnode string `json:"clnode,omitempty"`
Subs []string `json:"subs,omitempty"`
}

// Topic debug info.
type DebugTopic struct {
Topic string `json:"topic,omitempty"`
Xorig string `json:"xorig,omitempty"`
IsProxy bool `json:"is_proxy,omitempty"`
Cat int `json:"cat,omitempty"`
PerUser []string `json:"per_user,omitempty"`
PerSubs []string `json:"per_subs,omitempty"`
Sessions []string `json:"sessions,omitempty"`
}

// Server internal state dump for debugging.
type DebugDump struct {
Sessions []DebugSession `json:"sessions,omitempty"`
Topics []DebugTopic `json:"topics,omitempty"`
}

func makeDebugDump() *DebugDump {
result := &DebugDump{
Sessions: make([]DebugSession, 0, len(globals.sessionStore.sessCache)),
Topics: make([]DebugTopic, 0, 10),
}
// Sessions.
globals.sessionStore.Range(func(sid string, s *Session) bool {
keys := make([]string, 0, len(s.subs))
for tn := range s.subs {
keys = append(keys, tn)
}
sort.Strings(keys)
var clnode string
if s.clnode != nil {
clnode = s.clnode.name
}
result.Sessions = append(result.Sessions, DebugSession{
RemoteAddr: s.remoteAddr,
Ua: s.userAgent,
Uid: s.uid.String(),
Sid: sid,
Clnode: clnode,
Subs: keys,
})
return true
})
// Topics.
globals.hub.topics.Range(func(_, t interface{}) bool {
topic := t.(*Topic)
psd := make([]string, 0, len(topic.sessions))
for s := range topic.sessions {
psd = append(psd, s.sid)
}
pud := make([]string, 0, len(topic.perUser))
for uid := range topic.perUser {
pud = append(pud, uid.String())
}
ps := make([]string, 0, len(topic.perSubs))
for key := range topic.perSubs {
ps = append(ps, key)
}
result.Topics = append(result.Topics, DebugTopic{
Topic: topic.name,
Xorig: topic.xoriginal,
IsProxy: topic.isProxy,
Cat: int(topic.cat),
PerUser: pud,
PerSubs: ps,
Sessions: psd,
})
return true
})
return result
}

func main() {
executable, _ := os.Executable()

Expand Down Expand Up @@ -699,6 +616,7 @@ func main() {
}
}
logs.Info.Printf("API served from root URL path '%s'", config.ApiPath)
mux.HandleFunc("/status", serveStatus)

// Handle websocket clients.
mux.HandleFunc(config.ApiPath+"v0/channels", serveWebSocket)
Expand Down
5 changes: 0 additions & 5 deletions server/topic.go
Original file line number Diff line number Diff line change
Expand Up @@ -1901,11 +1901,6 @@ func (t *Topic) replyGetDesc(sess *Session, asUid types.Uid, asChan bool, opts *
}
}

if t.cat == types.TopicCatSys && sess.authLvl == auth.LevelRoot && !sess.isCluster() {
// Only serve debug data for non-cluster requests.
desc.Private = makeDebugDump()
}

sess.queueOut(&ServerComMessage{
Meta: &MsgServerMeta{
Id: id,
Expand Down
11 changes: 1 addition & 10 deletions server/topic_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"net/http"
"time"

"github.com/tinode/chat/server/auth"
"github.com/tinode/chat/server/logs"
"github.com/tinode/chat/server/store/types"
)
Expand Down Expand Up @@ -172,10 +171,6 @@ func (t *Topic) proxyMasterResponse(msg *ClusterResp, killTimer *time.Timer) {
}
switch msg.OrigReqType {
case ProxyReqJoin:
// If it's a sub.desc request in 'sys' topic.
if sess != nil && sess.authLvl == auth.LevelRoot && t.cat == types.TopicCatSys && msg.SrvMsg.Meta != nil && msg.SrvMsg.Meta.Desc != nil {
msg.SrvMsg.Meta.Desc.Private = makeDebugDump()
}
if sess != nil && msg.SrvMsg.Ctrl != nil {
// TODO: do we need to let the master topic know that the subscription is not longer valid
// or is it already informed by the session when it terminated?
Expand All @@ -201,12 +196,8 @@ func (t *Topic) proxyMasterResponse(msg *ClusterResp, killTimer *time.Timer) {
killTimer.Reset(keepAlive)
}
}
case ProxyReqBroadcast:
case ProxyReqBroadcast, ProxyReqMeta:
// no processing
case ProxyReqMeta:
if sess != nil && sess.authLvl == auth.LevelRoot && t.cat == types.TopicCatSys && msg.SrvMsg.Meta != nil && msg.SrvMsg.Meta.Desc != nil {
msg.SrvMsg.Meta.Desc.Private = makeDebugDump()
}
case ProxyReqLeave:
if msg.SrvMsg != nil && msg.SrvMsg.Ctrl != nil {
if msg.SrvMsg.Ctrl.Code < 300 {
Expand Down

0 comments on commit 55e8030

Please sign in to comment.