未验证 提交 e9e1f16b 编写于 作者: Y youyong205 提交者: GitHub

Merge pull request #1314 from stdrickforce/gocat

Gocat initialization
# Cat Client Golang
Gocat supports Go 1.8+
Gocat is highly dependent on `ccat`. (through CGO)
As we using the thread local to storage the transaction stack in `ccat`, which is neccessary to build message tree. It's hard for us to let it work approriately with goroutines. (Because a goroutine may run in different threads, due to the MPG model)
So we don't support `message tree` in this version. Don't worry, we are still working on it and have some great ideas at the moment.
## Installation
### via go get
```bash
$ go get github.com/dianping/cat/lib/go/...
```
## Initialization
First of all, you have to create `/data/appdatas/cat` directory, read and write permission is required (0644).`/data/applogs/cat` is also required if you'd like to preserve a debug log, it can be very useful while debugging.
And create a config file `/data/appdatas/cat/client.xml` with the following contents.
```xml
<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<server ip="<cat server ip address>" port="2280" http-port="8080" />
</servers>
</config>
```
Don't forget to change the `<cat server IP address>` to your own after you copy and paste the contents.
And then you can initialize gocat with following codes:
```c
import (
"gocat"
)
func init() {
gocat.Init("appkey")
}
```
> Only English characters, numbers, underscore and dash is allowed in appkey.
## Documentation
package ccat
/*
#cgo CFLAGS: -I./include
#cgo darwin LDFLAGS: -L${SRCDIR}/darwin/ -lcatclientstatic -Wl,-rpath,./darwin
#cgo !darwin LDFLAGS: -L${SRCDIR}/linux/ -lcatclientstatic -lm -Wl,-rpath,./linux
#include <stdlib.h>
#include "ccat.h"
*/
import "C"
import (
"runtime"
"sync"
"unsafe"
"message"
)
var ch = make(chan interface{}, 128)
var wg sync.WaitGroup
func Init(domain string) {
var (
c_language = C.CString("golang")
c_language_version = C.CString(runtime.Version())
c_domain = C.CString(domain)
)
defer C.free(unsafe.Pointer(c_language))
defer C.free(unsafe.Pointer(c_language_version))
defer C.free(unsafe.Pointer(c_domain))
C.catSetLanguageBinding(c_language, c_language_version)
C.catClientInit(c_domain)
}
func Background() {
// We need running ccat functions on the same thread due to ccat is using a thread local.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
wg.Add(1)
defer wg.Done()
for m := range ch {
switch m := m.(type) {
case *message.Transaction:
LogTransaction(m)
case *message.Event:
LogEvent(m)
}
}
}
func Shutdown() {
close(ch)
}
func Wait() {
wg.Wait()
}
func ShutdownAndWait() {
Shutdown()
Wait()
}
func Send(m message.Messager) {
ch <- m
}
func LogTransaction(trans *message.Transaction) {
var (
ctype = C.CString(trans.Type)
cname = C.CString(trans.Name)
cstatus = C.CString(trans.Status)
cdata = C.CString(trans.GetData().String())
)
defer C.free(unsafe.Pointer(ctype))
defer C.free(unsafe.Pointer(cname))
defer C.free(unsafe.Pointer(cstatus))
defer C.free(unsafe.Pointer(cdata))
C.callLogTransaction(
ctype,
cname,
cstatus,
cdata,
C.ulonglong(trans.GetTimestamp()),
C.ulonglong(trans.GetTimestamp()),
C.ulonglong(trans.DurationMs),
)
}
func LogEvent(event *message.Event) {
var (
ctype = C.CString(event.Type)
cname = C.CString(event.Name)
cstatus = C.CString(event.Status)
cdata = C.CString(event.GetData().String())
)
defer C.free(unsafe.Pointer(ctype))
defer C.free(unsafe.Pointer(cname))
defer C.free(unsafe.Pointer(cstatus))
defer C.free(unsafe.Pointer(cdata))
C.logEventWithTime(
ctype,
cname,
cstatus,
cdata,
C.ulonglong(event.GetTimestamp()),
)
}
func LogBatchTransaction(m_type, m_name string, m_count, m_error, m_sum int) {
var (
ctype = C.CString(m_type)
cname = C.CString(m_name)
)
defer C.free(unsafe.Pointer(ctype))
defer C.free(unsafe.Pointer(cname))
C.logBatchTransaction(
ctype,
cname,
C.int(m_count),
C.int(m_error),
C.ulonglong(m_sum),
)
}
func LogBatchEvent(m_type, m_name string, m_count, m_error int) {
var (
ctype = C.CString(m_type)
cname = C.CString(m_name)
)
defer C.free(unsafe.Pointer(ctype))
defer C.free(unsafe.Pointer(cname))
C.logBatchEvent(
ctype,
cname,
C.int(m_count),
C.int(m_error),
)
}
func LogMetricForCount(m_name string, m_count int) {
var cname = C.CString(m_name)
defer C.free(unsafe.Pointer(cname))
C.logMetricForCountQuantity(
cname,
C.int(m_count),
)
}
func LogMetricForDurationMs(m_name string, duration int64) {
var cname = C.CString(m_name)
defer C.free(unsafe.Pointer(cname))
C.logMetricForDuration(
cname,
C.ulonglong(duration),
)
}
func LogMetricForCountWithTags(m_name string, count int64, tags map[string]string) {
var (
cname = C.CString(m_name)
helper = C.CatBuildMetricHelper(cname)
)
defer C.free(unsafe.Pointer(cname))
// defer C.free(unsafe.Pointer(helper))
for key, val := range tags {
var (
ckey = C.CString(key)
cval = C.CString(val)
)
C.callAddTag(helper, ckey, cval)
C.free(unsafe.Pointer(ckey))
C.free(unsafe.Pointer(cval))
}
C.callCount(helper, C.int(count))
}
func LogMetricForDurationMsWithTags(m_name string, duration int64, tags map[string]string) {
var (
cname = C.CString(m_name)
helper = C.CatBuildMetricHelper(cname)
)
defer C.free(unsafe.Pointer(cname))
// defer C.free(unsafe.Pointer(helper))
for key, val := range tags {
var (
ckey = C.CString(key)
cval = C.CString(val)
)
C.callAddTag(helper, ckey, cval)
C.free(unsafe.Pointer(ckey))
C.free(unsafe.Pointer(cval))
}
C.callDuration(helper, C.int(duration))
}
#include "cat/client.h"
#include "cat/metric_helper.h"
void callAddTag(CatMetricHelper *helper, const char* key, const char* val) {
helper->AddTag(helper, key, val);
}
void callCount(CatMetricHelper *helper, int c) {
helper->AddCount(helper, c);
}
void callDuration(CatMetricHelper *helper, int d) {
helper->AddDuration(helper, d);
}
void callLogTransaction(
const char* type,
const char* name,
const char* status,
const char* data,
unsigned long long timestampMs,
unsigned long long durationStartMs,
unsigned long long durationMs
) {
CatTransaction *t = newTransaction(type, name);
t->setStatus((CatMessage*) t, status);
t->addDataPair((CatMessage*) t, data);
setTransactionTimestamp(t, timestampMs);
t->setDurationInMillis(t, durationMs);
t->setComplete((CatMessage*) t);
return;
}
#ifndef CAT_CLIENT_C_CLIENT_H
#define CAT_CLIENT_C_CLIENT_H
#include "client_common.h"
#include "heartbeat.h"
#include "event.h"
#include "trace.h"
#include "metric.h"
#include "metric_helper.h"
#include "transaction.h"
#define CAT_OK 1
#define CAT_ERR 0
#ifdef __cplusplus
extern "C" {
#endif
/**
* Client Apis
*/
CATCLIENT_EXPORT int catSetLanguageBinding(const char* language, const char* client_version);
CATCLIENT_EXPORT int catDisableHeartbeat();
/**
* Designed for python / nodejs which run multiple instances on same machine.
*/
CATCLIENT_EXPORT void catClientInitWithSingleProcessModel(); // for nodejs
CATCLIENT_EXPORT int catClientInit(const char *domain);
CATCLIENT_EXPORT int catClientDestroy();
/**
* Transaction Apis
*/
CATCLIENT_EXPORT CatTransaction *newTransaction(const char *type, const char *name);
CATCLIENT_EXPORT CatTransaction *newTransactionWithDuration(const char *type, const char *name, unsigned long long durationMs);
CATCLIENT_EXPORT void newCompletedTransactionWithDuration(const char *type, const char*name, unsigned long long durationMs);
CATCLIENT_EXPORT void logTransaction(const char *type, const char *name, const char *status, const char *nameValuePairs, unsigned long long durationMs);
CATCLIENT_EXPORT void logBatchTransaction(const char *type, const char *name, int count, int error, unsigned long long sum);
CATCLIENT_EXPORT void setTransactionStatus(CatTransaction *transaction, const char *status);
CATCLIENT_EXPORT void setTransactionTimestamp(CatTransaction *transaction, unsigned long long timestampMs);
CATCLIENT_EXPORT void completeTransaction(CatTransaction *transaction);
CATCLIENT_EXPORT void addTransactionDataPair(CatTransaction *transaction, const char *data);
/**
* Message Common Apis
*/
CATCLIENT_EXPORT void setMessageStatus(CatMessage *message, const char *status);
CATCLIENT_EXPORT void setMessageTimestamp(CatMessage *message, unsigned long long timestampMs);
CATCLIENT_EXPORT void completeMessage(CatMessage *message);
CATCLIENT_EXPORT void addMessageData(CatHeartBeat *message, const char *data);
/**
* Event Apis
*/
CATCLIENT_EXPORT CatEvent *newEvent(const char *type, const char *name);
CATCLIENT_EXPORT void logEvent(const char *type, const char *name, const char *status, const char *nameValuePairs);
CATCLIENT_EXPORT void logBatchEvent(const char *type, const char *name, int count, int error);
CATCLIENT_EXPORT void logEventWithTime(const char *type, const char *name, const char *status, const char *nameValuePairs, unsigned long long durationMs);
CATCLIENT_EXPORT void logError(const char *msg, const char *errStr);
/**
* Heartbeat Apis
*/
CATCLIENT_EXPORT CatHeartBeat *newHeartBeat(const char *type, const char *name);
/**
* @deprecated
*/
CATCLIENT_EXPORT void inline setHeartbeatStatus(CatHeartBeat *heartBeat, const char *status) {
setMessageStatus(heartBeat, status);
}
/**
* @deprecated
*/
CATCLIENT_EXPORT void inline completeHeartbeat(CatHeartBeat *heartBeat) {
completeMessage(heartBeat);
}
/**
* @deprecated
*/
CATCLIENT_EXPORT void inline addHeartbeatDataPair(CatHeartBeat *heartBeat, const char *data) {
addMessageData(heartBeat, data);
}
/**
* Metric Apis
*/
CATCLIENT_EXPORT CatMetric *newMetric(const char *type, const char *name);
CATCLIENT_EXPORT void logMetricForCount(const char *name);
CATCLIENT_EXPORT void logMetricForCountQuantity(const char *name, int quantity);
CATCLIENT_EXPORT void logMetricForDuration(const char *name, unsigned long long durationMs);
CATCLIENT_EXPORT void logMetricForLatestValue(const char *name, int quantity);
CATCLIENT_EXPORT void addMetricTag(CatMetricHelper *pHelper, const char *key, const char *val);
CATCLIENT_EXPORT void addMetricName(CatMetricHelper *pHelper, const char *name);
CATCLIENT_EXPORT void addMetricCount(CatMetricHelper *pHelper, int count);
CATCLIENT_EXPORT void addMetricDuration(CatMetricHelper *pHelper, unsigned long long durationMs);
/**
* Trace Apis
*/
#define logTrace(type, name, status, nameValuePairs) logTraceWithCodeLocation((type), (name), (status), (nameValuePairs), \
__FILE__, __FUNCTION__, __LINE__)
CATCLIENT_EXPORT void logTraceWithCodeLocation(const char *type, const char *name, const char *status,
const char *nameValuePairs, const char *fileName,
const char *funcationName, int lineNo);
#define logErrorTrace(type, name, data) logErrorWithCodeLocation((type), (name), (data), \
__FILE__, __FUNCTION__, __LINE__)
CATCLIENT_EXPORT void logErrorWithCodeLocation(const char *type, const char *name,
const char *data, const char *fileName, const char *funcationName,
int lineNo);
/**
* Message Id Apis
*/
CATCLIENT_EXPORT char *createMessageId();
CATCLIENT_EXPORT char *createRemoteServerMessageId(const char *domain);
CATCLIENT_EXPORT char *getThreadLocalMessageTreeId();
CATCLIENT_EXPORT char *getThreadLocalMessageTreeRootId();
CATCLIENT_EXPORT char *getThreadLocalMessageTreeParentId();
CATCLIENT_EXPORT void setThreadLocalMessageTreeId(char *messageId, char *rootMessageId, char *parentMessageId);
#ifdef __cplusplus
}
#endif
#endif //CAT_CLIENT_C_CLIENT_H
#ifndef CAT_CLIENT_C_CLIENT_COMMON_H
#define CAT_CLIENT_C_CLIENT_COMMON_H
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/**
* client export
*/
#ifdef WIN32
#ifdef CCATCLIENT_EXPORTS
#define CATCLIENT_EXPORT __declspec(dllexport)
#else
#define CATCLIENT_EXPORT __declspec(dllimport)
#endif
#else
#define CATCLIENT_EXPORT
#endif
/**
* inline
*/
#ifdef WIN32
#define inline __inline
#endif
#define CAT_SUCCESS "0"
#define CAT_SUCCESS_CHAR '0'
#define CAT_ERROR "-1"
#define CAT_FAIL "FAIL"
#define catChecktPtr(ptr) catChecktPtrWithName((ptr), (#ptr))
int isCatEnabled();
void catChecktPtrWithName(void *, char *);
static inline unsigned long long catTrimToHour(unsigned long long timeMs) {
return timeMs / (3600 * 1000);
}
// buf size must >= 32
static inline char * catItoA(int val, char * buf, int radix)
{
if (radix < 2 || radix > 16)
{
return NULL;
}
char * out = buf;
char sign = '\0';
if (val < 0)
{
val = -val;
sign = '-';
}
else if (val == 0)
{
buf[0] = '0';
buf[1] = '\0';
return buf;
}
int quotient = val;
buf[31] = '\0';
int i = 30;
for (; quotient && i; --i, quotient /= radix)
{
buf[i] = "0123456789abcdef"[quotient % radix];
}
if (sign == '-' && radix == 10)
{
buf[i] = sign;
--buf[i];
}
memcpy(buf, buf + i + 1, 31 - i);
return buf;
}
static inline int catAtoI(char * buf, int radix, int * pVal)
{
if (pVal == NULL || buf == NULL)
{
return 0;
}
char *eptr = NULL;
errno = 0;
*pVal = strtol(buf, &eptr, radix);
if (eptr == NULL || eptr[0] != '\0' || errno == ERANGE)
{
return 0;
}
return 1;
}
#ifdef WIN32
#define CATTHREADLOCAL __declspec(thread)
#elif defined(__linux__) || defined(__APPLE__)
#define CATTHREADLOCAL __thread
#else
#define CATTHREADLOCAL
#endif
#endif //CAT_CLIENT_C_CLIENT_COMMON_H
#ifndef CAT_CLIENT_C_EVENT_H
#define CAT_CLIENT_C_EVENT_H
#include "message.h"
typedef CatMessage CatEvent;
CatEvent * createCatEvent(const char *type, const char * name);
#endif //CAT_CLIENT_C_EVENT_H
#ifndef CAT_CLIENT_C_HEARTBEAT_H
#define CAT_CLIENT_C_HEARTBEAT_H
#include "message.h"
typedef CatMessage CatHeartBeat;
CatHeartBeat * createCatHeartBeat(const char *type, const char * name);
#endif //CAT_CLIENT_C_HEARTBEAT_H
#ifndef CAT_CLIENT_C_MESSAGE_H
#define CAT_CLIENT_C_MESSAGE_H
#include <string.h>
#include <stdlib.h>
#include "client_common.h"
typedef struct _CatMessage CatMessage;
struct _CatMessage {
void (*addDataPair)(CatMessage *message, const char *data);
void (*addData)(CatMessage *message, const char *dataKey, const char *dataValue);
void (*setStatus)(CatMessage *message, const char *status);
void (*setComplete)(CatMessage *message);
void *(*clear)(CatMessage *message);
};
#endif //CAT_CLIENT_C_MESSAGE_H
#ifndef CAT_CLIENT_C_METRIC_H
#define CAT_CLIENT_C_METRIC_H
#include "message.h"
typedef CatMessage CatMetric;
CatMetric *createCatMetric(const char *type, const char *name);
#endif //CAT_CLIENT_C_METRIC_H
#ifndef METRIC_HELPER_H_
#define METRIC_HELPER_H_
typedef struct _CatMetricHelper CatMetricHelper;
struct _CatMetricHelper {
CatMetricHelper *(*AddName)(CatMetricHelper *pHelper, const char *name);
CatMetricHelper *(*AddTags)(CatMetricHelper *pHelper, int tagCount, ...);
CatMetricHelper *(*AddTag)(CatMetricHelper *pHelper, const char *key, const char *val);
CatMetricHelper *(*AddCount)(CatMetricHelper *pHelper, int count);
CatMetricHelper *(*AddDuration)(CatMetricHelper *pHelper, unsigned long long durationMs);
};
#ifdef __cplusplus
extern "C" {
#endif
CatMetricHelper *CatBuildMetricHelper(const char *name);
#ifdef __cplusplus
}
#endif
#endif//METRIC_HELPER_H_
#ifndef CAT_CLIENT_C_TRACE_H
#define CAT_CLIENT_C_TRACE_H
#include "message.h"
typedef CatMessage CatTrace;
CatTrace *createCatTrace(const char *type, const char *name);
#endif //CAT_CLIENT_C_TRACE_H
#ifndef CAT_CLIENT_C_TRANSACTION_H
#define CAT_CLIENT_C_TRANSACTION_H
#include "message.h"
typedef struct _CatTransaction CatTransaction;
struct _CatTransaction {
void (*addDataPair)(CatMessage *message, const char *data);
void (*addData)(CatMessage *message, const char *dataKey, const char *dataValue);
void (*setStatus)(CatMessage *message, const char *status);
void (*setComplete)(CatMessage *message);
void *(*clear)(CatMessage *message);
void (*addChild)(CatTransaction *message, CatMessage *childMsg);
void (*setDurationInMillis)(CatTransaction *message, unsigned long long durationMs);
void (*setDurationStart)(CatTransaction *message, unsigned long long durationStart);
};
#endif //CAT_CLIENT_C_TRANSACTION_H
package gocat
import (
"time"
"ccat"
"message"
)
type catInstance struct {
}
func Instance() *catInstance {
return &catInstance{}
}
func (t *catInstance) flush(m message.Messager) {
ccat.Send(m)
}
func (t *catInstance) NewTransaction(mtype, name string) *message.Transaction {
return &message.Transaction{
Message: message.NewMessage(mtype, name, t.flush),
}
}
func (t *catInstance) NewCompletedTransactionWithDuration(mtype, name string, durationMs int64) {
var trans = t.NewTransaction(mtype, name)
trans.SetDurationInMillis(durationMs)
if durationMs > 0 && durationMs < 60*1000 {
trans.SetTimestamp(time.Now().UnixNano()/1000000 - durationMs)
}
trans.SetStatus(message.SUCCESS)
trans.Complete()
}
func (t *catInstance) NewEvent(mtype, name string) *message.Event {
return &message.Event{
message.NewMessage(mtype, name, t.flush),
}
}
func (t *catInstance) NewHeartbeat(mtype, name string) *message.Heartbeat {
return &message.Heartbeat{
message.NewMessage(mtype, name, t.flush),
}
}
func (t *catInstance) LogEvent(m_type, m_name string, args ...string) {
var e = t.NewEvent(m_type, m_name)
if len(args) > 0 {
e.SetStatus(args[0])
}
if len(args) > 1 {
e.AddData(args[1])
}
e.Complete()
}
func (t *catInstance) LogError(err error, args ...string) {
var category = "error"
if len(args) > 0 {
category = args[0]
}
var e = t.NewEvent("Exception", category)
var buf = newStacktrace(2, err)
e.SetStatus(message.FAIL)
e.AddData(buf.String())
e.Complete()
}
func (t *catInstance) LogMetricForCount(m_name string, args ...int) {
if len(args) == 0 {
ccat.LogMetricForCount(m_name, 1)
} else {
ccat.LogMetricForCount(m_name, args[0])
}
}
func (t *catInstance) LogMetricForDurationMs(m_name string, duration int64) {
ccat.LogMetricForDurationMs(m_name, duration)
}
package gocat
import (
"message"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_NewMessage(t *testing.T) {
var tree = Instance()
trans := tree.NewTransaction("foo", "bar")
assert.Equal(t, trans.Type, "foo")
assert.Equal(t, trans.Name, "bar")
assert.Equal(t, trans.Status, message.SUCCESS)
event := tree.NewEvent("foo", "bar")
assert.Equal(t, event.Type, "foo")
assert.Equal(t, event.Name, "bar")
assert.Equal(t, event.Status, message.SUCCESS)
}
package gocat
import (
"ccat"
)
type Config struct {
host string
port string
domain string
routerType int
}
func Init(domain string) {
ccat.Init(domain)
go ccat.Background()
}
func Shutdown() {
ccat.Shutdown()
}
func Wait() {
ccat.Wait()
}
package gocat
import "message"
const (
SUCCESS = message.SUCCESS
FAIL = message.FAIL
)
package gocat
import (
"bytes"
"fmt"
"go/build"
"path/filepath"
"runtime"
"strings"
)
var trimPaths []string
func init() {
for _, prefix := range build.Default.SrcDirs() {
if prefix[len(prefix)-1] != filepath.Separator {
prefix += string(filepath.Separator)
}
trimPaths = append(trimPaths, prefix)
}
}
func trimPath(filename string) string {
for _, prefix := range trimPaths {
if trimmed := strings.TrimPrefix(filename, prefix); len(trimmed) < len(filename) {
return trimmed
}
}
return filename
}
func functionName(pc uintptr) string {
fn := runtime.FuncForPC(pc)
if fn == nil {
return "unknown"
}
return fn.Name()
}
func newStacktrace(skip int, err error) (buf *bytes.Buffer) {
buf = bytes.NewBuffer([]byte{})
buf.WriteString(err.Error())
buf.WriteRune('\n')
for i := skip; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
file = trimPath(file)
name := functionName(pc)
// `runtime.goexit` is bootstrap loader and is meaningless in tracing.
if name == "runtime.goexit" {
continue
}
buf.WriteString(fmt.Sprintf("at %s(%s:%d)\n", name, file, line))
}
return buf
}
package gocat
import (
"runtime"
"testing"
)
func Test_functionName(t *testing.T) {
var skip = 0
var names = []string{
"gocat.Test_functionName",
"testing.tRunner",
"runtime.goexit",
}
for i := skip; ; i++ {
if pc, _, _, ok := runtime.Caller(i); !ok {
break
} else if functionName(pc) != names[i] {
t.Errorf("incorrect function name: got %s, want %s", functionName(pc), names[i])
}
}
}
func Test_trimPath(t *testing.T) {
var expect = "gocat/trace_test.go"
if _, filename, _, ok := runtime.Caller(0); !ok {
t.Errorf("error occured in reading top of the stack")
} else if trimPath(filename) != expect {
t.Errorf("incorrect trimPath result: got %s, want %s", trimPath(filename), expect)
}
}
package message
type Event struct {
Message
}
func (e *Event) Complete() {
e.Message.flush(e)
}
package message
type Heartbeat struct {
Message
}
func (e *Heartbeat) Complete() {
e.Message.flush(e)
}
package message
import (
"bytes"
"time"
)
const (
SUCCESS = "0"
FAIL = "-1"
)
type Flush func(m Messager)
type MessageGetter interface {
GetData() *bytes.Buffer
GetTime() time.Time
}
type Messager interface {
MessageGetter
AddData(k string, v ...string)
SetStatus(status string)
Complete()
}
type Message struct {
Type string
Name string
Status string
timestampInNano int64
m_data *bytes.Buffer
flush Flush
}
func NewMessage(mtype, name string, flush Flush) Message {
return Message{
Type: mtype,
Name: name,
Status: SUCCESS,
timestampInNano: time.Now().UnixNano(),
m_data: new(bytes.Buffer),
flush: flush,
}
}
func (m *Message) Complete() {
m.flush(m)
}
func (m *Message) GetData() *bytes.Buffer {
return m.m_data
}
func (m *Message) GetTime() time.Time {
return time.Unix(0, m.timestampInNano)
}
func (t *Message) SetTimestamp(timestampMs int64) {
t.timestampInNano = timestampMs * 1000000
}
func (m *Message) GetTimestamp() int64 {
return m.timestampInNano / 1000000
}
func (m *Message) AddData(k string, v ...string) {
if m.m_data.Len() != 0 {
m.m_data.WriteRune('&')
}
if len(v) == 0 {
m.m_data.WriteString(k)
} else {
m.m_data.WriteString(k)
m.m_data.WriteRune('=')
m.m_data.WriteString(v[0])
}
}
func (m *Message) SetStatus(status string) {
m.Status = status
}
package message
import "time"
type Transaction struct {
Message
DurationMs int64
}
func (t *Transaction) Complete() {
if t.DurationMs == 0 {
t.DurationMs = time.Now().UnixNano() - t.Message.timestampInNano
t.DurationMs /= 1000000
}
t.Message.flush(t)
}
func (t *Transaction) SetDurationInMillis(durationMs int64) {
t.DurationMs = durationMs
}
package test
import (
"errors"
"testing"
"time"
"gocat"
)
var cat = gocat.Instance()
const TTYPE = "foo"
func init() {
gocat.Init("gocat")
}
// send transaction
func case1() {
t := cat.NewTransaction(TTYPE, "test")
t.AddData("testcase")
t.AddData("foo", "bar")
t.SetStatus(gocat.FAIL)
t.SetTimestamp(time.Now().UnixNano()/1000/1000 - 20*1000)
t.SetDurationInMillis(15 * 1000)
t.Complete()
}
// send completed transaction with duration
func case2() {
cat.NewCompletedTransactionWithDuration(TTYPE, "completed", 24000)
cat.NewCompletedTransactionWithDuration(TTYPE, "completed-over-60s", 65000)
}
// send event
func case3() {
// way 1
e := cat.NewEvent(TTYPE, "event-1")
e.Complete()
// way 2
cat.LogEvent(TTYPE, "event-2")
cat.LogEvent(TTYPE, "event-3", gocat.FAIL)
cat.LogEvent(TTYPE, "event-4", gocat.FAIL, "foobar")
}
// send error with backtrace
func case4() {
err := errors.New("error")
cat.LogError(err)
}
// send metric
func case5() {
cat.LogMetricForCount("metric-1")
cat.LogMetricForCount("metric-2", 3)
cat.LogMetricForDurationMs("metric-3", 150)
}
func run(f func()) {
for {
f()
}
}
func Test_Message_Case(t *testing.T) {
go run(case1)
go run(case2)
go run(case3)
go run(case4)
go run(case5)
// wait until main process has been killed
var ch chan int
<-ch
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册