175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
// 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 redis
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
rd "github.com/garyburd/redigo/redis"
|
|
"github.com/gofrs/uuid"
|
|
|
|
"github.com/elastic/beats/libbeat/beat"
|
|
"github.com/elastic/beats/libbeat/common"
|
|
"github.com/elastic/beats/libbeat/logp"
|
|
|
|
"github.com/elastic/beats/filebeat/harvester"
|
|
"github.com/elastic/beats/filebeat/util"
|
|
)
|
|
|
|
// Harvester contains all redis harvester data
|
|
type Harvester struct {
|
|
id uuid.UUID
|
|
done chan struct{}
|
|
conn rd.Conn
|
|
forwarder *harvester.Forwarder
|
|
}
|
|
|
|
// log contains all data related to one slowlog entry
|
|
//
|
|
// The data is in the following format:
|
|
// 1) (integer) 13
|
|
// 2) (integer) 1309448128
|
|
// 3) (integer) 30
|
|
// 4) 1) "slowlog"
|
|
// 2) "get"
|
|
// 3) "100"
|
|
//
|
|
type log struct {
|
|
id int64
|
|
timestamp int64
|
|
duration int
|
|
cmd string
|
|
key string
|
|
args []string
|
|
}
|
|
|
|
// NewHarvester creates a new harvester with the given connection
|
|
func NewHarvester(conn rd.Conn) (*Harvester, error) {
|
|
id, err := uuid.NewV4()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Harvester{
|
|
id: id,
|
|
done: make(chan struct{}),
|
|
conn: conn,
|
|
}, nil
|
|
}
|
|
|
|
// Run starts a new redis harvester
|
|
func (h *Harvester) Run() error {
|
|
defer h.conn.Close()
|
|
|
|
select {
|
|
case <-h.done:
|
|
return nil
|
|
default:
|
|
}
|
|
// Writes Slowlog get and slowlog reset both to the buffer so they are executed together
|
|
h.conn.Send("SLOWLOG", "GET")
|
|
h.conn.Send("SLOWLOG", "RESET")
|
|
|
|
// Flush the buffer to execute both commands and receive the reply from SLOWLOG GET
|
|
h.conn.Flush()
|
|
|
|
// Receives first reply from redis which is the one from GET
|
|
logs, err := rd.Values(h.conn.Receive())
|
|
if err != nil {
|
|
return fmt.Errorf("error receiving slowlog data: %s", err)
|
|
}
|
|
|
|
// Read reply from RESET
|
|
_, err = h.conn.Receive()
|
|
if err != nil {
|
|
return fmt.Errorf("error receiving reset data: %s", err)
|
|
}
|
|
|
|
for _, item := range logs {
|
|
// Stopping here means some of the slowlog events are lost!
|
|
select {
|
|
case <-h.done:
|
|
return nil
|
|
default:
|
|
}
|
|
entry, err := rd.Values(item, nil)
|
|
if err != nil {
|
|
logp.Err("Error loading slowlog values: %s", err)
|
|
continue
|
|
}
|
|
|
|
var log log
|
|
var args []string
|
|
rd.Scan(entry, &log.id, &log.timestamp, &log.duration, &args)
|
|
|
|
// This splits up the args into cmd, key, args.
|
|
argsLen := len(args)
|
|
if argsLen > 0 {
|
|
log.cmd = args[0]
|
|
}
|
|
if argsLen > 1 {
|
|
log.key = args[1]
|
|
}
|
|
|
|
// This could contain confidential data, processors should be used to drop it if needed
|
|
if argsLen > 2 {
|
|
log.args = args[2:]
|
|
}
|
|
|
|
data := util.NewData()
|
|
subEvent := common.MapStr{
|
|
"id": log.id,
|
|
"cmd": log.cmd,
|
|
"key": log.key,
|
|
"duration": common.MapStr{
|
|
"us": log.duration,
|
|
},
|
|
}
|
|
|
|
if log.args != nil {
|
|
subEvent["args"] = log.args
|
|
|
|
}
|
|
|
|
data.Event = beat.Event{
|
|
Timestamp: time.Unix(log.timestamp, 0).UTC(),
|
|
Fields: common.MapStr{
|
|
"message": strings.Join(args, " "),
|
|
"redis": common.MapStr{
|
|
"slowlog": subEvent,
|
|
},
|
|
"read_timestamp": common.Time(time.Now().UTC()),
|
|
},
|
|
}
|
|
|
|
h.forwarder.Send(data)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the harvester
|
|
func (h *Harvester) Stop() {
|
|
close(h.done)
|
|
}
|
|
|
|
// ID returns the unique harvester ID
|
|
func (h *Harvester) ID() uuid.UUID {
|
|
return h.id
|
|
}
|