// Find the children of a given span id. func (store *dataStore) FindChildren(sid common.SpanId, lim int32) []common.SpanId { childIds := make([]common.SpanId, 0) var err error startIdx := store.getShardIndex(sid) idx := startIdx numShards := len(store.shards) for { if lim == 0 { break } shd := store.shards[idx] childIds, lim, err = shd.FindChildren(sid, childIds, lim) if err != nil { store.lg.Errorf("Shard(%s): FindChildren(%s) error: %s\n", shd.path, sid.String(), err.Error()) } idx++ if idx >= numShards { idx = 0 } if idx == startIdx { break } } return childIds }
func (hand *dataStoreHandler) parseSid(w http.ResponseWriter, str string) (common.SpanId, bool) { var id common.SpanId err := id.FromString(str) if err != nil { writeError(hand.lg, w, http.StatusBadRequest, fmt.Sprintf("Failed to parse span ID %s: %s", str, err.Error())) w.Write([]byte("Error parsing : " + err.Error())) return common.INVALID_SPAN_ID, false } return id, true }
func loadPredicateData(pred *common.Predicate) (*predicateData, error) { p := predicateData{Predicate: pred} // Parse the input value given to make sure it matches up with the field // type. switch pred.Field { case common.SPAN_ID: // Span IDs are sent as hex strings. var id common.SpanId if err := id.FromString(pred.Val); err != nil { return nil, errors.New(fmt.Sprintf("Unable to parse span id '%s': %s", pred.Val, err.Error())) } p.key = id.Val() break case common.DESCRIPTION: // Any string is valid for a description. p.key = []byte(pred.Val) break case common.BEGIN_TIME, common.END_TIME, common.DURATION: // Parse a base-10 signed numeric field. v, err := strconv.ParseInt(pred.Val, 10, 64) if err != nil { return nil, errors.New(fmt.Sprintf("Unable to parse %s '%s': %s", pred.Field, pred.Val, err.Error())) } p.key = u64toSlice(s2u64(v)) break case common.TRACER_ID: // Any string is valid for a tracer ID. p.key = []byte(pred.Val) break default: return nil, errors.New(fmt.Sprintf("Unknown field %s", pred.Field)) } // Validate the predicate operation. switch pred.Op { case common.EQUALS, common.LESS_THAN_OR_EQUALS, common.GREATER_THAN_OR_EQUALS, common.GREATER_THAN: break case common.CONTAINS: if p.fieldIsNumeric() { return nil, errors.New(fmt.Sprintf("Can't use CONTAINS on a "+ "numeric field like '%s'", pred.Field)) } default: return nil, errors.New(fmt.Sprintf("Unknown predicate operation '%s'", pred.Op)) } return &p, nil }
// Find the child IDs of a given span ID. func (hcl *Client) FindChildren(sid common.SpanId, lim int) ([]common.SpanId, error) { buf, _, err := hcl.makeGetRequest(fmt.Sprintf("span/%s/children?lim=%d", sid.String(), lim)) if err != nil { return nil, err } var spanIds []common.SpanId err = json.Unmarshal(buf, &spanIds) if err != nil { return nil, errors.New(fmt.Sprintf("Error: error unmarshalling response "+ "body %s: %s", string(buf), err.Error())) } return spanIds, nil }
// Get information about a trace span. Returns nil, nil if the span was not found. func (hcl *Client) FindSpan(sid common.SpanId) (*common.Span, error) { buf, rc, err := hcl.makeGetRequest(fmt.Sprintf("span/%s", sid.String())) if err != nil { if rc == http.StatusNoContent { return nil, nil } return nil, err } var span common.Span err = json.Unmarshal(buf, &span) if err != nil { return nil, errors.New(fmt.Sprintf("Error unmarshalling response "+ "body %s: %s", string(buf), err.Error())) } return &span, nil }
func (shd *shard) FindChildren(sid common.SpanId, childIds []common.SpanId, lim int32) ([]common.SpanId, int32, error) { searchKey := append([]byte{PARENT_ID_INDEX_PREFIX}, sid.Val()...) iter := shd.ldb.NewIterator(shd.store.readOpts) defer iter.Close() iter.Seek(searchKey) for { if !iter.Valid() { break } if lim == 0 { break } key := iter.Key() if !bytes.HasPrefix(key, searchKey) { break } id := common.SpanId(key[17:]) childIds = append(childIds, id) lim-- iter.Next() } return childIds, lim, nil }
func (shd *shard) FindSpan(sid common.SpanId) *common.Span { lg := shd.store.lg primaryKey := append([]byte{SPAN_ID_INDEX_PREFIX}, sid.Val()...) buf, err := shd.ldb.Get(shd.store.readOpts, primaryKey) if err != nil { if strings.Index(err.Error(), "NotFound:") != -1 { return nil } lg.Warnf("Shard(%s): FindSpan(%s) error: %s\n", shd.path, sid.String(), err.Error()) return nil } var span *common.Span span, err = shd.decodeSpan(sid, buf) if err != nil { lg.Errorf("Shard(%s): FindSpan(%s) decode error: %s\n", shd.path, sid.String(), err.Error()) return nil } return span }
// Fill in the entry in the 'next' array for a specific shard. func (src *source) populateNextFromShard(shardIdx int) { lg := src.store.lg var err error iter := src.iters[shardIdx] if iter == nil { lg.Debugf("Can't populate: No more entries in shard %d\n", shardIdx) return // There are no more entries in this shard. } if src.nexts[shardIdx] != nil { lg.Debugf("No need to populate shard %d\n", shardIdx) return // We already have a valid entry for this shard. } for { if !iter.Valid() { lg.Debugf("Can't populate: Iterator for shard %d is no longer valid.\n", shardIdx) break // Can't read past end of DB } src.numRead[shardIdx]++ key := iter.Key() if !bytes.HasPrefix(key, []byte{src.keyPrefix}) { lg.Debugf("Can't populate: Iterator for shard %d does not have prefix %s\n", shardIdx, string(src.keyPrefix)) break // Can't read past end of indexed section } var span *common.Span var sid common.SpanId if src.keyPrefix == SPAN_ID_INDEX_PREFIX { // The span id maps to the span itself. sid = common.SpanId(key[1:17]) span, err = src.store.shards[shardIdx].decodeSpan(sid, iter.Value()) if err != nil { lg.Debugf("Internal error decoding span %s in shard %d: %s\n", sid.String(), shardIdx, err.Error()) break } } else { // With a secondary index, we have to look up the span by id. sid = common.SpanId(key[9:25]) span = src.store.shards[shardIdx].FindSpan(sid) if span == nil { lg.Debugf("Internal error rehydrating span %s in shard %d\n", sid.String(), shardIdx) break } } if src.pred.Op.IsDescending() { iter.Prev() } else { iter.Next() } if src.pred.satisfiedBy(span) { lg.Debugf("Populated valid span %v from shard %d.\n", sid, shardIdx) src.nexts[shardIdx] = span // Found valid entry return } else { lg.Debugf("Span %s from shard %d does not satisfy the predicate.\n", sid.String(), shardIdx) if src.numRead[shardIdx] <= 1 && mayRequireOneSkip(src.pred.Op) { continue } // This and subsequent entries don't satisfy predicate break } } lg.Debugf("Closing iterator for shard %d.\n", shardIdx) iter.Close() src.iters[shardIdx] = nil }
// Get the index of the shard which stores the given spanId. func (store *dataStore) getShardIndex(sid common.SpanId) int { return int(sid.Hash32() % uint32(len(store.shards))) }
// Fill in the entry in the 'next' array for a specific shard. func (src *source) populateNextFromShard(shardIdx int) { lg := src.store.lg var err error iter := src.iters[shardIdx] shdPath := src.shards[shardIdx].path if iter == nil { lg.Debugf("Can't populate: No more entries in shard %s\n", shdPath) return // There are no more entries in this shard. } if src.nexts[shardIdx] != nil { lg.Debugf("No need to populate shard %s\n", shdPath) return // We already have a valid entry for this shard. } for { if !iter.Valid() { lg.Debugf("Can't populate: Iterator for shard %s is no longer valid.\n", shdPath) break // Can't read past end of DB } src.numRead[shardIdx]++ key := iter.Key() if len(key) < 1 { lg.Warnf("Encountered invalid zero-byte key in shard %s.\n", shdPath) break } ret := src.checkKeyPrefix(key[0], iter) if ret == NOT_SATISFIED { break // Can't read past end of indexed section } else if ret == NOT_YET_SATISFIED { if src.pred.Op.IsDescending() { iter.Prev() } else { iter.Next() } continue // Try again because we are not yet at the indexed section. } var span *common.Span var sid common.SpanId if src.keyPrefix == SPAN_ID_INDEX_PREFIX { // The span id maps to the span itself. sid = common.SpanId(key[1:17]) span, err = src.shards[shardIdx].decodeSpan(sid, iter.Value()) if err != nil { if lg.DebugEnabled() { lg.Debugf("Internal error decoding span %s in shard %s: %s\n", sid.String(), shdPath, err.Error()) } break } } else { // With a secondary index, we have to look up the span by id. sid = common.SpanId(key[9:25]) span = src.shards[shardIdx].FindSpan(sid) if span == nil { if lg.DebugEnabled() { lg.Debugf("Internal error rehydrating span %s in shard %s\n", sid.String(), shdPath) } break } } if src.pred.Op.IsDescending() { iter.Prev() } else { iter.Next() } ret = src.pred.satisfiedBy(span) if ret == SATISFIED { if lg.DebugEnabled() { lg.Debugf("Populated valid span %v from shard %s.\n", sid, shdPath) } src.nexts[shardIdx] = span // Found valid entry return } if ret == NOT_SATISFIED { // This and subsequent entries don't satisfy predicate break } } lg.Debugf("Closing iterator for shard %s.\n", shdPath) iter.Close() src.iters[shardIdx] = nil }
func main() { // Load htraced configuration cnf := common.LoadApplicationConfig() // Parse argv app := kingpin.New(os.Args[0], USAGE) app.Flag("Dmy.key", "Set configuration key 'my.key' to 'my.value'. Replace 'my.key' "+ "with any key you want to set.").Default("my.value").String() addr := app.Flag("addr", "Server address.").String() verbose = app.Flag("verbose", "Verbose.").Default("false").Bool() version := app.Command("version", "Print the version of this program.") serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.") serverStats := app.Command("serverStats", "Print statistics retrieved from the htraced server.") serverStatsJson := serverStats.Flag("json", "Display statistics as raw JSON.").Default("false").Bool() findSpan := app.Command("findSpan", "Print information about a trace span with a given ID.") findSpanId := findSpan.Arg("id", "Span ID to find. Example: be305e54-4534-2110-a0b2-e06b9effe112").Required().String() findChildren := app.Command("findChildren", "Print out the span IDs that are children of a given span ID.") parentSpanId := findChildren.Arg("id", "Span ID to print children for. Example: be305e54-4534-2110-a0b2-e06b9effe112"). Required().String() childLim := findChildren.Flag("lim", "Maximum number of child IDs to print.").Default("20").Int() loadFile := app.Command("loadFile", "Write whitespace-separated JSON spans from a file to the server.") loadFilePath := loadFile.Arg("path", "A file containing whitespace-separated span JSON.").Required().String() loadJson := app.Command("load", "Write JSON spans from the command-line to the server.") loadJsonArg := loadJson.Arg("json", "A JSON span to write to the server.").Required().String() dumpAll := app.Command("dumpAll", "Dump all spans from the htraced daemon.") dumpAllOutPath := dumpAll.Arg("path", "The path to dump the trace spans to.").Default("-").String() dumpAllLim := dumpAll.Flag("lim", "The number of spans to transfer from the server at once."). Default("100").Int() graph := app.Command("graph", "Visualize span JSON as a graph.") graphJsonFile := graph.Arg("input", "The JSON file to load").Required().String() graphDotFile := graph.Flag("output", "The path to write a GraphViz dotfile to. This file can be used as input to "+ "GraphViz, in order to generate a pretty picture. See graphviz.org for more "+ "information about generating pictures of graphs.").Default("-").String() query := app.Command("query", "Send a query to htraced.") queryLim := query.Flag("lim", "Maximum number of spans to retrieve.").Default("20").Int() queryArg := query.Arg("query", "The query string to send. Query strings have the format "+ "[TYPE] [OPERATOR] [CONST], joined by AND statements.").Required().String() rawQuery := app.Command("rawQuery", "Send a raw JSON query to htraced.") rawQueryArg := query.Arg("json", "The query JSON to send.").Required().String() cmd := kingpin.MustParse(app.Parse(os.Args[1:])) // Add the command-line settings into the configuration. if *addr != "" { cnf = cnf.Clone(conf.HTRACE_WEB_ADDRESS, *addr) } // Handle commands that don't require an HTrace client. switch cmd { case version.FullCommand(): os.Exit(printVersion()) case graph.FullCommand(): err := jsonSpanFileToDotFile(*graphJsonFile, *graphDotFile) if err != nil { fmt.Printf("graphing error: %s\n", err.Error()) os.Exit(EXIT_FAILURE) } os.Exit(EXIT_SUCCESS) } // Create HTrace client hcl, err := htrace.NewClient(cnf) if err != nil { fmt.Printf("Failed to create HTrace client: %s\n", err.Error()) os.Exit(EXIT_FAILURE) } // Handle commands that require an HTrace client. switch cmd { case version.FullCommand(): os.Exit(printVersion()) case serverInfo.FullCommand(): os.Exit(printServerInfo(hcl)) case serverStats.FullCommand(): if *serverStatsJson { os.Exit(printServerStatsJson(hcl)) } else { os.Exit(printServerStats(hcl)) } case findSpan.FullCommand(): var id *common.SpanId id.FromString(*findSpanId) os.Exit(doFindSpan(hcl, *id)) case findChildren.FullCommand(): var id *common.SpanId id.FromString(*parentSpanId) os.Exit(doFindChildren(hcl, *id, *childLim)) case loadJson.FullCommand(): os.Exit(doLoadSpanJson(hcl, *loadJsonArg)) case loadFile.FullCommand(): os.Exit(doLoadSpanJsonFile(hcl, *loadFilePath)) case dumpAll.FullCommand(): err := doDumpAll(hcl, *dumpAllOutPath, *dumpAllLim) if err != nil { fmt.Printf("dumpAll error: %s\n", err.Error()) os.Exit(EXIT_FAILURE) } os.Exit(EXIT_SUCCESS) case query.FullCommand(): err := doQueryFromString(hcl, *queryArg, *queryLim) if err != nil { fmt.Printf("query error: %s\n", err.Error()) os.Exit(EXIT_FAILURE) } os.Exit(EXIT_SUCCESS) case rawQuery.FullCommand(): err := doRawQuery(hcl, *rawQueryArg) if err != nil { fmt.Printf("raw query error: %s\n", err.Error()) os.Exit(EXIT_FAILURE) } os.Exit(EXIT_SUCCESS) } app.UsageErrorf(os.Stderr, "You must supply a command to run.") }