Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

example usage
```javascript
import {MetricsSetups} from './goodmetrics/metricsSetups';
import {Dimension} from './goodmetrics/_Metrics';
import {TimestampAt} from './goodmetrics/metricsFactory';
import {Dimension, MetricsSetups} from 'goodmetrics-nodejs';

const delay = async (ms: number): Promise<void> => {
return await new Promise(resolve => {
Expand All @@ -22,13 +20,16 @@ const main = async () => {
prescientDimensions: new Map<string, Dimension>(),
});

await metrics.record('my_metric', TimestampAt.Start, async metrics => {
console.info('inside metrics block');
metrics.measure('runs', 1);
await delay(100);
metrics.dimension('result', 'success');
});
await metrics.unaryMetricsFactory.record(
{name: 'test'},
async metrics => {
console.info('inside metrics block');
metrics.measure('runs', 1);
await delay(100);
metrics.dimension('result', 'success');
}
);
};

main().finally();
```
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"build": "tsc",
"lint": "eslint . --ext .ts",
"format": "eslint . --ext .ts --fix",
"dev-driver": "ts-node --prefer-ts-exts src/dev-driver.ts"
"dev-driver": "ts-node --prefer-ts-exts test/dev-driver.ts"
},
"main": "dist/index.js",
"files": [
Expand Down
53 changes: 0 additions & 53 deletions src/dev-driver.ts

This file was deleted.

23 changes: 22 additions & 1 deletion src/goodmetrics/_Metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Histogram = otlp_metrics.opentelemetry.proto.metrics.v1.Histogram;
import HistogramDataPoint = otlp_metrics.opentelemetry.proto.metrics.v1.HistogramDataPoint;
import AggregationTemporality = otlp_metrics.opentelemetry.proto.metrics.v1.AggregationTemporality;
import {newNumberDataPoint} from './data/otlp/numbersDataPoint';
import {goodmetrics} from 'goodmetrics-generated';

export enum MetricsBehavior {
DEFAULT = 'default',
Expand All @@ -21,6 +22,8 @@ export abstract class Dimension {
}

abstract asOtlpKeyValue(): KeyValue;

abstract asGoodmetricsDimension(): goodmetrics.Dimension;
}

export class StringDimension extends Dimension {
Expand All @@ -34,6 +37,12 @@ export class StringDimension extends Dimension {
const value = new AnyValue({string_value: this.value});
return new KeyValue({key: this.name, value});
}

asGoodmetricsDimension(): goodmetrics.Dimension {
return new goodmetrics.Dimension({
string: this.value,
});
}
}

export class NumberDimension extends Dimension {
Expand All @@ -47,6 +56,12 @@ export class NumberDimension extends Dimension {
const value = new AnyValue({int_value: this.value});
return new KeyValue({key: this.name, value});
}

asGoodmetricsDimension(): goodmetrics.Dimension {
return new goodmetrics.Dimension({
number: this.value,
});
}
}

export class BooleanDimension extends Dimension {
Expand All @@ -60,6 +75,12 @@ export class BooleanDimension extends Dimension {
const value = new AnyValue({bool_value: this.value});
return new KeyValue({key: this.name, value});
}

asGoodmetricsDimension(): goodmetrics.Dimension {
return new goodmetrics.Dimension({
boolean: this.value,
});
}
}

interface ViewProps {
Expand Down Expand Up @@ -109,7 +130,7 @@ export class _Metrics implements Metrics {
readonly metricsBehavior: MetricsBehavior;
readonly metricMeasurements: Map<string, number> = new Map();
readonly metricDistributions: Map<string, number> = new Map();
private readonly metricDimensions: Map<string, Dimension> = new Map();
readonly metricDimensions: Map<string, Dimension> = new Map();
constructor(props: MetricsProps) {
this.name = props.name;
this.timestampMillis = props.timestampMillis;
Expand Down
9 changes: 9 additions & 0 deletions src/goodmetrics/data/StatisticSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export class StatisticSet extends Aggregation {
});
}

values() {
return {
min: this.min,
max: this.max,
count: this.count,
sum: this.sum,
};
}

private statisticSetDataPoint(props: StatisticSetDataPointProps): Metric {
return new Metric({
name: `${props.metricName}_${props.measurementName}_${props.statisticSetComponent}`,
Expand Down
108 changes: 108 additions & 0 deletions src/goodmetrics/downstream/goodmetricsClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {_Metrics, Dimension} from '../_Metrics';
import {goodmetrics} from 'goodmetrics-generated';
import MetricsClient = goodmetrics.MetricsClient;
import {ChannelCredentials, Interceptor} from '@grpc/grpc-js';
import MetricsRequest = goodmetrics.MetricsRequest;
import {AggregatedBatch} from '../pipeline/aggregator';

interface GoodmetricsClientProps {
address: string;
}
interface GoodmetricsConnectProps {
hostname: string;
port: number;
}

export class GoodmetricsClient {
private readonly prescientDimensions: Map<string, Dimension>;
private readonly client: MetricsClient;
private readonly interceptors: Interceptor[];
private constructor(props: GoodmetricsClientProps) {
this.client = new MetricsClient(
props.address,
ChannelCredentials.createSsl()
);
this.prescientDimensions = new Map<string, Dimension>();
this.interceptors = [];
}

static connect(props: GoodmetricsConnectProps): GoodmetricsClient {
return new GoodmetricsClient({
address: `${props.hostname}:${props.port}`,
});
}

async sendMetricsBatch(metrics: _Metrics[]): Promise<void> {
const request = new MetricsRequest({
shared_dimensions: this.prescientDimensionsToProto(),
metrics: this.metricsToGoodmetrics(metrics),
});

return await new Promise((resolve, reject) => {
this.client.SendMetrics(request, {interceptors: this.interceptors}, e => {
if (!e) {
resolve();
} else {
reject(e);
}
});
});
}

async sendPreaggregatedMetrics(
aggregatedBatch: AggregatedBatch[]
): Promise<void> {
const datums: goodmetrics.Datum[] = [];
for (const batch of aggregatedBatch) {
datums.push(...batch.asGoodmetrics());
}
const request = new MetricsRequest({
shared_dimensions: this.prescientDimensionsToProto(),
metrics: datums,
});

return await new Promise((resolve, reject) => {
this.client.SendMetrics(request, {interceptors: this.interceptors}, e => {
if (!e) {
resolve();
} else {
reject(e);
}
});
});
}

private prescientDimensionsToProto(): Map<string, goodmetrics.Dimension> {
const dimensionsMap = new Map<string, goodmetrics.Dimension>();
for (const [key, dimen] of this.prescientDimensions) {
dimensionsMap.set(key, dimen.asGoodmetricsDimension());
}
return new Map();
}

private metricsToGoodmetrics(metrics: _Metrics[]): goodmetrics.Datum[] {
return metrics.map(metric => {
const dimensionsMap = new Map<string, goodmetrics.Dimension>();
const measurementsMap = new Map<string, goodmetrics.Measurement>();
for (const [key, dimen] of metric.metricDimensions) {
dimensionsMap.set(key, dimen.asGoodmetricsDimension());
}
for (const [key, value] of metric.metricMeasurements) {
const _int = Number.isInteger(value) ? value : undefined;
if (_int) {
measurementsMap.set(key, new goodmetrics.Measurement({i64: _int}));
} else {
measurementsMap.set(key, new goodmetrics.Measurement({f64: value}));
}
}
for (const [key, value] of metric.metricDistributions) {
measurementsMap.set(key, new goodmetrics.Measurement({i64: value}));
}
return new goodmetrics.Datum({
metric: metric.name,
dimensions: dimensionsMap,
measurements: measurementsMap,
});
});
}
}
29 changes: 20 additions & 9 deletions src/goodmetrics/metricsFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ interface Props {
totalTimeType: TotaltimeType;
}

interface RecordOptions {
name: string;
stampAt?: TimestampAt;
}

interface RecordWithBehaviorOptions {
name: string;
stampAt?: TimestampAt;
behavior: MetricsBehavior;
}

export class MetricsFactory {
protected readonly metricsSink: MetricsSink;
private readonly totalTimeType: TotaltimeType;
Expand Down Expand Up @@ -66,25 +77,25 @@ export class MetricsFactory {
}

async record<T>(
name: string,
stampAt: TimestampAt,
options: RecordOptions,
block: (metrics: Metrics) => Promise<T> | T
): Promise<T> {
return await this.recordWithBehavior(
name,
stampAt,
MetricsBehavior.DEFAULT,
{
name: options.name,
stampAt: options.stampAt,
behavior: MetricsBehavior.DEFAULT,
},
block
);
}

async recordWithBehavior<T>(
name: string,
stampAt: TimestampAt,
metricsBehavior: MetricsBehavior,
options: RecordWithBehaviorOptions,
block: (metrics: Metrics) => Promise<T> | T
): Promise<T> {
const metrics = this.getMetrics(name, stampAt, metricsBehavior);
const stampAt = options.stampAt ?? TimestampAt.Start;
const metrics = this.getMetrics(options.name, stampAt, options.behavior);
try {
const res = await block(metrics);
return res;
Expand Down
Loading