diff --git a/ChangeLog.md b/ChangeLog.md
index 997dd2006..7f3dd3c58 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -35,6 +35,7 @@ New Features / Enhancements:
* #291 - compile templates into `nsqadmin` binary
* #285/#288 - `nsq_tail` support for `-n #` to get recent # messages
* #287/#294 - nsqadmin support added for showing client attributes (sample rate, TLS, compression)
+ * #189/#296 - add client user agent to nsqadmin
### 0.2.24 - 2013-12-07
diff --git a/nsqadmin/templates/channel.html.go b/nsqadmin/templates/channel.html.go
index a604d31e0..02d5bce32 100644
--- a/nsqadmin/templates/channel.html.go
+++ b/nsqadmin/templates/channel.html.go
@@ -201,7 +201,7 @@ func init() {
{{range .ChannelStats.Clients}}
{{.ClientIdentifier}} |
- {{.ClientVersion}} |
+ {{.ClientVersion}} {{if ne .ClientUserAgent ""}}({{.ClientUserAgent}}){{end}} |
{{if gt .SampleRate 0}}
Sampled {{.SampleRate}}%
diff --git a/nsqadmin/templates/node.html.go b/nsqadmin/templates/node.html.go
index 9e306d4fb..2313ea3b5 100644
--- a/nsqadmin/templates/node.html.go
+++ b/nsqadmin/templates/node.html.go
@@ -128,6 +128,7 @@ func init() {
| |
Client Host |
Protocol |
+ Attributes |
NSQd Host |
In-Flight |
Ready Count |
@@ -140,7 +141,21 @@ func init() {
|
{{.ClientIdentifier}} |
- {{.ClientVersion}} |
+ {{.ClientVersion}} {{if ne .ClientUserAgent ""}}({{.ClientUserAgent}}){{end}} |
+
+ {{if gt .SampleRate 0}}
+ Sampled {{.SampleRate}}%
+ {{end}}
+ {{if .TLS}}
+ TLS
+ {{end}}
+ {{if .Deflate}}
+ Delfate
+ {{end}}
+ {{if .Snappy}}
+ Snappy
+ {{end}}
+ |
{{.HostAddress}} |
{{.InFlightCount | commafy}} |
{{.ReadyCount | commafy}} |
diff --git a/nsqd/client_v2.go b/nsqd/client_v2.go
index 094593397..e1845e11d 100644
--- a/nsqd/client_v2.go
+++ b/nsqd/client_v2.go
@@ -29,6 +29,7 @@ type IdentifyDataV2 struct {
DeflateLevel int `json:"deflate_level"`
Snappy bool `json:"snappy"`
SampleRate int32 `json:"sample_rate"`
+ UserAgent string `json:"user_agent"`
}
type ClientV2 struct {
@@ -42,8 +43,9 @@ type ClientV2 struct {
sync.Mutex
- ID int64
- context *Context
+ ID int64
+ context *Context
+ UserAgent string
// original connection
net.Conn
@@ -135,6 +137,7 @@ func (c *ClientV2) String() string {
func (c *ClientV2) Identify(data IdentifyDataV2) error {
c.ShortIdentifier = data.ShortId
c.LongIdentifier = data.LongId
+ c.UserAgent = data.UserAgent
err := c.SetHeartbeatInterval(data.HeartbeatInterval)
if err != nil {
return err
@@ -155,6 +158,7 @@ func (c *ClientV2) Stats() ClientStats {
Version: "V2",
RemoteAddress: c.RemoteAddr().String(),
Name: c.ShortIdentifier,
+ UserAgent: c.UserAgent,
State: atomic.LoadInt32(&c.State),
ReadyCount: atomic.LoadInt64(&c.ReadyCount),
InFlightCount: atomic.LoadInt64(&c.InFlightCount),
diff --git a/nsqd/stats.go b/nsqd/stats.go
index ab8b8c82a..a8903dca5 100644
--- a/nsqd/stats.go
+++ b/nsqd/stats.go
@@ -76,6 +76,7 @@ type ClientStats struct {
TLS bool `json:"tls"`
Deflate bool `json:"deflate"`
Snappy bool `json:"snappy"`
+ UserAgent string `json:"user_agent"`
}
type Topics []*Topic
diff --git a/nsqd/stats_test.go b/nsqd/stats_test.go
index 1cf9e968f..ee5c37293 100644
--- a/nsqd/stats_test.go
+++ b/nsqd/stats_test.go
@@ -1,8 +1,12 @@
package main
import (
+ "encoding/json"
+ "fmt"
"github.com/bitly/go-nsq"
+ "github.com/bitly/nsq/util"
"github.com/bmizerany/assert"
+ "github.com/mreiferson/go-snappystream"
"io/ioutil"
"log"
"os"
@@ -36,3 +40,50 @@ func TestStats(t *testing.T) {
assert.Equal(t, len(stats[0].Channels[0].Clients), 1)
log.Printf("stats: %+v", stats)
}
+
+func TestClientAttributes(t *testing.T) {
+ log.SetOutput(ioutil.Discard)
+ defer log.SetOutput(os.Stdout)
+
+ userAgent := "Test User Agent"
+
+ *verbose = true
+ options := NewNSQDOptions()
+ options.SnappyEnabled = true
+ tcpAddr, httpAddr, nsqd := mustStartNSQD(options)
+ defer nsqd.Exit()
+
+ conn, err := mustConnectNSQD(tcpAddr)
+ assert.Equal(t, err, nil)
+
+ data := identifyFeatureNegotiation(t, conn, map[string]interface{}{"snappy": true, "user_agent": userAgent})
+ r := struct {
+ Snappy bool `json:"snappy"`
+ UserAgent string `json:"user_agent"`
+ }{}
+ err = json.Unmarshal(data, &r)
+ assert.Equal(t, err, nil)
+ assert.Equal(t, r.Snappy, true)
+
+ compressConn := snappystream.NewReader(conn, snappystream.SkipVerifyChecksum)
+
+ w := snappystream.NewWriter(conn)
+
+ rw := readWriter{compressConn, w}
+
+ topicName := "test_client_attributes" + strconv.Itoa(int(time.Now().Unix()))
+ sub(t, rw, topicName, "ch")
+
+ err = nsq.Ready(1).Write(rw)
+ assert.Equal(t, err, nil)
+
+ testUrl := fmt.Sprintf("http://127.0.0.1:%d/stats?format=json", httpAddr.Port)
+
+ statsData, err := util.ApiRequest(testUrl)
+ if err != nil {
+ t.Fatalf(err.Error())
+ }
+ client := statsData.Get("topics").GetIndex(0).Get("channels").GetIndex(0).Get("clients").GetIndex(0)
+ assert.Equal(t, client.Get("user_agent").MustString(), userAgent)
+ assert.Equal(t, client.Get("snappy").MustBool(), true)
+}
diff --git a/util/lookupd/lookupd.go b/util/lookupd/lookupd.go
index 535490b6b..ee95ba690 100644
--- a/util/lookupd/lookupd.go
+++ b/util/lookupd/lookupd.go
@@ -411,8 +411,9 @@ func GetNSQDStats(nsqdHTTPAddrs []string, selectedTopic string) ([]*TopicStats,
connectedDuration := time.Now().Sub(connected).Seconds()
clientInfo := &ClientInfo{
- HostAddress: addr,
- ClientVersion: client.Get("version").MustString(),
+ HostAddress: addr,
+ ClientVersion: client.Get("version").MustString(),
+ ClientUserAgent: client.Get("user_agent").MustString(),
ClientIdentifier: fmt.Sprintf("%s:%s", client.Get("name").MustString(),
strings.Split(client.Get("remote_address").MustString(), ":")[1]),
ConnectedDuration: time.Duration(int64(connectedDuration)) * time.Second, // truncate to second
diff --git a/util/lookupd/statsinfo.go b/util/lookupd/statsinfo.go
index 5f93b281c..124f2d97c 100644
--- a/util/lookupd/statsinfo.go
+++ b/util/lookupd/statsinfo.go
@@ -155,6 +155,7 @@ func (c *ChannelStats) Host() string {
type ClientInfo struct {
HostAddress string
ClientVersion string
+ ClientUserAgent string
ClientIdentifier string
ConnectedDuration time.Duration
InFlightCount int