Browse Source

Implemented stats API.

pull/3/head
Simon Eisenmann 12 years ago
parent
commit
a4a815cc06
  1. 1
      server.conf.in
  2. 19
      src/app/spreed-speakfreely-server/api.go
  3. 43
      src/app/spreed-speakfreely-server/hub.go
  4. 16
      src/app/spreed-speakfreely-server/main.go
  5. 2
      src/app/spreed-speakfreely-server/rooms.go
  6. 82
      src/app/spreed-speakfreely-server/stats.go

1
server.conf.in

@ -7,6 +7,7 @@ listen = 127.0.0.1:8080 @@ -7,6 +7,7 @@ listen = 127.0.0.1:8080
#writetimeout = 10
#basePath = /some/sub/path/ # Set this when running behind a web server under a sub path.
#maxfd = 32768 # Try to set max open files limit on start (works only when run as root).
#stats = true # Provide stats API at /api/v1/stats (do not enable this in production or unprotected!).
[app]
#title = Spreed Speak Freely

19
src/app/spreed-speakfreely-server/api.go

@ -20,11 +20,7 @@ @@ -20,11 +20,7 @@
*/
package main
import (
"app/spreed-speakfreely-server/sleepy"
"github.com/gorilla/mux"
"github.com/strukturag/httputils"
)
import ()
type ApiError struct {
Id string `json:"code"`
@ -35,16 +31,3 @@ type ApiError struct { @@ -35,16 +31,3 @@ type ApiError struct {
func NewApiError(id, message string) *ApiError {
return &ApiError{id, message, false}
}
func makeApiHandler(r *mux.Router, tokenProvider TokenProvider) {
a := r.PathPrefix("/api/v1/").Subrouter()
api := sleepy.NewAPI(a)
rooms := &Rooms{}
api.AddResource(rooms, "/rooms")
tokens := &Tokens{tokenProvider}
api.AddResourceWithWrapper(tokens, httputils.MakeGzipHandler, "/tokens")
}

43
src/app/spreed-speakfreely-server/hub.go

@ -45,6 +45,16 @@ type MessageRequest struct { @@ -45,6 +45,16 @@ type MessageRequest struct {
Id string
}
type HubStat struct {
Rooms int `json:"rooms"`
Connections int `json:"connections"`
Users int `json:"users"`
Count uint64 `json:"count"`
IdsInRoom map[string][]string `json:"idsinroom,omitempty"`
UsersById map[string]*DataUser `json:"usersbyid,omitempty"`
ConnectionsByIdx map[string]string `json:"connectionsbyidx,omitempty"`
}
type Hub struct {
server *Server
connectionTable map[string]*Connection
@ -76,6 +86,39 @@ func NewHub(version string, config *Config, sessionSecret string, turnSecret str @@ -76,6 +86,39 @@ func NewHub(version string, config *Config, sessionSecret string, turnSecret str
}
func (h *Hub) Stat(details bool) *HubStat {
h.mutex.RLock()
defer h.mutex.RUnlock()
stat := &HubStat{
Rooms: len(h.roomTable),
Connections: len(h.connectionTable),
Users: len(h.userTable),
Count: h.count,
}
if details {
rooms := make(map[string][]string)
for roomid, room := range h.roomTable {
users := make([]string, 0, len(room.connections))
for id, _ := range room.connections {
users = append(users, id)
}
rooms[roomid] = users
}
stat.IdsInRoom = rooms
users := make(map[string]*DataUser)
for userid, user := range h.userTable {
users[userid] = user.Data()
}
stat.UsersById = users
connections := make(map[string]string)
for id, connection := range h.connectionTable {
connections[fmt.Sprintf("%d", connection.Idx)]=id
}
stat.ConnectionsByIdx = connections
}
return stat
}
func (h *Hub) CreateTurnData(id string) *DataTurn {
// Create turn data credentials for shared secret auth with TURN

16
src/app/spreed-speakfreely-server/main.go

@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
package main
import (
"app/spreed-speakfreely-server/sleepy"
"flag"
"fmt"
"github.com/gorilla/mux"
@ -147,6 +148,11 @@ func runner(runtime phoenix.Runtime) error { @@ -147,6 +148,11 @@ func runner(runtime phoenix.Runtime) error {
log.Printf("Using '%s' base base path.", basePath)
}
statsEnabled, err := runtime.GetBool("http", "stats")
if err != nil {
statsEnabled = false
}
sessionSecret, err := runtime.GetString("app", "sessionSecret")
if err != nil {
return fmt.Errorf("No sessionSecret in config file.")
@ -294,7 +300,15 @@ func runner(runtime phoenix.Runtime) error { @@ -294,7 +300,15 @@ func runner(runtime phoenix.Runtime) error {
r.Handle("/favicon.ico", http.StripPrefix(basePath, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img")))))
r.Handle("/ws", makeWsHubHandler(hub))
r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler))
makeApiHandler(r, tokenProvider)
// Add API end points.
api := sleepy.NewAPI(r.PathPrefix("/api/v1/").Subrouter())
api.AddResource(&Rooms{}, "/rooms")
api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens")
if statsEnabled {
api.AddResourceWithWrapper(&Stats{hub: hub}, httputils.MakeGzipHandler, "/stats")
log.Println("Stats are enabled!")
}
// Add extra/static support if configured and exists.
if extraFolder != "" {

2
src/app/spreed-speakfreely-server/rooms.go

@ -33,7 +33,7 @@ type Room struct { @@ -33,7 +33,7 @@ type Room struct {
type Rooms struct {
}
func (rooms Rooms) Post(r *http.Request) (int, interface{}) {
func (rooms *Rooms) Post(r *http.Request) (int, interface{}) {
name := RandomString(11)
return 200, &Room{name, fmt.Sprintf("/%s", name)}

82
src/app/spreed-speakfreely-server/stats.go

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
/*
* Spreed Speak Freely.
* Copyright (C) 2013-2014 struktur AG
*
* This file is part of Spreed Speak Freely.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package main
import (
"net/http"
"runtime"
"time"
)
type Stat struct {
details bool
Runtime *RuntimeStat `json:"runtime"`
Hub *HubStat `json:"hub"`
}
func NewStat(details bool, h *Hub) *Stat {
stat := &Stat{
details: details,
Runtime: &RuntimeStat{},
Hub: h.Stat(details),
}
stat.Runtime.Read()
return stat
}
type RuntimeStat struct {
Goroutines float64 `json:"goroutines"`
Alloc float64 `json:"alloc"`
Mallocs float64 `json:"mallocs"`
Frees float64 `json:"frees"`
Pauses float64 `json:"pauses"`
Heap float64 `json:"heap"`
Stack float64 `json:"stack"`
}
func (stat *RuntimeStat) Read() {
memStats := &runtime.MemStats{}
runtime.ReadMemStats(memStats)
stat.Goroutines = float64(runtime.NumGoroutine())
stat.Alloc = float64(memStats.Alloc)
stat.Mallocs = float64(memStats.Mallocs)
stat.Frees = float64(memStats.Frees)
stat.Pauses = float64(memStats.PauseTotalNs) / float64(time.Millisecond)
stat.Heap = float64(memStats.HeapAlloc)
stat.Stack = float64(memStats.StackInuse)
}
type Stats struct {
hub *Hub
}
func (stats *Stats) Get(r *http.Request) (int, interface{}) {
r.ParseForm()
details := r.FormValue("details") == "1"
stat := NewStat(details, stats.hub)
return 200, stat
}
Loading…
Cancel
Save