// Satisfy the LineFormatter interface. Formats the StatLines as a grid. func (glf *GridLineFormatter) FormatLines(lines []StatLine, index int, discover bool) string { buf := &bytes.Buffer{} // Automatically turn on discover-style formatting if more than one host's // output is being displayed (to include things like hostname column) discover = discover || len(lines) > 1 if discover { glf.Writer.WriteCell(" ") } lineFlags := getLineFlags(lines) // Sort the stat lines by hostname, so that we see the output // in the same order for each snapshot sort.Sort(StatLines(lines)) // Print the columns that are enabled for _, header := range StatHeaders { maskedAttrs := lineFlags & header.ActivateFlags // Only show the header if this column has the "Always" flag, or all // other flags for this column are matched if (maskedAttrs&Always == 0) && maskedAttrs != header.ActivateFlags { continue } // Don't write any cell content for blank headers, since they act as placeholders if len(header.HeaderText) > 0 { glf.Writer.WriteCell(header.HeaderText) } } glf.Writer.EndRow() for _, line := range lines { mmap := line.StorageEngine == "mmapv1" if discover { glf.Writer.WriteCell(line.Key) } if line.Error != nil { glf.Writer.Feed(line.Error.Error()) continue } // Write the opcount columns (always active) glf.Writer.WriteCell(formatOpcount(line.Insert, line.InsertR, false)) glf.Writer.WriteCell(formatOpcount(line.Query, line.QueryR, false)) glf.Writer.WriteCell(formatOpcount(line.Update, line.UpdateR, false)) glf.Writer.WriteCell(formatOpcount(line.Delete, line.DeleteR, false)) glf.Writer.WriteCell(fmt.Sprintf("%v", line.GetMore)) glf.Writer.WriteCell(formatOpcount(line.Command, line.CommandR, true)) if lineFlags&WTOnly > 0 { if line.CacheDirtyPercent < 0 { glf.Writer.WriteCell("") } else { glf.Writer.WriteCell(fmt.Sprintf("%.1f", line.CacheDirtyPercent*100)) } if line.CacheUsedPercent < 0 { glf.Writer.WriteCell("") } else { glf.Writer.WriteCell(fmt.Sprintf("%.1f", line.CacheUsedPercent*100)) } } glf.Writer.WriteCell(fmt.Sprintf("%v", line.Flushes)) // Columns for flushes + mapped only show up if mmap columns are active if lineFlags&MMAPOnly > 0 { if line.Mapped > 0 { glf.Writer.WriteCell(text.FormatMegabyteAmount(int64(line.Mapped))) } else { //for mongos nodes, Mapped is empty, so write a blank cell. glf.Writer.WriteCell("") } } // Columns for Virtual and Resident are always active glf.Writer.WriteCell(text.FormatMegabyteAmount(int64(line.Virtual))) glf.Writer.WriteCell(text.FormatMegabyteAmount(int64(line.Resident))) if lineFlags&MMAPOnly > 0 { if lineFlags&AllOnly > 0 { nonMappedVal := "" if line.NonMapped >= 0 { // not mongos, update accordingly nonMappedVal = text.FormatMegabyteAmount(int64(line.NonMapped)) } glf.Writer.WriteCell(nonMappedVal) } if mmap { glf.Writer.WriteCell(fmt.Sprintf("%v", line.Faults)) } else { glf.Writer.WriteCell("n/a") } } if lineFlags&MMAPOnly > 0 && lineFlags&AllOnly > 0 { // check if we have any locks if lineFlags&Locks <= 0 { if line.CollectionLocks != nil && !line.IsMongos { percentCell := fmt.Sprintf("%.1f%%|%.1f%%", line.CollectionLocks.ReadAcquireWaitsPercentage, line.CollectionLocks.WriteAcquireWaitsPercentage) glf.Writer.WriteCell(percentCell) timeCell := fmt.Sprintf("%v|%v", line.CollectionLocks.ReadAcquireTimeMicros, line.CollectionLocks.WriteAcquireTimeMicros) glf.Writer.WriteCell(timeCell) } else { //don't write any lock status for mongos nodes glf.Writer.WriteCell("") glf.Writer.WriteCell("") } } else { // no locks glf.Writer.WriteCell("n/a") glf.Writer.WriteCell("n/a") } } // Write columns related to lock % if activated if lineFlags&Locks > 0 { if line.HighestLocked != nil && !line.IsMongos { lockCell := fmt.Sprintf("%v:%.1f", line.HighestLocked.DBName, line.HighestLocked.Percentage) + "%" glf.Writer.WriteCell(lockCell) } else { //don't write any lock status for mongos nodes glf.Writer.WriteCell("") } } glf.Writer.WriteCell(fmt.Sprintf("%v|%v", line.QueuedReaders, line.QueuedWriters)) glf.Writer.WriteCell(fmt.Sprintf("%v|%v", line.ActiveReaders, line.ActiveWriters)) glf.Writer.WriteCell(text.FormatBits(line.NetIn)) glf.Writer.WriteCell(text.FormatBits(line.NetOut)) glf.Writer.WriteCell(fmt.Sprintf("%v", line.NumConnections)) if discover || lineFlags&Repl > 0 { //only show these fields when in discover or repl mode. glf.Writer.WriteCell(line.ReplSetName) glf.Writer.WriteCell(line.NodeType) } glf.Writer.WriteCell(fmt.Sprintf("%v", line.Time.Format("2006-01-02T15:04:05Z07:00"))) glf.Writer.EndRow() } glf.Writer.Flush(buf) // clear the flushed data glf.Writer.Reset() returnVal := buf.String() if !glf.IncludeHeader || index%glf.HeaderInterval != 0 { // Strip out the first line of the formatted output, // which contains the headers. They've been left in up until this point // in order to force the formatting of the columns to be wide enough. firstNewLinePos := strings.Index(returnVal, "\n") if firstNewLinePos >= 0 { returnVal = returnVal[firstNewLinePos+1:] } } if len(lines) > 1 { // For multi-node stats, add an extra newline to tell each block apart return "\n" + returnVal } return returnVal }
// Satisfy the LineFormatter interface. Formats the StatLines as JSON. func (jlf *JSONLineFormatter) FormatLines(lines []StatLine, index int, discover bool) string { lineFlags := getLineFlags(lines) // middle ground b/t the StatLines and the JSON string to be returned jsonFormat := map[string]interface{}{} // convert each StatLine to JSON for _, line := range lines { // each line can just be a string->string map (header->value) lineJson := map[string]string{} // check for error if line.Error != nil { lineJson["error"] = line.Error.Error() jsonFormat[line.Key] = lineJson continue } // put all the appropriate values into the stat line's JSON representation lineJson["insert"] = formatOpcount(line.Insert, line.InsertR, false) lineJson["query"] = formatOpcount(line.Query, line.QueryR, false) lineJson["update"] = formatOpcount(line.Update, line.UpdateR, false) lineJson["delete"] = formatOpcount(line.Delete, line.DeleteR, false) lineJson["getmore"] = fmt.Sprintf("%v", line.GetMore) lineJson["command"] = formatOpcount(line.Command, line.CommandR, true) lineJson["netIn"] = text.FormatBits(line.NetIn) lineJson["netOut"] = text.FormatBits(line.NetOut) lineJson["conn"] = fmt.Sprintf("%v", line.NumConnections) lineJson["time"] = fmt.Sprintf("%v", line.Time.Format("15:04:05")) lineJson["host"] = line.Host lineJson["vsize"] = text.FormatMegabyteAmount(int64(line.Virtual)) lineJson["res"] = text.FormatMegabyteAmount(int64(line.Resident)) // add mmapv1-specific fields if lineFlags&MMAPOnly > 0 { lineJson["flushes"] = fmt.Sprintf("%v", line.Flushes) lineJson["qr|qw"] = fmt.Sprintf("%v|%v", line.QueuedReaders, line.QueuedWriters) lineJson["ar|aw"] = fmt.Sprintf("%v|%v", line.ActiveReaders, line.ActiveWriters) mappedVal := "" // empty for mongos if line.Mapped > 0 { // not mongos, update accordingly mappedVal = text.FormatMegabyteAmount(int64(line.Mapped)) } lineJson["mapped"] = mappedVal nonMappedVal := "" // empty for mongos if line.NonMapped >= 0 { // not mongos, update accordingly nonMappedVal = text.FormatMegabyteAmount(int64(line.NonMapped)) } lineJson["non-mapped"] = nonMappedVal lineJson["faults"] = fmt.Sprintf("%v", line.Faults) if lineFlags&AllOnly > 0 { // check if we have any locks if lineFlags&Locks <= 0 { if line.CollectionLocks != nil && !line.IsMongos { lineJson["lr|lw %"] = fmt.Sprintf("%.1f%%|%.1f%%", line.CollectionLocks.ReadAcquireWaitsPercentage, line.CollectionLocks.WriteAcquireWaitsPercentage) lineJson["lrt|lwt"] = fmt.Sprintf("%v|%v", line.CollectionLocks.ReadAcquireTimeMicros, line.CollectionLocks.WriteAcquireTimeMicros) } } } highestLockedVal := "" // empty for mongos if line.HighestLocked != nil && !line.IsMongos { highestLockedVal = fmt.Sprintf("%v:%.1f%%", line.HighestLocked.DBName, line.HighestLocked.Percentage) } lineJson["locked"] = highestLockedVal } if lineFlags&Repl > 0 { lineJson["set"] = line.ReplSetName lineJson["repl"] = line.NodeType } // add the line to the final json jsonFormat[line.Host] = lineJson } // convert the JSON format of the lines to a json string to be returned linesAsJsonBytes, err := json.Marshal(jsonFormat) if err != nil { return fmt.Sprintf(`{"json error": "%v"}`, err.Error()) } return string(linesAsJsonBytes) + "\n" }