| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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 main |
| |
| import ( |
| "encoding/json" |
| "errors" |
| "fmt" |
| htrace "org/apache/htrace/client" |
| "org/apache/htrace/common" |
| "strings" |
| "unicode" |
| ) |
| |
| // Convert a string into a whitespace-separated sequence of strings. |
| func tokenize(str string) []string { |
| prevQuote := rune(0) |
| f := func(c rune) bool { |
| switch { |
| case c == prevQuote: |
| prevQuote = rune(0) |
| return true |
| case prevQuote != rune(0): |
| return false |
| case unicode.In(c, unicode.Quotation_Mark): |
| prevQuote = c |
| return true |
| default: |
| return unicode.IsSpace(c) |
| } |
| } |
| return strings.FieldsFunc(str, f) |
| } |
| |
| // Parses a query string in the format of a series of |
| // [TYPE] [OPERATOR] [CONST] tuples, joined by AND statements. |
| type predicateParser struct { |
| tokens []string |
| curToken int |
| } |
| |
| func (ps *predicateParser) Parse() (*common.Predicate, error) { |
| if ps.curToken >= len(ps.tokens) { |
| return nil, nil |
| } |
| if ps.curToken > 0 { |
| if strings.ToLower(ps.tokens[ps.curToken]) != "and" { |
| return nil, errors.New(fmt.Sprintf("Error parsing on token %d: "+ |
| "expected predicates to be joined by 'and', but found '%s'", |
| ps.curToken, ps.tokens[ps.curToken])) |
| } |
| ps.curToken++ |
| if ps.curToken > len(ps.tokens) { |
| return nil, errors.New(fmt.Sprintf("Nothing found after 'and' at "+ |
| "token %d", ps.curToken)) |
| } |
| } |
| field := common.Field(strings.ToLower(ps.tokens[ps.curToken])) |
| if !field.IsValid() { |
| return nil, errors.New(fmt.Sprintf("Invalid field specifier at token %d. "+ |
| "Can't understand %s. Valid field specifiers are %v", ps.curToken, |
| ps.tokens[ps.curToken], common.ValidFields())) |
| } |
| ps.curToken++ |
| if ps.curToken > len(ps.tokens) { |
| return nil, errors.New(fmt.Sprintf("Nothing found after field specifier "+ |
| "at token %d", ps.curToken)) |
| } |
| op := common.Op(strings.ToLower(ps.tokens[ps.curToken])) |
| if !op.IsValid() { |
| return nil, errors.New(fmt.Sprintf("Invalid operation specifier at token %d. "+ |
| "Can't understand %s. Valid operation specifiers are %v", ps.curToken, |
| ps.tokens[ps.curToken], common.ValidOps())) |
| } |
| ps.curToken++ |
| if ps.curToken > len(ps.tokens) { |
| return nil, errors.New(fmt.Sprintf("Nothing found after field specifier "+ |
| "at token %d", ps.curToken)) |
| } |
| val := ps.tokens[ps.curToken] |
| ps.curToken++ |
| return &common.Predicate{Op: op, Field: field, Val: val}, nil |
| } |
| |
| func parseQueryString(str string) ([]common.Predicate, error) { |
| ps := predicateParser{tokens: tokenize(str)} |
| if verbose { |
| fmt.Printf("Running query [ ") |
| prefix := "" |
| for tokenIdx := range(ps.tokens) { |
| fmt.Printf("%s'%s'", prefix, ps.tokens[tokenIdx]) |
| prefix = ", " |
| } |
| fmt.Printf(" ]\n") |
| } |
| preds := make([]common.Predicate, 0) |
| for { |
| pred, err := ps.Parse() |
| if err != nil { |
| return nil, err |
| } |
| if pred == nil { |
| break |
| } |
| preds = append(preds, *pred) |
| } |
| if len(preds) == 0 { |
| return nil, errors.New("Empty query string") |
| } |
| return preds, nil |
| } |
| |
| // Send a query from a query string. |
| func doQueryFromString(hcl *htrace.Client, str string, lim int) error { |
| query := &common.Query{Lim: lim} |
| var err error |
| query.Predicates, err = parseQueryString(str) |
| if err != nil { |
| return err |
| } |
| return doQuery(hcl, query) |
| } |
| |
| // Send a query from a raw JSON string. |
| func doRawQuery(hcl *htrace.Client, str string) error { |
| jsonBytes := []byte(str) |
| var query common.Query |
| err := json.Unmarshal(jsonBytes, &query) |
| if err != nil { |
| return errors.New(fmt.Sprintf("Error parsing provided JSON: %s\n", err.Error())) |
| } |
| return doQuery(hcl, &query) |
| } |
| |
| // Send a query. |
| func doQuery(hcl *htrace.Client, query *common.Query) error { |
| if verbose { |
| qbytes, err := json.Marshal(*query) |
| if err != nil { |
| qbytes = []byte("marshaling error: " + err.Error()) |
| } |
| fmt.Printf("Sending query: %s\n", string(qbytes)) |
| } |
| spans, err := hcl.Query(query) |
| if err != nil { |
| return err |
| } |
| if verbose { |
| fmt.Printf("%d results...\n", len(spans)) |
| } |
| for i := range spans { |
| fmt.Printf("%s\n", spans[i].ToJson()) |
| } |
| return nil |
| } |