package mongodb import ( "log" "net/url" "time" "github.com/influxdata/telegraf" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) type Server struct { Url *url.URL Session *mgo.Session lastResult *MongoStatus } func (s *Server) getDefaultTags() map[string]string { tags := make(map[string]string) tags["hostname"] = s.Url.Host return tags } type oplogEntry struct { Timestamp bson.MongoTimestamp `bson:"ts"` } func (s *Server) gatherOplogStats() *OplogStats { stats := &OplogStats{} localdb := s.Session.DB("local") op_first := oplogEntry{} op_last := oplogEntry{} query := bson.M{"ts": bson.M{"$exists": true}} for _, collection_name := range []string{"oplog.rs", "oplog.$main"} { if err := localdb.C(collection_name).Find(query).Sort("$natural").Limit(1).One(&op_first); err != nil { if err == mgo.ErrNotFound { continue } log.Println("E! Error getting first oplog entry (" + err.Error() + ")") return stats } if err := localdb.C(collection_name).Find(query).Sort("-$natural").Limit(1).One(&op_last); err != nil { if err == mgo.ErrNotFound { continue } log.Println("E! Error getting last oplog entry (" + err.Error() + ")") return stats } } op_first_time := time.Unix(int64(op_first.Timestamp>>32), 0) op_last_time := time.Unix(int64(op_last.Timestamp>>32), 0) stats.TimeDiff = int64(op_last_time.Sub(op_first_time).Seconds()) return stats } func (s *Server) gatherData(acc telegraf.Accumulator, gatherDbStats bool) error { s.Session.SetMode(mgo.Eventual, true) s.Session.SetSocketTimeout(0) result_server := &ServerStatus{} err := s.Session.DB("admin").Run(bson.D{ { Name: "serverStatus", Value: 1, }, { Name: "recordStats", Value: 0, }, }, result_server) if err != nil { return err } result_repl := &ReplSetStatus{} // ignore error because it simply indicates that the db is not a member // in a replica set, which is fine. _ = s.Session.DB("admin").Run(bson.D{ { Name: "replSetGetStatus", Value: 1, }, }, result_repl) jumbo_chunks, _ := s.Session.DB("config").C("chunks").Find(bson.M{"jumbo": true}).Count() result_cluster := &ClusterStatus{ JumboChunksCount: int64(jumbo_chunks), } resultShards := &ShardStats{} err = s.Session.DB("admin").Run(bson.D{ { Name: "shardConnPoolStats", Value: 1, }, }, &resultShards) if err != nil { log.Println("E! Error getting database shard stats (" + err.Error() + ")") } oplogStats := s.gatherOplogStats() result_db_stats := &DbStats{} if gatherDbStats == true { names := []string{} names, err = s.Session.DatabaseNames() if err != nil { log.Println("E! Error getting database names (" + err.Error() + ")") } for _, db_name := range names { db_stat_line := &DbStatsData{} err = s.Session.DB(db_name).Run(bson.D{ { Name: "dbStats", Value: 1, }, }, db_stat_line) if err != nil { log.Println("E! Error getting db stats from " + db_name + "(" + err.Error() + ")") } db := &Db{ Name: db_name, DbStatsData: db_stat_line, } result_db_stats.Dbs = append(result_db_stats.Dbs, *db) } } result := &MongoStatus{ ServerStatus: result_server, ReplSetStatus: result_repl, ClusterStatus: result_cluster, DbStats: result_db_stats, ShardStats: resultShards, OplogStats: oplogStats, } defer func() { s.lastResult = result }() result.SampleTime = time.Now() if s.lastResult != nil && result != nil { duration := result.SampleTime.Sub(s.lastResult.SampleTime) durationInSeconds := int64(duration.Seconds()) if durationInSeconds == 0 { durationInSeconds = 1 } data := NewMongodbData( NewStatLine(*s.lastResult, *result, s.Url.Host, true, durationInSeconds), s.getDefaultTags(), ) data.AddDefaultStats() data.AddDbStats() data.AddShardHostStats() data.flush(acc) } return nil }