Files
zeekay 9a2c38391e debrand: purge ClickHouse -> datastore + hanzo-ds; datastore:// DSN; no replace
- imports: ClickHouse/ch-go, clickhouse-go/v2, srikanthccv/ClickHouse-go-mock
  -> github.com/hanzo-ds/{native,go,mock} (require, clean semver, NO replace)
- identifiers/aliases/type-names/config-keys/type-IDs: clickhouse -> datastore
  (receiver dir clickhousesystemtablesreceiver -> datastoresystemtablesreceiver)
- DSN scheme clickhouse:// -> datastore:// (driver treats non-http/https as Native)
- removed the go.opencensus.io version-pin replace (require-pinned instead)
- kept: real external refs (github.com/ClickHouse, clickhouse.com docs) and the
  hanzo-ds/mock test API (NewClickHouseNative) pending the mock's own rename
Full ./... build passes. 0 replace directives, 0 signoz.
2026-07-12 19:08:48 -07:00

127 lines
3.6 KiB
Go

package usage
import (
"context"
"fmt"
"sync"
"time"
"github.com/goccy/go-json"
"github.com/hanzo-ds/go"
"github.com/google/uuid"
"go.opencensus.io/metric/metricdata"
"go.opencensus.io/metric/metricexport"
"go.uber.org/zap"
)
// Options provides options for LogExporter
type Options struct {
// ReportingInterval is a time interval between two successive metrics export.
ReportingInterval time.Duration
}
type Usage struct {
TimeStamp time.Time
Count int64
Size int64
}
type UsageCollector struct {
exporterID uuid.UUID
reader *metricexport.Reader
ir *metricexport.IntervalReader
initReaderOnce sync.Once
o Options
db datastore.Conn
dbName string
tableName string
distributedTableName string
usageParser func(metrics []*metricdata.Metric, exporterID uuid.UUID) (map[string]Usage, error)
prevCount int64
prevSize int64
ttl int
logger *zap.Logger
}
var CollectorID uuid.UUID
func init() {
CollectorID = uuid.New()
}
func NewUsageCollector(
exporterId uuid.UUID,
db datastore.Conn,
options Options,
dbName string,
usageParser func(metrics []*metricdata.Metric, id uuid.UUID) (map[string]Usage, error),
logger *zap.Logger,
) *UsageCollector {
return &UsageCollector{
exporterID: exporterId,
reader: metricexport.NewReader(),
o: options,
db: db,
dbName: dbName,
tableName: UsageTableName,
distributedTableName: "distributed_" + UsageTableName,
usageParser: usageParser,
prevCount: 0,
prevSize: 0,
ttl: 3,
logger: logger,
}
}
func (e *UsageCollector) Start() error {
// start collector routine which
e.initReaderOnce.Do(func() {
var err error
e.ir, err = metricexport.NewIntervalReader(&metricexport.Reader{}, e)
if err != nil {
e.logger.Error("Error starting usage collector", zap.Error(err))
}
})
e.ir.ReportingInterval = e.o.ReportingInterval
return e.ir.Start()
}
func (c *UsageCollector) Stop() error {
c.ir.Stop()
c.ir.Flush()
return nil
}
func (e *UsageCollector) ExportMetrics(ctx context.Context, metrics []*metricdata.Metric) error {
e.logger.Debug("ExportMetrics", zap.Any("db", e.db), zap.Any("dbName", e.dbName), zap.Any("distributedTableName", e.distributedTableName), zap.Any("metrics", metrics))
usages, err := e.usageParser(metrics, e.exporterID)
if err != nil {
e.logger.Error("ExportMetrics parse error", zap.Error(err))
return err
}
time := time.Now()
for tenant, usage := range usages {
usage.TimeStamp = time
usageBytes, err := json.Marshal(usage)
if err != nil {
e.logger.Error("ExportMetrics marshal error", zap.Error(err))
return err
}
encryptedData, err := Encrypt([]byte(e.exporterID.String())[:32], usageBytes)
if err != nil {
e.logger.Error("ExportMetrics encrypt error", zap.Error(err))
return err
}
e.logger.Debug("ExportMetrics", zap.Any("tenant", tenant), zap.Any("collectorID", CollectorID.String()), zap.Any("exporterID", e.exporterID.String()), zap.Any("time", time), zap.Any("encryptedData", string(encryptedData)))
// insert everything as a new row
err = e.db.Exec(ctx, fmt.Sprintf("insert into %s.%s values ($1, $2, $3, $4, $5)", e.dbName, e.distributedTableName), tenant, CollectorID.String(), e.exporterID.String(), time, string(encryptedData))
if err != nil {
e.logger.Error("ExportMetrics insert error", zap.Error(err))
return err
}
}
return nil
}