github.com/netdata/go.d.plugin@v0.58.1/modules/pika/collect_info.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package pika
     4  
     5  import (
     6  	"bufio"
     7  	"regexp"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/netdata/go.d.plugin/agent/module"
    12  )
    13  
    14  // https://github.com/Qihoo360/pika/blob/master/src/pika_admin.cc
    15  // https://github.com/Qihoo360/pika/blob/a0dbdcf5897dd7800ba8a4d1eafce1595619ddc8/src/pika_admin.cc#L694-L710
    16  
    17  const (
    18  	infoSectionServer           = "# Server"
    19  	infoSectionData             = "# Data"
    20  	infoSectionClients          = "# Clients"
    21  	infoSectionStats            = "# Stats"
    22  	infoSectionCommandExecCount = "# Command_Exec_Count"
    23  	infoSectionCPU              = "# CPU"
    24  	infoSectionReplMaster       = "# Replication(MASTER)"
    25  	infoSectionReplSlave        = "# Replication(SLAVE)"
    26  	infoSectionReplMasterSlave  = "# Replication(Master && SLAVE)"
    27  	infoSectionKeyspace         = "# Keyspace"
    28  )
    29  
    30  var infoSections = map[string]struct{}{
    31  	infoSectionServer:           {},
    32  	infoSectionData:             {},
    33  	infoSectionClients:          {},
    34  	infoSectionStats:            {},
    35  	infoSectionCommandExecCount: {},
    36  	infoSectionCPU:              {},
    37  	infoSectionReplMaster:       {},
    38  	infoSectionReplSlave:        {},
    39  	infoSectionReplMasterSlave:  {},
    40  	infoSectionKeyspace:         {},
    41  }
    42  
    43  func isInfoSection(line string) bool { _, ok := infoSections[line]; return ok }
    44  
    45  func (p *Pika) collectInfo(ms map[string]int64, info string) {
    46  	var curSection string
    47  
    48  	sc := bufio.NewScanner(strings.NewReader(info))
    49  	for sc.Scan() {
    50  		line := strings.TrimSpace(sc.Text())
    51  		if len(line) == 0 {
    52  			curSection = ""
    53  			continue
    54  		}
    55  		if strings.HasPrefix(line, "#") {
    56  			if isInfoSection(line) {
    57  				curSection = line
    58  			}
    59  			continue
    60  		}
    61  
    62  		field, value, ok := parseProperty(line)
    63  		if !ok {
    64  			continue
    65  		}
    66  
    67  		switch curSection {
    68  		case infoSectionCommandExecCount:
    69  			p.collectInfoCommandExecCountProperty(ms, field, value)
    70  		case infoSectionKeyspace:
    71  			p.collectInfoKeyspaceProperty(ms, field, value)
    72  		default:
    73  			collectNumericValue(ms, field, value)
    74  		}
    75  	}
    76  }
    77  
    78  var reKeyspaceValue = regexp.MustCompile(`^(.+)_keys=(\d+), expires=(\d+), invalid_keys=(\d+)`)
    79  
    80  func (p *Pika) collectInfoKeyspaceProperty(ms map[string]int64, field, value string) {
    81  	match := reKeyspaceValue.FindStringSubmatch(value)
    82  	if match == nil {
    83  		return
    84  	}
    85  
    86  	dataType, keys, expires, invalid := strings.ToLower(match[1]), match[2], match[3], match[4]
    87  	collectNumericValue(ms, field+"_"+dataType+"_keys", keys)
    88  	collectNumericValue(ms, field+"_"+dataType+"_expires_keys", expires)
    89  	collectNumericValue(ms, field+"_"+dataType+"_invalid_keys", invalid)
    90  
    91  	if !p.collectedDbs[field] {
    92  		p.collectedDbs[field] = true
    93  		p.addDbToKeyspaceCharts(field)
    94  	}
    95  }
    96  
    97  func (p *Pika) collectInfoCommandExecCountProperty(ms map[string]int64, field, value string) {
    98  	collectNumericValue(ms, "cmd_"+field+"_calls", value)
    99  
   100  	if !p.collectedCommands[field] {
   101  		p.collectedCommands[field] = true
   102  		p.addCmdToCommandsCharts(field)
   103  	}
   104  }
   105  
   106  func (p *Pika) addCmdToCommandsCharts(cmd string) {
   107  	p.addDimToChart(chartCommandsCalls.ID, &module.Dim{
   108  		ID:   "cmd_" + cmd + "_calls",
   109  		Name: cmd,
   110  		Algo: module.Incremental,
   111  	})
   112  }
   113  
   114  func (p *Pika) addDbToKeyspaceCharts(db string) {
   115  	p.addDimToChart(chartDbStringsKeys.ID, &module.Dim{
   116  		ID:   db + "_strings_keys",
   117  		Name: db,
   118  	})
   119  	p.addDimToChart(chartDbStringsExpiresKeys.ID, &module.Dim{
   120  		ID:   db + "_strings_expires_keys",
   121  		Name: db,
   122  	})
   123  	p.addDimToChart(chartDbStringsInvalidKeys.ID, &module.Dim{
   124  		ID:   db + "_strings_invalid_keys",
   125  		Name: db,
   126  	})
   127  
   128  	p.addDimToChart(chartDbHashesKeys.ID, &module.Dim{
   129  		ID:   db + "_hashes_keys",
   130  		Name: db,
   131  	})
   132  	p.addDimToChart(chartDbHashesExpiresKeys.ID, &module.Dim{
   133  		ID:   db + "_hashes_expires_keys",
   134  		Name: db,
   135  	})
   136  	p.addDimToChart(chartDbHashesInvalidKeys.ID, &module.Dim{
   137  		ID:   db + "_hashes_invalid_keys",
   138  		Name: db,
   139  	})
   140  
   141  	p.addDimToChart(chartDbListsKeys.ID, &module.Dim{
   142  		ID:   db + "_lists_keys",
   143  		Name: db,
   144  	})
   145  	p.addDimToChart(chartDbListsExpiresKeys.ID, &module.Dim{
   146  		ID:   db + "_lists_expires_keys",
   147  		Name: db,
   148  	})
   149  	p.addDimToChart(chartDbListsInvalidKeys.ID, &module.Dim{
   150  		ID:   db + "_lists_invalid_keys",
   151  		Name: db,
   152  	})
   153  
   154  	p.addDimToChart(chartDbZsetsKeys.ID, &module.Dim{
   155  		ID:   db + "_zsets_keys",
   156  		Name: db,
   157  	})
   158  	p.addDimToChart(chartDbZsetsExpiresKeys.ID, &module.Dim{
   159  		ID:   db + "_zsets_expires_keys",
   160  		Name: db,
   161  	})
   162  	p.addDimToChart(chartDbZsetsInvalidKeys.ID, &module.Dim{
   163  		ID:   db + "_zsets_invalid_keys",
   164  		Name: db,
   165  	})
   166  
   167  	p.addDimToChart(chartDbSetsKeys.ID, &module.Dim{
   168  		ID:   db + "_sets_keys",
   169  		Name: db,
   170  	})
   171  	p.addDimToChart(chartDbSetsExpiresKeys.ID, &module.Dim{
   172  		ID:   db + "_sets_expires_keys",
   173  		Name: db,
   174  	})
   175  	p.addDimToChart(chartDbSetsInvalidKeys.ID, &module.Dim{
   176  		ID:   db + "_sets_invalid_keys",
   177  		Name: db,
   178  	})
   179  }
   180  
   181  func (p *Pika) addDimToChart(chartID string, dim *module.Dim) {
   182  	chart := p.Charts().Get(chartID)
   183  	if chart == nil {
   184  		p.Warningf("error on adding '%s' dimension: can not find '%s' chart", dim.ID, chartID)
   185  		return
   186  	}
   187  	if err := chart.AddDim(dim); err != nil {
   188  		p.Warning(err)
   189  		return
   190  	}
   191  	chart.MarkNotCreated()
   192  }
   193  
   194  func parseProperty(prop string) (field, value string, ok bool) {
   195  	var sep byte
   196  	if strings.HasPrefix(prop, "db") {
   197  		sep = ' '
   198  	} else {
   199  		sep = ':'
   200  	}
   201  	i := strings.IndexByte(prop, sep)
   202  	if i == -1 {
   203  		return "", "", false
   204  	}
   205  	field, value = prop[:i], prop[i+1:]
   206  	return field, value, field != "" && value != ""
   207  }
   208  
   209  func collectNumericValue(ms map[string]int64, field, value string) {
   210  	v, err := strconv.ParseFloat(value, 64)
   211  	if err != nil {
   212  		return
   213  	}
   214  	if strings.IndexByte(value, '.') == -1 {
   215  		ms[field] = int64(v)
   216  	} else {
   217  		ms[field] = int64(v * precision)
   218  	}
   219  }