181 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| package mongodb
 | |
| 
 | |
| import (
 | |
| 	"log"
 | |
| 	"net/url"
 | |
| 	"strings"
 | |
| 	"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 IsAuthorization(err error) bool {
 | |
| 	return strings.Contains(err.Error(), "not authorized")
 | |
| }
 | |
| 
 | |
| 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
 | |
| 			}
 | |
| 			if IsAuthorization(err) {
 | |
| 				log.Println("D! Error getting first oplog entry (" + err.Error() + ")")
 | |
| 			} else {
 | |
| 				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 || IsAuthorization(err) {
 | |
| 				continue
 | |
| 			}
 | |
| 			if IsAuthorization(err) {
 | |
| 				log.Println("D! Error getting first oplog entry (" + err.Error() + ")")
 | |
| 			} else {
 | |
| 				log.Println("E! Error getting first 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 {
 | |
| 		if IsAuthorization(err) {
 | |
| 			log.Println("D! Error getting database shard stats (" + err.Error() + ")")
 | |
| 		} else {
 | |
| 			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
 | |
| }
 |