blob: de5e84027e393af907d1eeaa0489ff6381094cd6 [file] [log] [blame]
/*
* 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 (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/alecthomas/kingpin"
"io"
"io/ioutil"
"net/http"
"org/apache/htrace/common"
"org/apache/htrace/conf"
"os"
)
func main() {
// Load htraced configuration
cnf := conf.LoadApplicationConfig()
// Parse argv
app := kingpin.New("htrace", "The HTrace tracing utility.")
addr := app.Flag("addr", "Server address.").
Default(cnf.Get(conf.HTRACE_WEB_ADDRESS)).TCP()
version := app.Command("version", "Print the version of this program.")
serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.")
findSpan := app.Command("findSpan", "Print information about a trace span with a given ID.")
findSpanId := findSpan.Flag("id", "Span ID to find, as a signed decimal 64-bit "+
"number").Required().Uint64()
findChildren := app.Command("findChildren", "Print out the span IDs that are children of a given span ID.")
parentSpanId := findChildren.Flag("id", "Span ID to print children for, as a signed decimal 64-bit "+
"number").Required().Uint64()
childLim := findChildren.Flag("lim", "Maximum number of child IDs to print.").Default("20").Int()
writeSpans := app.Command("writeSpans", "Write spans to the server in JSON form.")
spanJson := writeSpans.Flag("json", "The JSON span data to write to the server.").Required().String()
// Handle operation
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case version.FullCommand():
os.Exit(printVersion())
case serverInfo.FullCommand():
os.Exit(printServerInfo((*addr).String()))
case findSpan.FullCommand():
os.Exit(doFindSpan((*addr).String(), *findSpanId))
case findChildren.FullCommand():
os.Exit(doFindChildren((*addr).String(), *parentSpanId, *childLim))
case writeSpans.FullCommand():
os.Exit(doWriteSpans((*addr).String(), *spanJson))
}
app.UsageErrorf(os.Stderr, "You must supply a command to run.")
}
// Print the version of the htrace binary.
func printVersion() int {
fmt.Printf("Running htrace command version %s.\n", common.RELEASE_VERSION)
return 0
}
// Print information retrieved from an htraced server via /server/info
func printServerInfo(restAddr string) int {
buf, err := makeGetRequest(restAddr, "server/info")
if err != nil {
fmt.Printf("%s\n", err.Error())
return 1
}
var info common.ServerInfo
err = json.Unmarshal(buf, &info)
if err != nil {
fmt.Printf("Error: error unmarshalling response body %s: %s\n",
string(buf), err.Error())
return 1
}
fmt.Printf("HTraced server version %s (%s)\n", info.ReleaseVersion, info.GitVersion)
return 0
}
// Print information about a trace span.
func doFindSpan(restAddr string, sid uint64) int {
buf, err := makeGetRequest(restAddr, fmt.Sprintf("span/%016x", sid))
if err != nil {
fmt.Printf("%s\n", err.Error())
return 1
}
var span common.Span
err = json.Unmarshal(buf, &span)
if err != nil {
fmt.Printf("Error: error unmarshalling response body %s: %s\n",
string(buf), err.Error())
return 1
}
pbuf, err := json.MarshalIndent(span, "", " ")
if err != nil {
fmt.Println("Error: error pretty-printing span to JSON: %s", err.Error())
return 1
}
fmt.Printf("%s\n", string(pbuf))
return 0
}
func doWriteSpans(restAddr string, spanJson string) int {
body := []byte(spanJson)
_, err := makeRestRequest("POST", restAddr, "writeSpans", bytes.NewReader(body))
if err != nil {
fmt.Printf("%s\n", err.Error())
return 1
}
return 0
}
// Find information about the children of a span.
func doFindChildren(restAddr string, sid uint64, lim int) int {
buf, err := makeGetRequest(restAddr, fmt.Sprintf("span/%016x/children?lim=%d", sid, lim))
if err != nil {
fmt.Printf("%s\n", err.Error())
return 1
}
var spanIds []common.SpanId
err = json.Unmarshal(buf, &spanIds)
if err != nil {
fmt.Printf("Error: error unmarshalling response body %s: %s\n",
string(buf), err.Error())
return 1
}
pbuf, err := json.MarshalIndent(spanIds, "", " ")
if err != nil {
fmt.Println("Error: error pretty-printing span IDs to JSON: %s", err.Error())
return 1
}
fmt.Printf("%s\n", string(pbuf))
return 0
}
func makeGetRequest(restAddr string, reqName string) ([]byte, error) {
return makeRestRequest("GET", restAddr, reqName, nil)
}
// Print information retrieved from an htraced server via /serverInfo
func makeRestRequest(reqType string, restAddr string, reqName string,
reqBody io.Reader) ([]byte, error) {
url := fmt.Sprintf("http://%s/%s", restAddr, reqName)
req, err := http.NewRequest(reqType, url, reqBody)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.New(fmt.Sprintf("Error: error making http request to %s: %s\n", url,
err.Error()))
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New(fmt.Sprintf("Error: got bad response status from %s: %s\n", url, resp.Status))
}
var body []byte
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.New(fmt.Sprintf("Error: error reading response body: %s\n", err.Error()))
}
return body, nil
}