157 lines
3.2 KiB
Go
157 lines
3.2 KiB
Go
// Simple wrapper for rrdtool C library
|
|
package rrdlite
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
type Error string
|
|
|
|
func (e Error) Error() string {
|
|
return string(e)
|
|
}
|
|
|
|
type cstring []byte
|
|
|
|
func newCstring(s string) cstring {
|
|
cs := make(cstring, len(s)+1)
|
|
copy(cs, s)
|
|
return cs
|
|
}
|
|
|
|
func (cs cstring) p() unsafe.Pointer {
|
|
if len(cs) == 0 {
|
|
return nil
|
|
}
|
|
return unsafe.Pointer(&cs[0])
|
|
}
|
|
|
|
func (cs cstring) String() string {
|
|
return string(cs[:len(cs)-1])
|
|
}
|
|
|
|
func join(args []interface{}) string {
|
|
sa := make([]string, len(args))
|
|
for i, a := range args {
|
|
var s string
|
|
switch v := a.(type) {
|
|
case time.Time:
|
|
s = i64toa(v.Unix())
|
|
default:
|
|
s = fmt.Sprint(v)
|
|
}
|
|
sa[i] = s
|
|
}
|
|
return strings.Join(sa, ":")
|
|
}
|
|
|
|
type Creator struct {
|
|
filename string
|
|
start time.Time
|
|
step uint
|
|
args []string
|
|
}
|
|
|
|
// NewCreator returns new Creator object. You need to call Create to really
|
|
// create database file.
|
|
// filename - name of database file
|
|
// start - don't accept any data timed before or at time specified
|
|
// step - base interval in seconds with which data will be fed into RRD
|
|
func NewCreator(filename string, start time.Time, step uint) *Creator {
|
|
return &Creator{
|
|
filename: filename,
|
|
start: start,
|
|
step: step,
|
|
}
|
|
}
|
|
|
|
func (c *Creator) DS(name, compute string, args ...interface{}) {
|
|
c.args = append(c.args, "DS:"+name+":"+compute+":"+join(args))
|
|
}
|
|
|
|
func (c *Creator) RRA(cf string, args ...interface{}) {
|
|
c.args = append(c.args, "RRA:"+cf+":"+join(args))
|
|
}
|
|
|
|
// Create creates new database file. If overwrite is true it overwrites
|
|
// database file if exists. If overwrite is false it returns error if file
|
|
// exists (you can use os.IsExist function to check this case).
|
|
func (c *Creator) Create(overwrite bool) error {
|
|
if !overwrite {
|
|
f, err := os.OpenFile(
|
|
c.filename,
|
|
os.O_WRONLY|os.O_CREATE|os.O_EXCL,
|
|
0666,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Close()
|
|
}
|
|
return c.create()
|
|
}
|
|
|
|
// Use cstring and unsafe.Pointer to avoid alocations for C calls
|
|
|
|
type Updater struct {
|
|
filename cstring
|
|
template cstring
|
|
|
|
args []string
|
|
}
|
|
|
|
func NewUpdater(filename string) *Updater {
|
|
return &Updater{filename: newCstring(filename)}
|
|
}
|
|
|
|
func (u *Updater) SetTemplate(dsName ...string) {
|
|
u.template = newCstring(strings.Join(dsName, ":"))
|
|
}
|
|
|
|
// Cache chaches data for later save using Update(). Use it to avoid
|
|
// open/read/write/close for every update.
|
|
func (u *Updater) Cache(args ...interface{}) {
|
|
u.args = append(u.args, join(args))
|
|
}
|
|
|
|
// Update saves data in RRDB.
|
|
// Without args Update saves all subsequent updates buffered by Cache method.
|
|
// If you specify args it saves them immediately.
|
|
func (u *Updater) Update(args ...interface{}) error {
|
|
if len(args) != 0 {
|
|
a := make([]string, 1)
|
|
a[0] = join(args)
|
|
return u.update(a)
|
|
} else if len(u.args) != 0 {
|
|
err := u.update(u.args)
|
|
u.args = nil
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
maxUint = ^uint(0)
|
|
maxInt = int(maxUint >> 1)
|
|
minInt = -maxInt - 1
|
|
)
|
|
|
|
type FetchResult struct {
|
|
Filename string
|
|
Cf string
|
|
Start time.Time
|
|
End time.Time
|
|
Step time.Duration
|
|
DsNames []string
|
|
RowCnt int
|
|
values []float64
|
|
}
|
|
|
|
func (r *FetchResult) ValueAt(dsIndex, rowIndex int) float64 {
|
|
return r.values[len(r.DsNames)*rowIndex+dsIndex]
|
|
}
|