diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0173fce --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: go + +go: + - 1.6.2 + - tip + +sudo: false + +os: + - linux + +before_install: + - go get pkg.re/essentialkaos/ek.v1 + +script: + - go build examples/annotations_example.go + - go build examples/async_example.go + - go build examples/basic_example.go + - go build examples/collector_example.go diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1c9a8d7 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +# Dummy make file +# \ No newline at end of file diff --git a/changelog.md b/changelog.md index 47874b4..837fc6f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ ## Changelog +#### v2.0.0 + +* Added usage examples +* Using values instead pointers for measurements (gauge/counter) structs +* Now `librato.AddMetrics` supports sending many metrics at once +* Code refactoring + #### v1.2.1 * Added pkg.re usage diff --git a/examples/annotations_example.go b/examples/annotations_example.go new file mode 100644 index 0000000..c5fe9e7 --- /dev/null +++ b/examples/annotations_example.go @@ -0,0 +1,55 @@ +package main + +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "time" + + "github.com/essentialkaos/librato" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func main() { + librato.Mail = "mail@domain.com" + librato.Token = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" + + // Add annotation to example:annotation_1 stream + errs := librato.AddAnnotation("example:annotation_1", + librato.Annotation{ + Title: "Deploy v31", + Source: "server123", + Desc: "Revision: abcd1234", + Links: []string{ + "https://build-service.com/build/31", + "https://git-repo.com/commit/abcd1234", + }, + }, + ) + + if len(errs) != 0 { + fmt.Println("Errors:") + + for _, err := range errs { + fmt.Printf(" %v\n", err) + } + } else { + fmt.Println("Annotation added") + } + + time.Sleep(time.Minute) + + // Delete stream example:annotation_1 with all annotations + errs = librato.DeleteAnnotations("example:annotation_1") + + if len(errs) != 0 { + fmt.Println("Errors:") + + for _, err := range errs { + fmt.Printf(" %v\n", err) + } + } else { + fmt.Println("Annotation deleted") + } +} diff --git a/examples/async_example.go b/examples/async_example.go new file mode 100644 index 0000000..38d22e5 --- /dev/null +++ b/examples/async_example.go @@ -0,0 +1,40 @@ +package main + +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "time" + + "pkg.re/essentialkaos/ek.v1/rand" + + "github.com/essentialkaos/librato" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func main() { + librato.Mail = "mail@domain.com" + librato.Token = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" + + // Create struct for async sending metrics data + // With this preferences metrics will be sended to Librato once + // a minute or if queue size reached 60 elements + metrics, err := librato.NewMetrics(time.Minute, 60) + + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + + for { + metrics.Add( + librato.Gauge{ + Name: "example:gauge_1", + Value: rand.Int(1000), + }, + ) + + time.Sleep(15 * time.Second) + } +} diff --git a/examples/basic_example.go b/examples/basic_example.go new file mode 100644 index 0000000..7a5eefa --- /dev/null +++ b/examples/basic_example.go @@ -0,0 +1,49 @@ +package main + +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "time" + + "pkg.re/essentialkaos/ek.v1/rand" + + "github.com/essentialkaos/librato" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func main() { + librato.Mail = "mail@domain.com" + librato.Token = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" + + for { + errs := librato.AddMetric( + librato.Gauge{ + Name: "example:gauge_1", + Value: rand.Int(1000), + }, + librato.Gauge{ + Name: "example:gauge_2", + Value: float64(rand.Int(1000)) / float64(rand.Int(20)), + Source: "go_librato_example", + }, + librato.Counter{ + Name: "example:counter_1", + Value: rand.Int(1000), + }, + ) + + if len(errs) != 0 { + fmt.Println("Errors:") + + for _, err := range errs { + fmt.Printf(" %v\n", err) + } + } else { + fmt.Println("Data sended to Librato Metrics") + } + + time.Sleep(time.Minute) + } +} diff --git a/examples/collector_example.go b/examples/collector_example.go new file mode 100644 index 0000000..f8da514 --- /dev/null +++ b/examples/collector_example.go @@ -0,0 +1,54 @@ +package main + +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "fmt" + "time" + + "pkg.re/essentialkaos/ek.v1/rand" + + "github.com/essentialkaos/librato" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +func main() { + librato.Mail = "mail@domain.com" + librato.Token = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234" + + collector := librato.NewCollector(time.Minute, collectSomeMetrics) + collector.ErrorHandler = errorHandler + + for { + time.Sleep(time.Hour) + } +} + +func collectSomeMetrics() []librato.Measurement { + fmt.Println("Metrics collected") + + return []librato.Measurement{ + librato.Gauge{ + Name: "example:gauge_1", + Value: rand.Int(1000), + }, + librato.Gauge{ + Name: "example:gauge_2", + Value: float64(rand.Int(1000)) / float64(rand.Int(20)), + Source: "go_librato_example", + }, + librato.Counter{ + Name: "example:counter_1", + Value: rand.Int(1000), + }, + } +} + +func errorHandler(errs []error) { + fmt.Println("Errors:") + + for _, err := range errs { + fmt.Printf(" %v\n", err) + } +} diff --git a/librato.go b/librato.go index 51adf06..5af01f0 100644 --- a/librato.go +++ b/librato.go @@ -23,7 +23,7 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // // VERSION contains current version of librato package and used as part of User-Agent -const VERSION = "1.2.1" +const VERSION = "2.0.0" // ////////////////////////////////////////////////////////////////////////////////// // @@ -187,9 +187,9 @@ type Annotation struct { // ////////////////////////////////////////////////////////////////////////////////// // -type mesData struct { - Gauges []*Gauge `json:"gauges,omitempty"` - Counters []*Counter `json:"counters,omitempty"` +type measurements struct { + Gauges []Gauge `json:"gauges,omitempty"` + Counters []Counter `json:"counters,omitempty"` } type paramsErrorMap struct { @@ -241,9 +241,9 @@ func NewMetrics(period time.Duration, maxQueueSize int) (*Metrics, error) { metrics := &Metrics{ maxQueueSize: maxQueueSize, period: period, - lastSendingDate: -1, initialized: true, queue: make([]Measurement, 0), + lastSendingDate: -1, } err := validateMetrics(metrics) @@ -266,8 +266,8 @@ func NewMetrics(period time.Duration, maxQueueSize int) (*Metrics, error) { func NewCollector(period time.Duration, collectFunc func() []Measurement) *Collector { collector := &Collector{ period: period, - lastSendingDate: -1, collectFunc: collectFunc, + lastSendingDate: -1, } if sources == nil { @@ -281,28 +281,38 @@ func NewCollector(period time.Duration, collectFunc func() []Measurement) *Colle } // AddMetric synchronously send metric to librato -func AddMetric(m Measurement) []error { - err := m.Validate() +func AddMetric(m ...Measurement) []error { + data := &measurements{} - if err != nil { - return []error{err} + var errs []error + + for _, metric := range m { + err := metric.Validate() + + if err != nil { + errs = append(errs, err) + } } - data := &mesData{} + if len(errs) != 0 { + return errs + } - switch m.(type) { - case *Gauge: - data.Gauges = append(data.Gauges, m.(*Gauge)) + for _, metric := range m { + switch metric.(type) { + case Gauge: + data.Gauges = append(data.Gauges, metric.(Gauge)) - case *Counter: - data.Counters = append(data.Counters, m.(*Counter)) + case Counter: + data.Counters = append(data.Counters, metric.(Counter)) + } } return execRequest(req.POST, APIEndpoint+"/v1/metrics/", data) } // AddAnnotation synchronously send annotation to librato -func AddAnnotation(stream string, a *Annotation) []error { +func AddAnnotation(stream string, a Annotation) []error { if stream == "" { return []error{errors.New("Stream name can't be empty")} } @@ -328,7 +338,7 @@ func DeleteAnnotations(stream string) []error { // ////////////////////////////////////////////////////////////////////////////////// // // Add adds gauge to sending queue -func (mt *Metrics) Add(m Measurement) error { +func (mt *Metrics) Add(m ...Measurement) error { var err error err = validateMetrics(mt) @@ -337,13 +347,15 @@ func (mt *Metrics) Add(m Measurement) error { return err } - err = m.Validate() + for _, metric := range m { + err = metric.Validate() - if err != nil { - return err + if err != nil { + return err + } } - mt.queue = append(mt.queue, m) + mt.queue = append(mt.queue, m...) if len(mt.queue) >= mt.maxQueueSize { mt.Send() @@ -391,7 +403,7 @@ func (cl *Collector) Send() []error { ms := cl.collectFunc() - if ms == nil || len(ms) == 0 { + if len(ms) == 0 { return []error{} } @@ -408,12 +420,12 @@ func (cl *Collector) Send() []error { // ////////////////////////////////////////////////////////////////////////////////// // // Validate validates gauge struct -func (g *Gauge) Validate() error { +func (g Gauge) Validate() error { return validateGauge(g) } // Validate validates gauge struct -func (c *Counter) Validate() error { +func (c Counter) Validate() error { return validateCounter(c) } @@ -488,19 +500,19 @@ func sendingLoop() { // convertMeasurementSlice convert slice with measurements to struct // with counters and gauges slices -func convertMeasurementSlice(queue []Measurement) *mesData { - result := &mesData{} +func convertMeasurementSlice(queue []Measurement) *measurements { + result := &measurements{} now := time.Now().Unix() for _, m := range queue { switch m.(type) { - case *Gauge: + case Gauge: if result.Gauges == nil { - result.Gauges = make([]*Gauge, 0) + result.Gauges = make([]Gauge, 0) } - gauge := m.(*Gauge) + gauge := m.(Gauge) if gauge.MeasureTime != 0 { gauge.MeasureTime = now @@ -508,12 +520,12 @@ func convertMeasurementSlice(queue []Measurement) *mesData { result.Gauges = append(result.Gauges, gauge) - case *Counter: + case Counter: if result.Counters == nil { - result.Counters = make([]*Counter, 0) + result.Counters = make([]Counter, 0) } - counter := m.(*Counter) + counter := m.(Counter) if counter.MeasureTime != 0 { counter.MeasureTime = now @@ -570,7 +582,7 @@ func validateMetrics(m *Metrics) error { } // validateCounter validate counter struct -func validateCounter(c *Counter) error { +func validateCounter(c Counter) error { if c.Name == "" { return errors.New("Counter property Name can't be empty") } @@ -589,7 +601,7 @@ func validateCounter(c *Counter) error { } // validateGauge validate gauge struct -func validateGauge(g *Gauge) error { +func validateGauge(g Gauge) error { if g.Name == "" { return errors.New("Gauge property Name can't be empty") } @@ -638,7 +650,7 @@ func validateGauge(g *Gauge) error { } // validateAnotation validate annotation struct -func validateAnotation(a *Annotation) error { +func validateAnotation(a Annotation) error { if a.Title == "" { return errors.New("Annotation property Title can't be empty") } diff --git a/readme.md b/readme.md index 0acff3d..54b84be 100644 --- a/readme.md +++ b/readme.md @@ -1,118 +1,24 @@ -### Librato [![GoDoc](https://godoc.org/pkg.re/essentialkaos/librato.v1?status.svg)](https://godoc.org/pkg.re/essentialkaos/librato.v1) +# Librato [![GoDoc](https://godoc.org/pkg.re/essentialkaos/librato.v2?status.svg)](https://godoc.org/pkg.re/essentialkaos/librato.v2) [![Build Status](https://travis-ci.org/essentialkaos/librato.svg?branch=master)](https://travis-ci.org/essentialkaos/librato) Package for working with [Librato Metrics](https://www.librato.com) API from go code. -#### Installation +* [Installation](#installation) +* [Examples](#examples) +* [License](#license) + +### Installation ```` -go get pkg.re/essentialkaos/librato.v1 +go get pkg.re/essentialkaos/librato.v2 ```` -#### Status - -This package is unnder heavy construction, please do not use in production code. - -#### Example - -```Go -package main - -// ////////////////////////////////////////////////////////////////////////////////// // - -import ( - "os" - "time" - - "pkg.re/essentialkaos/librato.v1" -) - -// ////////////////////////////////////////////////////////////////////////////////// // - -func main() { - - // Set auth credentials which will be used for all actions - librato.Mail = "mail@domain.com" - librato.Token = "mysupertokenhere" - - var errs []error - - // Add annotation to service:annotation stream - errs = librato.AddAnnotation("service:annotation", - &librato.Annotation{ - Title: "Deploy v31", - Source: "server123", - Desc: "Revision: abcd1234", - Links: []string{ - "https://build-service.com/build/31", - "https://git-repo.com/commit/abcd1234", - }, - }, - ) - - // Exit with 1 if we have errors - if len(errs) != 0 { - os.Exit(1) - } - - // Delete stream service:annotation with all annotations - errs = librato.DeleteAnnotations("service:annotation") - - if len(errs) != 0 { - os.Exit(1) - } - - // Add counter - errs = librato.AddMetric( - &librato.Counter{ - Name: "service:random1", - Value: 345, - }, - ) - - if len(errs) != 0 { - os.Exit(1) - } - - // Add gauge - errs = librato.AddMetric( - &librato.Gauge{ - Name: "service:random2", - Value: 45.2, - }, - ) - - if len(errs) != 0 { - os.Exit(1) - } - - // Create struct for async sending metrics data - // With this preferences metrics will be sended to Librato once - // a minute or if queue size reached 60 elements - metrics, err := librato.NewMetrics(time.Minute, 60) - - if err != nil { - os.Exit(1) - } - - metrics.Add( - &librato.Counter{ - Name: "service:random1", - Value: 345, - }, - ) - - metrics.Add( - &librato.Gauge{ - Name: "service:random2", - Value: 45.2, - }, - ) +### Examples - // Force sending metrics before exit - metrics.Send() -} -``` +* [Basic Usage](examples/basic_example.go) +* [Metrics Collector](examples/collector_example.go) +* [Async Sending](examples/async_example.go) +* [Annotations](examples/annotations_example.go) -#### License +### License [EKOL](https://essentialkaos.com/ekol)