// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package status import ( "bufio" "regexp" "strings" "github.com/elastic/beats/libbeat/common" s "github.com/elastic/beats/libbeat/common/schema" c "github.com/elastic/beats/libbeat/common/schema/mapstrstr" ) var ( scoreboardRegexp = regexp.MustCompile("(Scoreboard):\\s+((_|S|R|W|K|D|C|L|G|I|\\.)+)") // This should match: "CPUSystem: .01" matchNumber = regexp.MustCompile("(^[0-9a-zA-Z ]+):\\s+(\\d*\\.?\\d+)") schema = s.Schema{ "total_accesses": c.Int("Total Accesses"), "total_kbytes": c.Int("Total kBytes"), "requests_per_sec": c.Float("ReqPerSec", s.Optional), "bytes_per_sec": c.Float("BytesPerSec", s.Optional), "bytes_per_request": c.Float("BytesPerReq", s.Optional), "workers": s.Object{ "busy": c.Int("BusyWorkers"), "idle": c.Int("IdleWorkers"), }, "uptime": s.Object{ "server_uptime": c.Int("ServerUptimeSeconds"), "uptime": c.Int("Uptime"), }, "cpu": s.Object{ "load": c.Float("CPULoad", s.Optional), "user": c.Float("CPUUser"), "system": c.Float("CPUSystem"), "children_user": c.Float("CPUChildrenUser"), "children_system": c.Float("CPUChildrenSystem"), }, "connections": s.Object{ "total": c.Int("ConnsTotal", s.Optional), "async": s.Object{ "writing": c.Int("ConnsAsyncWriting", s.Optional), "keep_alive": c.Int("ConnsAsyncKeepAlive", s.Optional), "closing": c.Int("ConnsAsyncClosing", s.Optional), }, }, "load": s.Object{ "1": c.Float("Load1", s.Optional), "5": c.Float("Load5", s.Optional), "15": c.Float("Load15", s.Optional), }, } // Schema used till apache 2.4.12 schemaOld = s.Schema{ "total_accesses": c.Int("Total Accesses"), "total_kbytes": c.Int("Total kBytes"), "requests_per_sec": c.Float("ReqPerSec", s.Optional), "bytes_per_sec": c.Float("BytesPerSec", s.Optional), "bytes_per_request": c.Float("BytesPerReq", s.Optional), "workers": s.Object{ "busy": c.Int("BusyWorkers"), "idle": c.Int("IdleWorkers"), }, "uptime": s.Object{ "uptime": c.Int("Uptime"), }, "cpu": s.Object{ "load": c.Float("CPULoad", s.Optional), }, "connections": s.Object{ "total": c.Int("ConnsTotal", s.Optional), "async": s.Object{ "writing": c.Int("ConnsAsyncWriting", s.Optional), "keep_alive": c.Int("ConnsAsyncKeepAlive", s.Optional), "closing": c.Int("ConnsAsyncClosing", s.Optional), }, }, } ) func applySchema(event common.MapStr, fullEvent map[string]interface{}) error { applicableSchema := schema if _, found := fullEvent["ServerUptimeSeconds"]; !found { applicableSchema = schemaOld } _, err := applicableSchema.ApplyTo(event, fullEvent) return err.Err() } // Map body to MapStr func eventMapping(scanner *bufio.Scanner, hostname string) (common.MapStr, error) { var ( totalS int totalR int totalW int totalK int totalD int totalC int totalL int totalG int totalI int totalDot int totalUnderscore int totalAll int ) fullEvent := map[string]interface{}{} // Iterate through all events to gather data for scanner.Scan() { if match := matchNumber.FindStringSubmatch(scanner.Text()); len(match) == 3 { // Total Accesses: 16147 //Total kBytes: 12988 // Uptime: 3229728 // CPULoad: .000408393 // CPUUser: 0 // CPUSystem: .01 // CPUChildrenUser: 0 // CPUChildrenSystem: 0 // ReqPerSec: .00499949 // BytesPerSec: 4.1179 // BytesPerReq: 823.665 // BusyWorkers: 1 // IdleWorkers: 8 // ConnsTotal: 4940 // ConnsAsyncWriting: 527 // ConnsAsyncKeepAlive: 1321 // ConnsAsyncClosing: 2785 // ServerUptimeSeconds: 43 //Load1: 0.01 //Load5: 0.10 //Load15: 0.06 fullEvent[match[1]] = match[2] } else if match := scoreboardRegexp.FindStringSubmatch(scanner.Text()); len(match) == 4 { // Scoreboard Key: // "_" Waiting for Connection, "S" Starting up, "R" Reading Request, // "W" Sending Reply, "K" Keepalive (read), "D" DNS Lookup, // "C" Closing connection, "L" Logging, "G" Gracefully finishing, // "I" Idle cleanup of worker, "." Open slot with no current process // Scoreboard: _W____........___............................................................................................................................................................................................................................................... totalUnderscore = strings.Count(match[2], "_") totalS = strings.Count(match[2], "S") totalR = strings.Count(match[2], "R") totalW = strings.Count(match[2], "W") totalK = strings.Count(match[2], "K") totalD = strings.Count(match[2], "D") totalC = strings.Count(match[2], "C") totalL = strings.Count(match[2], "L") totalG = strings.Count(match[2], "G") totalI = strings.Count(match[2], "I") totalDot = strings.Count(match[2], ".") totalAll = totalUnderscore + totalS + totalR + totalW + totalK + totalD + totalC + totalL + totalG + totalI + totalDot } else { debugf("Unexpected line in apache server-status output: %s", scanner.Text()) } } event := common.MapStr{ "hostname": hostname, "scoreboard": common.MapStr{ "starting_up": totalS, "reading_request": totalR, "sending_reply": totalW, "keepalive": totalK, "dns_lookup": totalD, "closing_connection": totalC, "logging": totalL, "gracefully_finishing": totalG, "idle_cleanup": totalI, "open_slot": totalDot, "waiting_for_connection": totalUnderscore, "total": totalAll, }, } return event, applySchema(event, fullEvent) } /* func parseMatchFloat(input interface{}, fieldName string) float64 { var parseString string if input != nil { if strings.HasPrefix(input.(string), ".") { parseString = strings.Replace(input.(string), ".", "0.", 1) } else { parseString = input.(string) } outputFloat, err := strconv.ParseFloat(parseString, 64) if err != nil { logp.Err("Cannot parse string '%s' to float for field '%s'. Error: %+v", input.(string), fieldName, err) return 0.0 } return outputFloat } else { return 0.0 } }*/