1
0
Fork 0
forked from boyska/circolog

server-side filtering

circologctl filter lets you load filters on circologd
This commit is contained in:
boyska 2018-12-25 03:52:53 +01:00
parent 8735ad2c21
commit 14e97dd43e
4 changed files with 70 additions and 2 deletions

View file

@ -9,6 +9,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"strings"
"time" "time"
) )
@ -30,6 +31,7 @@ func init() {
//"set": setCmd, //"set": setCmd,
//"get": getCmd, //"get": getCmd,
"pause": pauseCmd, "pause": pauseCmd,
"filter": filterCmd,
"reload": reloadCmd, "reload": reloadCmd,
"restart": restartCmd, "restart": restartCmd,
"help": helpCmd, "help": helpCmd,
@ -40,6 +42,24 @@ func init() {
//func getCmd(ctlSock string, args []string) error {} //func getCmd(ctlSock string, args []string) error {}
func filterCmd(args []string) error {
filter := strings.Join(args[1:], " ")
postBody := make(map[string][]string)
postBody["where"] = []string{filter}
if globalOpts.debug {
fmt.Println("[DEBUG] postBody:", postBody)
}
resp, err := ctl.PostForm("http://unix/filter", postBody)
if resp.StatusCode != 200 || globalOpts.verbose {
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Println(string(bodyBytes))
}
return err
}
func pauseCmd(args []string) error { func pauseCmd(args []string) error {
var dontChangeAgain time.Duration var dontChangeAgain time.Duration
flagset := flag.NewFlagSet(args[0], flag.ExitOnError) flagset := flag.NewFlagSet(args[0], flag.ExitOnError)

View file

@ -16,6 +16,7 @@ import (
func setupHTTPCtl(hub circolog.Hub, verbose, debug bool) *mux.Router { func setupHTTPCtl(hub circolog.Hub, verbose, debug bool) *mux.Router {
m := mux.NewRouter() m := mux.NewRouter()
m.HandleFunc("/pause/toggle", togglePause(hub, verbose, debug)).Methods("POST") m.HandleFunc("/pause/toggle", togglePause(hub, verbose, debug)).Methods("POST")
m.HandleFunc("/filter", setFilter(hub, verbose, debug)).Methods("POST")
m.HandleFunc("/status", getStatus(hub, verbose, debug)).Methods("GET") m.HandleFunc("/status", getStatus(hub, verbose, debug)).Methods("GET")
m.HandleFunc("/logs/clear", clearQueue(hub, verbose)).Methods("POST") m.HandleFunc("/logs/clear", clearQueue(hub, verbose)).Methods("POST")
m.HandleFunc("/help", printHelp(verbose)).Methods("GET") m.HandleFunc("/help", printHelp(verbose)).Methods("GET")
@ -71,6 +72,26 @@ func togglePause(hub circolog.Hub, verbose, debug bool) http.HandlerFunc {
} }
} }
func setFilter(hub circolog.Hub, verbose, debug bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
where := r.FormValue("where")
response := make(chan circolog.CommandResponse)
hub.Commands <- circolog.HubFullCommand{
Command: circolog.CommandNewFilter,
Response: response,
Parameters: map[string]interface{}{"where": where},
}
resp := <-response
if !resp.Value.(map[string]interface{})["success"].(bool) {
w.WriteHeader(400)
}
w.Header().Set("content-type", "application/json")
enc := json.NewEncoder(w)
enc.Encode(resp.Value)
}
}
func clearQueue(hub circolog.Hub, verbose bool) http.HandlerFunc { func clearQueue(hub circolog.Hub, verbose bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if verbose { if verbose {

View file

@ -11,7 +11,8 @@ import (
) )
type ExprValue struct { type ExprValue struct {
Node expr.Node Node expr.Node
Expression string
} }
func (e *ExprValue) String() string { func (e *ExprValue) String() string {
@ -22,15 +23,24 @@ func (e *ExprValue) String() string {
} }
} }
func (e *ExprValue) Set(value string) error { func (e *ExprValue) Set(value string) error {
if value == "" {
e.Node = nil
e.Expression = value
return nil
}
ast, err := expr.ParseExpression(value) ast, err := expr.ParseExpression(value)
if err != nil { if err != nil {
return err return err
} }
e.Node = ast e.Node = ast
e.Expression = value
return nil return nil
} }
func (e *ExprValue) Validate(line map[string]interface{}) bool { func (e *ExprValue) Validate(line map[string]interface{}) bool {
if e.Node == nil {
return true
}
context := datasource.NewContextSimpleNative(line) context := datasource.NewContextSimpleNative(line)
val, ok := vm.Eval(context, e.Node) val, ok := vm.Eval(context, e.Node)
if !ok || val == nil { // errors when evaluating if !ok || val == nil { // errors when evaluating

19
hub.go
View file

@ -6,6 +6,7 @@ import (
"os" "os"
"time" "time"
"git.lattuga.net/boyska/circolog/filtering"
"gopkg.in/mcuadros/go-syslog.v2/format" "gopkg.in/mcuadros/go-syslog.v2/format"
) )
@ -34,6 +35,7 @@ const (
CommandClear = iota CommandClear = iota
CommandPauseToggle = iota CommandPauseToggle = iota
CommandStatus = iota CommandStatus = iota
CommandNewFilter = iota
) )
// An HubFullCommand is a Command, complete with arguments // An HubFullCommand is a Command, complete with arguments
@ -102,6 +104,7 @@ func (h *Hub) register(cl Client) {
// Run is hub main loop; keeps everything going // Run is hub main loop; keeps everything going
func (h *Hub) Run() { func (h *Hub) Run() {
active := true active := true
var filter filtering.ExprValue
for { for {
select { select {
case cl := <-h.Register: case cl := <-h.Register:
@ -113,7 +116,7 @@ func (h *Hub) Run() {
delete(h.clients, cl) delete(h.clients, cl)
} }
case msg := <-h.LogMessages: case msg := <-h.LogMessages:
if active == true { if active == true && filter.Validate(msg) {
h.circbuf.Value = msg h.circbuf.Value = msg
h.circbuf = h.circbuf.Next() h.circbuf = h.circbuf.Next()
for client := range h.clients { for client := range h.clients {
@ -141,7 +144,21 @@ func (h *Hub) Run() {
cmd.Response <- CommandResponse{Value: map[string]interface{}{ cmd.Response <- CommandResponse{Value: map[string]interface{}{
"size": h.circbuf.Len(), "size": h.circbuf.Len(),
"paused": !active, "paused": !active,
"filter": filter.Expression,
}} }}
case CommandNewFilter:
if err := filter.Set(cmd.Parameters["where"].(string)); err != nil {
cmd.Response <- CommandResponse{Value: map[string]interface{}{
"success": false,
"error": err.Error(),
}}
} else {
cmd.Response <- CommandResponse{Value: map[string]interface{}{
"success": true,
"error": "",
}}
}
} }
} }
} }