mirror of
https://gitee.com/johng/gf
synced 2026-07-05 13:22:16 +08:00
update dependences
This commit is contained in:
@ -1,124 +0,0 @@
|
||||
// Command line utility for the lz4 package.
|
||||
package main
|
||||
|
||||
import (
|
||||
// "bytes"
|
||||
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/third/github.com/pierrec/lz4"
|
||||
"github.com/gogf/gf/third/github.com/pkg/profile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Process command line arguments
|
||||
var (
|
||||
blockMaxSizeDefault = 4 << 20
|
||||
flagStdout = flag.Bool("c", false, "output to stdout")
|
||||
flagDecompress = flag.Bool("d", false, "decompress flag")
|
||||
flagBlockMaxSize = flag.Int("B", blockMaxSizeDefault, "block max size [64Kb,256Kb,1Mb,4Mb]")
|
||||
flagBlockChecksum = flag.Bool("BX", false, "enable block checksum")
|
||||
flagStreamChecksum = flag.Bool("Sx", false, "disable stream checksum")
|
||||
flagCompressionLevel = flag.Int("l", 0, "compression level (0=fastest)")
|
||||
profileName = flag.String("p", "", "path to the profile file")
|
||||
mode = flag.String("profile.mode", "", "enable profiling mode, one of [cpu, mem, mutex, block]")
|
||||
)
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage:\n\t%s [arg] [input]...\n\tNo input means [de]compress stdin to stdout\n\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
ppath := profile.ProfilePath(*profileName)
|
||||
switch *mode {
|
||||
case "cpu":
|
||||
defer profile.Start(profile.CPUProfile, ppath).Stop()
|
||||
case "mem":
|
||||
defer profile.Start(profile.MemProfile, ppath).Stop()
|
||||
case "mutex":
|
||||
defer profile.Start(profile.MutexProfile, ppath).Stop()
|
||||
case "block":
|
||||
defer profile.Start(profile.BlockProfile, ppath).Stop()
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// Use all CPUs
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
zr := lz4.NewReader(nil)
|
||||
zw := lz4.NewWriter(nil)
|
||||
zh := lz4.Header{
|
||||
BlockChecksum: *flagBlockChecksum,
|
||||
BlockMaxSize: *flagBlockMaxSize,
|
||||
NoChecksum: *flagStreamChecksum,
|
||||
CompressionLevel: *flagCompressionLevel,
|
||||
}
|
||||
|
||||
worker := func(in io.Reader, out io.Writer) {
|
||||
if *flagDecompress {
|
||||
zr.Reset(in)
|
||||
if _, err := io.Copy(out, zr); err != nil {
|
||||
log.Fatalf("Error while decompressing input: %v", err)
|
||||
}
|
||||
} else {
|
||||
zw.Reset(out)
|
||||
zw.Header = zh
|
||||
if _, err := io.Copy(zw, in); err != nil {
|
||||
log.Fatalf("Error while compressing input: %v", err)
|
||||
}
|
||||
if err := zw.Close(); err != nil {
|
||||
log.Fatalf("Error while closing stream: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No input means [de]compress stdin to stdout
|
||||
if len(flag.Args()) == 0 {
|
||||
worker(os.Stdin, os.Stdout)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Compress or decompress all input files
|
||||
for _, inputFileName := range flag.Args() {
|
||||
outputFileName := path.Clean(inputFileName)
|
||||
|
||||
if !*flagStdout {
|
||||
if *flagDecompress {
|
||||
outputFileName = strings.TrimSuffix(outputFileName, lz4.Extension)
|
||||
if outputFileName == inputFileName {
|
||||
log.Fatalf("Invalid output file name: same as input: %s", inputFileName)
|
||||
}
|
||||
} else {
|
||||
outputFileName += lz4.Extension
|
||||
}
|
||||
}
|
||||
|
||||
inputFile, err := os.Open(inputFileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Error while opening input: %v", err)
|
||||
}
|
||||
|
||||
outputFile := os.Stdout
|
||||
if !*flagStdout {
|
||||
outputFile, err = os.Create(outputFileName)
|
||||
if err != nil {
|
||||
log.Fatalf("Error while opening output: %v", err)
|
||||
}
|
||||
}
|
||||
worker(inputFile, outputFile)
|
||||
|
||||
inputFile.Close()
|
||||
if !*flagStdout {
|
||||
outputFile.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
22
third/github.com/stathat/go/.gitignore
vendored
Normal file
22
third/github.com/stathat/go/.gitignore
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
19
third/github.com/stathat/go/LICENSE
Normal file
19
third/github.com/stathat/go/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (C) 2012 Numerotron Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
67
third/github.com/stathat/go/README.md
Normal file
67
third/github.com/stathat/go/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
stathat
|
||||
=======
|
||||
|
||||
This is a Go package for posting stats to your StatHat account.
|
||||
|
||||
For more information about StatHat, visit [www.stathat.com](http://www.stathat.com).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Use `go get`:
|
||||
|
||||
go get github.com/stathat/go
|
||||
|
||||
That's it.
|
||||
|
||||
Import it like this:
|
||||
|
||||
import (
|
||||
"github.com/stathat/go"
|
||||
)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The easiest way to use the package is with the EZ API functions. You can add stats
|
||||
directly in your code by just adding a call with a new stat name. Once StatHat
|
||||
receives the call, a new stat will be created for you.
|
||||
|
||||
To post a count of 1 to a stat:
|
||||
|
||||
stathat.PostEZCountOne("messages sent - female to male", "something@stathat.com")
|
||||
|
||||
To specify the count:
|
||||
|
||||
stathat.PostEZCount("messages sent - male to male", "something@stathat.com", 37)
|
||||
|
||||
To post a value:
|
||||
|
||||
stathat.PostEZValue("ws0 load average", "something@stathat.com", 0.372)
|
||||
|
||||
There are also functions for the classic API. The drawback to the classic API is
|
||||
that you need to create the stats using the web interface and copy the keys it
|
||||
gives you into your code.
|
||||
|
||||
To post a count of 1 to a stat using the classic API:
|
||||
|
||||
stathat.PostCountOne("statkey", "userkey")
|
||||
|
||||
To specify the count:
|
||||
|
||||
stathat.PostCount("statkey", "userkey", 37)
|
||||
|
||||
To post a value:
|
||||
|
||||
stathat.PostValue("statkey", "userkey", 0.372)
|
||||
|
||||
Contact us
|
||||
----------
|
||||
|
||||
We'd love to hear from you if you are using this in your projects! Please drop us a
|
||||
line: [@stat_hat](http://twitter.com/stat_hat) or [contact us here](http://www.stathat.com/docs/contact).
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
Written by Patrick Crosby at [StatHat](http://www.stathat.com). Twitter: [@stat_hat](http://twitter.com/stat_hat)
|
||||
29
third/github.com/stathat/go/example_test.go
Normal file
29
third/github.com/stathat/go/example_test.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (C) 2012 Numerotron Inc.
|
||||
// Use of this source code is governed by an MIT-style license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package stathat_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
stathat "github.com/stathat/go"
|
||||
)
|
||||
|
||||
func ExamplePostEZCountOne() {
|
||||
log.Printf("starting example")
|
||||
stathat.Verbose = true
|
||||
err := stathat.PostEZCountOne("go example test run", "nobody@stathat.com")
|
||||
if err != nil {
|
||||
log.Printf("error posting ez count one: %v", err)
|
||||
return
|
||||
}
|
||||
// If using this with a valid account, this could be helpful in a short script:
|
||||
/*
|
||||
ok := stathat.WaitUntilFinished(5 * time.Second)
|
||||
if ok {
|
||||
fmt.Println("ok")
|
||||
}
|
||||
*/
|
||||
// Output:
|
||||
}
|
||||
605
third/github.com/stathat/go/stathat.go
Normal file
605
third/github.com/stathat/go/stathat.go
Normal file
@ -0,0 +1,605 @@
|
||||
// Copyright (C) 2012 Numerotron Inc.
|
||||
// Use of this source code is governed by an MIT-style license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Copyright 2012 Numerotron Inc.
|
||||
// Use of this source code is governed by an MIT-style license
|
||||
// that can be found in the LICENSE file.
|
||||
//
|
||||
// Developed at www.stathat.com by Patrick Crosby
|
||||
// Contact us on twitter with any questions: twitter.com/stat_hat
|
||||
|
||||
// The stathat package makes it easy to post any values to your StatHat
|
||||
// account.
|
||||
package stathat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const hostname = "api.stathat.com"
|
||||
|
||||
type statKind int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
kcounter statKind = iota
|
||||
kvalue
|
||||
)
|
||||
|
||||
func (sk statKind) classicPath() string {
|
||||
switch sk {
|
||||
case kcounter:
|
||||
return "/c"
|
||||
case kvalue:
|
||||
return "/v"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type apiKind int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
classic apiKind = iota
|
||||
ez
|
||||
)
|
||||
|
||||
func (ak apiKind) path(sk statKind) string {
|
||||
switch ak {
|
||||
case ez:
|
||||
return "/ez"
|
||||
case classic:
|
||||
return sk.classicPath()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type statReport struct {
|
||||
StatKey string
|
||||
UserKey string
|
||||
Value float64
|
||||
Timestamp int64
|
||||
statType statKind
|
||||
apiType apiKind
|
||||
}
|
||||
|
||||
// Reporter describes an interface for communicating with the StatHat API
|
||||
type Reporter interface {
|
||||
PostCount(statKey, userKey string, count int) error
|
||||
PostCountTime(statKey, userKey string, count int, timestamp int64) error
|
||||
PostCountOne(statKey, userKey string) error
|
||||
PostValue(statKey, userKey string, value float64) error
|
||||
PostValueTime(statKey, userKey string, value float64, timestamp int64) error
|
||||
PostEZCountOne(statName, ezkey string) error
|
||||
PostEZCount(statName, ezkey string, count int) error
|
||||
PostEZCountTime(statName, ezkey string, count int, timestamp int64) error
|
||||
PostEZValue(statName, ezkey string, value float64) error
|
||||
PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error
|
||||
WaitUntilFinished(timeout time.Duration) bool
|
||||
}
|
||||
|
||||
// BasicReporter is a StatHat client that can report stat values/counts to the servers.
|
||||
type BasicReporter struct {
|
||||
reports chan *statReport
|
||||
done chan bool
|
||||
client *http.Client
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewReporter returns a new Reporter. You must specify the channel bufferSize and the
|
||||
// goroutine poolSize. You can pass in nil for the transport and it will create an
|
||||
// http transport with MaxIdleConnsPerHost set to the goroutine poolSize. Note if you
|
||||
// pass in your own transport, it's a good idea to have its MaxIdleConnsPerHost be set
|
||||
// to at least the poolSize to allow for effective connection reuse.
|
||||
func NewReporter(bufferSize, poolSize int, transport http.RoundTripper) Reporter {
|
||||
r := new(BasicReporter)
|
||||
if transport == nil {
|
||||
transport = &http.Transport{
|
||||
// Allow for an idle connection per goroutine.
|
||||
MaxIdleConnsPerHost: poolSize,
|
||||
}
|
||||
}
|
||||
r.client = &http.Client{Transport: transport}
|
||||
r.reports = make(chan *statReport, bufferSize)
|
||||
r.done = make(chan bool)
|
||||
r.wg = new(sync.WaitGroup)
|
||||
for i := 0; i < poolSize; i++ {
|
||||
r.wg.Add(1)
|
||||
go r.processReports()
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type statCache struct {
|
||||
counterStats map[string]int
|
||||
valueStats map[string][]float64
|
||||
}
|
||||
|
||||
func (sc *statCache) AverageValue(statName string) float64 {
|
||||
total := 0.0
|
||||
values := sc.valueStats[statName]
|
||||
if len(values) == 0 {
|
||||
return total
|
||||
}
|
||||
for _, value := range values {
|
||||
total += value
|
||||
}
|
||||
return total / float64(len(values))
|
||||
}
|
||||
|
||||
// BatchReporter wraps an existing Reporter in order to implement sending stats
|
||||
// to the StatHat server in batch. The flow is only available for the EZ API.
|
||||
// The following describes how stats are sent:
|
||||
// 1.) PostEZCountOne is called and adds the stat request to a queue.
|
||||
// 2.) PostEZCountOne is called again on the same stat, the value in the queue is incremented.
|
||||
// 3.) After batchInterval amount of time, all stat requests from the queue are
|
||||
// sent to the server.
|
||||
type BatchReporter struct {
|
||||
sync.Mutex
|
||||
r Reporter
|
||||
batchInterval time.Duration
|
||||
caches map[string]*statCache
|
||||
shutdownBatchCh chan struct{}
|
||||
}
|
||||
|
||||
// DefaultReporter is the default instance of *Reporter.
|
||||
var DefaultReporter = NewReporter(100000, 10, nil)
|
||||
|
||||
var testingEnv = false
|
||||
|
||||
type testPost struct {
|
||||
url string
|
||||
values url.Values
|
||||
}
|
||||
|
||||
var testPostChannel chan *testPost
|
||||
|
||||
// The Verbose flag determines if the package should write verbose output to stdout.
|
||||
var Verbose = false
|
||||
|
||||
func setTesting() {
|
||||
testingEnv = true
|
||||
testPostChannel = make(chan *testPost)
|
||||
}
|
||||
|
||||
func newEZStatCount(statName, ezkey string, count int) *statReport {
|
||||
return &statReport{StatKey: statName,
|
||||
UserKey: ezkey,
|
||||
Value: float64(count),
|
||||
statType: kcounter,
|
||||
apiType: ez}
|
||||
}
|
||||
|
||||
func newEZStatValue(statName, ezkey string, value float64) *statReport {
|
||||
return &statReport{StatKey: statName,
|
||||
UserKey: ezkey,
|
||||
Value: value,
|
||||
statType: kvalue,
|
||||
apiType: ez}
|
||||
}
|
||||
|
||||
func newClassicStatCount(statKey, userKey string, count int) *statReport {
|
||||
return &statReport{StatKey: statKey,
|
||||
UserKey: userKey,
|
||||
Value: float64(count),
|
||||
statType: kcounter,
|
||||
apiType: classic}
|
||||
}
|
||||
|
||||
func newClassicStatValue(statKey, userKey string, value float64) *statReport {
|
||||
return &statReport{StatKey: statKey,
|
||||
UserKey: userKey,
|
||||
Value: value,
|
||||
statType: kvalue,
|
||||
apiType: classic}
|
||||
}
|
||||
|
||||
func (sr *statReport) values() url.Values {
|
||||
switch sr.apiType {
|
||||
case ez:
|
||||
return sr.ezValues()
|
||||
case classic:
|
||||
return sr.classicValues()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *statReport) ezValues() url.Values {
|
||||
switch sr.statType {
|
||||
case kcounter:
|
||||
return sr.ezCounterValues()
|
||||
case kvalue:
|
||||
return sr.ezValueValues()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *statReport) classicValues() url.Values {
|
||||
switch sr.statType {
|
||||
case kcounter:
|
||||
return sr.classicCounterValues()
|
||||
case kvalue:
|
||||
return sr.classicValueValues()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sr *statReport) ezCommonValues() url.Values {
|
||||
result := make(url.Values)
|
||||
result.Set("stat", sr.StatKey)
|
||||
result.Set("ezkey", sr.UserKey)
|
||||
if sr.Timestamp > 0 {
|
||||
result.Set("t", sr.timeString())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) classicCommonValues() url.Values {
|
||||
result := make(url.Values)
|
||||
result.Set("key", sr.StatKey)
|
||||
result.Set("ukey", sr.UserKey)
|
||||
if sr.Timestamp > 0 {
|
||||
result.Set("t", sr.timeString())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) ezCounterValues() url.Values {
|
||||
result := sr.ezCommonValues()
|
||||
result.Set("count", sr.valueString())
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) ezValueValues() url.Values {
|
||||
result := sr.ezCommonValues()
|
||||
result.Set("value", sr.valueString())
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) classicCounterValues() url.Values {
|
||||
result := sr.classicCommonValues()
|
||||
result.Set("count", sr.valueString())
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) classicValueValues() url.Values {
|
||||
result := sr.classicCommonValues()
|
||||
result.Set("value", sr.valueString())
|
||||
return result
|
||||
}
|
||||
|
||||
func (sr *statReport) valueString() string {
|
||||
return strconv.FormatFloat(sr.Value, 'g', -1, 64)
|
||||
}
|
||||
|
||||
func (sr *statReport) timeString() string {
|
||||
return strconv.FormatInt(sr.Timestamp, 10)
|
||||
}
|
||||
|
||||
func (sr *statReport) path() string {
|
||||
return sr.apiType.path(sr.statType)
|
||||
}
|
||||
|
||||
func (sr *statReport) url() string {
|
||||
return fmt.Sprintf("https://%s%s", hostname, sr.path())
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count to a stat using DefaultReporter.
|
||||
func PostCount(statKey, userKey string, count int) error {
|
||||
return DefaultReporter.PostCount(statKey, userKey, count)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count to a stat using DefaultReporter at a specific
|
||||
// time.
|
||||
func PostCountTime(statKey, userKey string, count int, timestamp int64) error {
|
||||
return DefaultReporter.PostCountTime(statKey, userKey, count, timestamp)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count of 1 to a stat using DefaultReporter.
|
||||
func PostCountOne(statKey, userKey string) error {
|
||||
return DefaultReporter.PostCountOne(statKey, userKey)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a value to a stat using DefaultReporter.
|
||||
func PostValue(statKey, userKey string, value float64) error {
|
||||
return DefaultReporter.PostValue(statKey, userKey, value)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a value to a stat at a specific time using DefaultReporter.
|
||||
func PostValueTime(statKey, userKey string, value float64, timestamp int64) error {
|
||||
return DefaultReporter.PostValueTime(statKey, userKey, value, timestamp)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count of 1 to a stat using DefaultReporter.
|
||||
func PostEZCountOne(statName, ezkey string) error {
|
||||
return DefaultReporter.PostEZCountOne(statName, ezkey)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count to a stat using DefaultReporter.
|
||||
func PostEZCount(statName, ezkey string, count int) error {
|
||||
return DefaultReporter.PostEZCount(statName, ezkey, count)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count to a stat at a specific time using DefaultReporter.
|
||||
func PostEZCountTime(statName, ezkey string, count int, timestamp int64) error {
|
||||
return DefaultReporter.PostEZCountTime(statName, ezkey, count, timestamp)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a value to a stat using DefaultReporter.
|
||||
func PostEZValue(statName, ezkey string, value float64) error {
|
||||
return DefaultReporter.PostEZValue(statName, ezkey, value)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a value to a stat at a specific time using DefaultReporter.
|
||||
func PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error {
|
||||
return DefaultReporter.PostEZValueTime(statName, ezkey, value, timestamp)
|
||||
}
|
||||
|
||||
// Wait for all stats to be sent, or until timeout. Useful for simple command-
|
||||
// line apps to defer a call to this in main()
|
||||
func WaitUntilFinished(timeout time.Duration) bool {
|
||||
return DefaultReporter.WaitUntilFinished(timeout)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count to a stat.
|
||||
func (r *BasicReporter) PostCount(statKey, userKey string, count int) error {
|
||||
r.add(newClassicStatCount(statKey, userKey, count))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count to a stat at a specific time.
|
||||
func (r *BasicReporter) PostCountTime(statKey, userKey string, count int, timestamp int64) error {
|
||||
x := newClassicStatCount(statKey, userKey, count)
|
||||
x.Timestamp = timestamp
|
||||
r.add(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the classic API, posts a count of 1 to a stat.
|
||||
func (r *BasicReporter) PostCountOne(statKey, userKey string) error {
|
||||
return r.PostCount(statKey, userKey, 1)
|
||||
}
|
||||
|
||||
// Using the classic API, posts a value to a stat.
|
||||
func (r *BasicReporter) PostValue(statKey, userKey string, value float64) error {
|
||||
r.add(newClassicStatValue(statKey, userKey, value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the classic API, posts a value to a stat at a specific time.
|
||||
func (r *BasicReporter) PostValueTime(statKey, userKey string, value float64, timestamp int64) error {
|
||||
x := newClassicStatValue(statKey, userKey, value)
|
||||
x.Timestamp = timestamp
|
||||
r.add(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count of 1 to a stat.
|
||||
func (r *BasicReporter) PostEZCountOne(statName, ezkey string) error {
|
||||
return r.PostEZCount(statName, ezkey, 1)
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count to a stat.
|
||||
func (r *BasicReporter) PostEZCount(statName, ezkey string, count int) error {
|
||||
r.add(newEZStatCount(statName, ezkey, count))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a count to a stat at a specific time.
|
||||
func (r *BasicReporter) PostEZCountTime(statName, ezkey string, count int, timestamp int64) error {
|
||||
x := newEZStatCount(statName, ezkey, count)
|
||||
x.Timestamp = timestamp
|
||||
r.add(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a value to a stat.
|
||||
func (r *BasicReporter) PostEZValue(statName, ezkey string, value float64) error {
|
||||
r.add(newEZStatValue(statName, ezkey, value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Using the EZ API, posts a value to a stat at a specific time.
|
||||
func (r *BasicReporter) PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error {
|
||||
x := newEZStatValue(statName, ezkey, value)
|
||||
x.Timestamp = timestamp
|
||||
r.add(x)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *BasicReporter) processReports() {
|
||||
for sr := range r.reports {
|
||||
if Verbose {
|
||||
log.Printf("posting stat to stathat: %s, %v", sr.url(), sr.values())
|
||||
}
|
||||
|
||||
if testingEnv {
|
||||
if Verbose {
|
||||
log.Printf("in test mode, putting stat on testPostChannel")
|
||||
}
|
||||
testPostChannel <- &testPost{sr.url(), sr.values()}
|
||||
continue
|
||||
}
|
||||
|
||||
resp, err := r.client.PostForm(sr.url(), sr.values())
|
||||
if err != nil {
|
||||
log.Printf("error posting stat to stathat: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if Verbose {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
log.Printf("stathat post result: %s", body)
|
||||
} else {
|
||||
// Read the body even if we don't intend to use it. Otherwise golang won't pool the connection.
|
||||
// See also: http://stackoverflow.com/questions/17948827/reusing-http-connections-in-golang/17953506#17953506
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
}
|
||||
|
||||
resp.Body.Close()
|
||||
}
|
||||
r.wg.Done()
|
||||
}
|
||||
|
||||
func (r *BasicReporter) add(rep *statReport) {
|
||||
select {
|
||||
case r.reports <- rep:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (r *BasicReporter) finish() {
|
||||
close(r.reports)
|
||||
r.wg.Wait()
|
||||
r.done <- true
|
||||
}
|
||||
|
||||
// Wait for all stats to be sent, or until timeout. Useful for simple command-
|
||||
// line apps to defer a call to this in main()
|
||||
func (r *BasicReporter) WaitUntilFinished(timeout time.Duration) bool {
|
||||
go r.finish()
|
||||
select {
|
||||
case <-r.done:
|
||||
return true
|
||||
case <-time.After(timeout):
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// NewBatchReporter creates a batching stat reporter. The interval parameter
|
||||
// specifies how often stats should be posted to the StatHat server.
|
||||
func NewBatchReporter(reporter Reporter, interval time.Duration) Reporter {
|
||||
|
||||
br := &BatchReporter{
|
||||
r: reporter,
|
||||
batchInterval: interval,
|
||||
caches: make(map[string]*statCache),
|
||||
shutdownBatchCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
go br.batchLoop()
|
||||
|
||||
return br
|
||||
}
|
||||
|
||||
func (br *BatchReporter) getEZCache(ezkey string) *statCache {
|
||||
var cache *statCache
|
||||
var ok bool
|
||||
|
||||
// Fetch ezkey cache
|
||||
if cache, ok = br.caches[ezkey]; !ok {
|
||||
cache = &statCache{
|
||||
counterStats: make(map[string]int),
|
||||
valueStats: make(map[string][]float64),
|
||||
}
|
||||
br.caches[ezkey] = cache
|
||||
}
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostEZCount(statName, ezkey string, count int) error {
|
||||
br.Lock()
|
||||
defer br.Unlock()
|
||||
|
||||
// Increment stat by count
|
||||
br.getEZCache(ezkey).counterStats[statName] += count
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostEZCountOne(statName, ezkey string) error {
|
||||
return br.PostEZCount(statName, ezkey, 1)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostEZValue(statName, ezkey string, value float64) error {
|
||||
br.Lock()
|
||||
defer br.Unlock()
|
||||
|
||||
// Update value cache
|
||||
cache := br.getEZCache(ezkey)
|
||||
cache.valueStats[statName] = append(cache.valueStats[statName], value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (br *BatchReporter) batchPost() {
|
||||
|
||||
// Copy and clear cache
|
||||
br.Lock()
|
||||
caches := br.caches
|
||||
br.caches = make(map[string]*statCache)
|
||||
br.Unlock()
|
||||
|
||||
// Post stats
|
||||
for ezkey, cache := range caches {
|
||||
// Post counters
|
||||
for statName, count := range cache.counterStats {
|
||||
br.r.PostEZCount(statName, ezkey, count)
|
||||
}
|
||||
|
||||
// Post values
|
||||
for statName := range cache.valueStats {
|
||||
br.r.PostEZValue(statName, ezkey, cache.AverageValue(statName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (br *BatchReporter) batchLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-br.shutdownBatchCh:
|
||||
return
|
||||
case <-time.After(br.batchInterval):
|
||||
br.batchPost()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostCount(statKey, userKey string, count int) error {
|
||||
return br.r.PostCount(statKey, userKey, count)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostCountTime(statKey, userKey string, count int, timestamp int64) error {
|
||||
return br.r.PostCountTime(statKey, userKey, count, timestamp)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostCountOne(statKey, userKey string) error {
|
||||
return br.r.PostCountOne(statKey, userKey)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostValue(statKey, userKey string, value float64) error {
|
||||
return br.r.PostValue(statKey, userKey, value)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostValueTime(statKey, userKey string, value float64, timestamp int64) error {
|
||||
return br.r.PostValueTime(statKey, userKey, value, timestamp)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostEZCountTime(statName, ezkey string, count int, timestamp int64) error {
|
||||
return br.r.PostEZCountTime(statName, ezkey, count, timestamp)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) PostEZValueTime(statName, ezkey string, value float64, timestamp int64) error {
|
||||
return br.r.PostEZValueTime(statName, ezkey, value, timestamp)
|
||||
}
|
||||
|
||||
func (br *BatchReporter) WaitUntilFinished(timeout time.Duration) bool {
|
||||
// Shut down batch loop
|
||||
close(br.shutdownBatchCh)
|
||||
|
||||
// One last post
|
||||
br.batchPost()
|
||||
|
||||
return br.r.WaitUntilFinished(timeout)
|
||||
}
|
||||
319
third/github.com/stathat/go/stathat_test.go
Normal file
319
third/github.com/stathat/go/stathat_test.go
Normal file
@ -0,0 +1,319 @@
|
||||
// Copyright (C) 2012 Numerotron Inc.
|
||||
// Use of this source code is governed by an MIT-style license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package stathat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewEZStatCount(t *testing.T) {
|
||||
setTesting()
|
||||
x := newEZStatCount("abc", "pc@pc.com", 1)
|
||||
if x == nil {
|
||||
t.Fatalf("expected a StatReport object")
|
||||
}
|
||||
if x.statType != kcounter {
|
||||
t.Errorf("expected counter")
|
||||
}
|
||||
if x.apiType != ez {
|
||||
t.Errorf("expected EZ api")
|
||||
}
|
||||
if x.StatKey != "abc" {
|
||||
t.Errorf("expected abc")
|
||||
}
|
||||
if x.UserKey != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if x.Value != 1.0 {
|
||||
t.Errorf("expected 1.0")
|
||||
}
|
||||
if x.Timestamp != 0 {
|
||||
t.Errorf("expected 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewEZStatValue(t *testing.T) {
|
||||
setTesting()
|
||||
x := newEZStatValue("abc", "pc@pc.com", 3.14159)
|
||||
if x == nil {
|
||||
t.Fatalf("expected a StatReport object")
|
||||
}
|
||||
if x.statType != kvalue {
|
||||
t.Errorf("expected value")
|
||||
}
|
||||
if x.apiType != ez {
|
||||
t.Errorf("expected EZ api")
|
||||
}
|
||||
if x.StatKey != "abc" {
|
||||
t.Errorf("expected abc")
|
||||
}
|
||||
if x.UserKey != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if x.Value != 3.14159 {
|
||||
t.Errorf("expected 3.14159")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClassicStatCount(t *testing.T) {
|
||||
setTesting()
|
||||
x := newClassicStatCount("statkey", "userkey", 1)
|
||||
if x == nil {
|
||||
t.Fatalf("expected a StatReport object")
|
||||
}
|
||||
if x.statType != kcounter {
|
||||
t.Errorf("expected counter")
|
||||
}
|
||||
if x.apiType != classic {
|
||||
t.Errorf("expected CLASSIC api")
|
||||
}
|
||||
if x.StatKey != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if x.UserKey != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if x.Value != 1.0 {
|
||||
t.Errorf("expected 1.0")
|
||||
}
|
||||
if x.Timestamp != 0 {
|
||||
t.Errorf("expected 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewClassicStatValue(t *testing.T) {
|
||||
setTesting()
|
||||
x := newClassicStatValue("statkey", "userkey", 2.28)
|
||||
if x == nil {
|
||||
t.Fatalf("expected a StatReport object")
|
||||
}
|
||||
if x.statType != kvalue {
|
||||
t.Errorf("expected value")
|
||||
}
|
||||
if x.apiType != classic {
|
||||
t.Errorf("expected CLASSIC api")
|
||||
}
|
||||
if x.StatKey != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if x.UserKey != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if x.Value != 2.28 {
|
||||
t.Errorf("expected 2.28")
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLValues(t *testing.T) {
|
||||
setTesting()
|
||||
x := newEZStatCount("abc", "pc@pc.com", 1)
|
||||
v := x.values()
|
||||
if v == nil {
|
||||
t.Fatalf("expected url values")
|
||||
}
|
||||
if v.Get("stat") != "abc" {
|
||||
t.Errorf("expected abc")
|
||||
}
|
||||
if v.Get("ezkey") != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if v.Get("count") != "1" {
|
||||
t.Errorf("expected count of 1")
|
||||
}
|
||||
|
||||
y := newEZStatValue("abc", "pc@pc.com", 3.14159)
|
||||
v = y.values()
|
||||
if v == nil {
|
||||
t.Fatalf("expected url values")
|
||||
}
|
||||
if v.Get("stat") != "abc" {
|
||||
t.Errorf("expected abc")
|
||||
}
|
||||
if v.Get("ezkey") != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if v.Get("value") != "3.14159" {
|
||||
t.Errorf("expected value of 3.14159")
|
||||
}
|
||||
|
||||
a := newClassicStatCount("statkey", "userkey", 1)
|
||||
v = a.values()
|
||||
if v == nil {
|
||||
t.Fatalf("expected url values")
|
||||
}
|
||||
if v.Get("key") != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if v.Get("ukey") != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if v.Get("count") != "1" {
|
||||
t.Errorf("expected count of 1")
|
||||
}
|
||||
|
||||
b := newClassicStatValue("statkey", "userkey", 2.28)
|
||||
v = b.values()
|
||||
if v == nil {
|
||||
t.Fatalf("expected url values")
|
||||
}
|
||||
if v.Get("key") != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if v.Get("ukey") != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if v.Get("value") != "2.28" {
|
||||
t.Errorf("expected value of 2.28")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaths(t *testing.T) {
|
||||
if ez.path(kcounter) != "/ez" {
|
||||
t.Errorf("expected /ez")
|
||||
}
|
||||
if ez.path(kvalue) != "/ez" {
|
||||
t.Errorf("expected /ez")
|
||||
}
|
||||
if classic.path(kcounter) != "/c" {
|
||||
t.Errorf("expected /c")
|
||||
}
|
||||
if classic.path(kvalue) != "/v" {
|
||||
t.Errorf("expected /v")
|
||||
}
|
||||
|
||||
x := newEZStatCount("abc", "pc@pc.com", 1)
|
||||
if x.path() != "/ez" {
|
||||
t.Errorf("expected /ez")
|
||||
}
|
||||
y := newEZStatValue("abc", "pc@pc.com", 3.14159)
|
||||
if y.path() != "/ez" {
|
||||
t.Errorf("expected /ez")
|
||||
}
|
||||
a := newClassicStatCount("statkey", "userkey", 1)
|
||||
if a.path() != "/c" {
|
||||
t.Errorf("expected /c")
|
||||
}
|
||||
b := newClassicStatValue("statkey", "userkey", 2.28)
|
||||
if b.path() != "/v" {
|
||||
t.Errorf("expected /v")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPosts(t *testing.T) {
|
||||
setTesting()
|
||||
PostCountOne("statkey", "userkey")
|
||||
p := <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/c" {
|
||||
t.Errorf("expected classic count url")
|
||||
}
|
||||
if p.values.Get("key") != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if p.values.Get("ukey") != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if p.values.Get("count") != "1" {
|
||||
t.Errorf("expected count of 1")
|
||||
}
|
||||
|
||||
PostCount("statkey", "userkey", 13)
|
||||
p = <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/c" {
|
||||
t.Errorf("expected classic count url")
|
||||
}
|
||||
if p.values.Get("key") != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if p.values.Get("ukey") != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if p.values.Get("count") != "13" {
|
||||
t.Errorf("expected count of 13")
|
||||
}
|
||||
|
||||
PostValue("statkey", "userkey", 9.312)
|
||||
p = <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/v" {
|
||||
t.Errorf("expected classic value url")
|
||||
}
|
||||
if p.values.Get("key") != "statkey" {
|
||||
t.Errorf("expected statkey")
|
||||
}
|
||||
if p.values.Get("ukey") != "userkey" {
|
||||
t.Errorf("expected userkey")
|
||||
}
|
||||
if p.values.Get("value") != "9.312" {
|
||||
t.Errorf("expected value of 9.312")
|
||||
}
|
||||
|
||||
PostEZCountOne("a stat", "pc@pc.com")
|
||||
p = <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/ez" {
|
||||
t.Errorf("expected ez url")
|
||||
}
|
||||
if p.values.Get("stat") != "a stat" {
|
||||
t.Errorf("expected a stat")
|
||||
}
|
||||
if p.values.Get("ezkey") != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if p.values.Get("count") != "1" {
|
||||
t.Errorf("expected count of 1")
|
||||
}
|
||||
|
||||
PostEZCount("a stat", "pc@pc.com", 213)
|
||||
p = <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/ez" {
|
||||
t.Errorf("expected ez url")
|
||||
}
|
||||
if p.values.Get("stat") != "a stat" {
|
||||
t.Errorf("expected a stat")
|
||||
}
|
||||
if p.values.Get("ezkey") != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if p.values.Get("count") != "213" {
|
||||
t.Errorf("expected count of 213")
|
||||
}
|
||||
|
||||
PostEZValue("a stat", "pc@pc.com", 2.13)
|
||||
p = <-testPostChannel
|
||||
if p.url != "https://api.stathat.com/ez" {
|
||||
t.Errorf("expected ez url")
|
||||
}
|
||||
if p.values.Get("stat") != "a stat" {
|
||||
t.Errorf("expected a stat")
|
||||
}
|
||||
if p.values.Get("ezkey") != "pc@pc.com" {
|
||||
t.Errorf("expected pc@pc.com")
|
||||
}
|
||||
if p.values.Get("value") != "2.13" {
|
||||
t.Errorf("expected value of 2.13")
|
||||
}
|
||||
|
||||
PostCountTime("statkey", "userkey", 13, 100000)
|
||||
p = <-testPostChannel
|
||||
if p.values.Get("t") != "100000" {
|
||||
t.Errorf("expected t value of 100000, got %s", p.values.Get("t"))
|
||||
}
|
||||
|
||||
PostValueTime("statkey", "userkey", 9.312, 200000)
|
||||
p = <-testPostChannel
|
||||
if p.values.Get("t") != "200000" {
|
||||
t.Errorf("expected t value of 200000, got %s", p.values.Get("t"))
|
||||
}
|
||||
|
||||
PostEZCountTime("a stat", "pc@pc.com", 213, 300000)
|
||||
p = <-testPostChannel
|
||||
if p.values.Get("t") != "300000" {
|
||||
t.Errorf("expected t value of 300000, got %s", p.values.Get("t"))
|
||||
}
|
||||
|
||||
PostEZValueTime("a stat", "pc@pc.com", 2.13, 400000)
|
||||
p = <-testPostChannel
|
||||
if p.values.Get("t") != "400000" {
|
||||
t.Errorf("expected t value of 400000, got %s", p.values.Get("t"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user