Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(prometheus): serialize resource as target_info gauge #3300

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
fix(prometheus-exporter): remove generation of job and instance labels
  • Loading branch information
pichlermarc committed Oct 10, 2022
commit 7e338e6067c8f1a3d6677fd885fc1a387a59b282
Original file line number Diff line number Diff line change
Expand Up @@ -171,43 +171,6 @@ function stringify(
}\n`;
}

function filterReservedResourceAttributes(resource: Resource): MetricAttributes {
return Object.fromEntries(
Object.entries(resource.attributes)
.filter(([key]) => {
if (key === 'job') {
diag.warn('Cannot serialize reserved resource attribute "job".');
return true;
}
if (key === 'instance') {
diag.warn('Cannot serialize reserved resource attribute "instance".');
return true;
}

// drop any entries that will be used to construct 'job' and 'instance'
return key !== 'service.name' && key !== 'service.namespace' && key !== 'service.instance.id';
}
));
}

function extractJobLabel(resource: Resource): string | undefined {
const serviceName = resource.attributes['service.name'];
const serviceNamespace = resource.attributes['service.namespace'];

if (serviceNamespace != null && serviceName != null) {
return `${serviceNamespace.toString()}/${serviceName.toString()}`;
} else if (serviceName != null) {
return serviceName.toString();
}

return undefined;
}

function extractInstanceLabel(resource: Resource): string | undefined {
const instance = resource.attributes['service.instance.id'];
return instance?.toString();
}

export class PrometheusSerializer {
private _prefix: string | undefined;
private _appendTimestamp: boolean;
Expand Down Expand Up @@ -358,14 +321,7 @@ export class PrometheusSerializer {
const help = `# HELP ${name} Target metadata`;
const type = `# TYPE ${name} gauge`;

const resourceAttributes = filterReservedResourceAttributes(resource);

const otherAttributes = {
job: extractJobLabel(resource),
instance: extractInstanceLabel(resource)
};

const results = stringify(name, resourceAttributes, 1, undefined, otherAttributes).trim();
const results = stringify(name, resource.attributes, 1).trim();
return `${help}\n${type}\n${results}\n`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { Counter } from '@opentelemetry/api-metrics';
const serializedEmptyResourceLines = [
'# HELP target_info Target metadata',
'# TYPE target_info gauge',
'target_info{job="",instance=""} 1'
'target_info 1'
];

describe('PrometheusExporter', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const attributes = {
const serializedEmptyResource =
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{job="",instance=""} 1\n';
'target_info 1\n';

class TestMetricReader extends MetricReader {
constructor() {
Expand Down Expand Up @@ -651,111 +651,7 @@ describe('PrometheusSerializer', () => {
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{env="prod",hostname="myhost",datacenter="sdc",region="europe",owner="frontend",job="",instance=""} 1\n'
);
});

it('should drop service attributes', () => {
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
env: 'prod',
hostname: 'myhost',
datacenter: 'sdc',
region: 'europe',
owner: 'frontend',
'service.name': 'name',
'service.namespace': 'namespace',
'service.instance.id': 'instance id'
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{env="prod",hostname="myhost",datacenter="sdc",region="europe",owner="frontend",job="namespace/name",instance="instance id"} 1\n'
);
});

it('with undefined service.instance.id should add empty instance id', () => {
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
foo: 'bar',
'service.name': 'name',
'service.namespace': 'namespace',
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{foo="bar",job="namespace/name",instance=""} 1\n'
);
});

it('with undefined service.instance.id should add empty instance id', () => {
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
foo: 'bar',
'service.name': 'name',
'service.namespace': 'namespace',
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{foo="bar",job="namespace/name",instance=""} 1\n'
);
});

it('with undefined service.namespace should only use service.name for "job"', () => {
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
foo: 'bar',
'service.name': 'name',
'service.instance.id': '123'
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{foo="bar",job="name",instance="123"} 1\n'
);
});

it('with undefined service.namespace and service.name should add empty "job"', () => {
// service.name MUST exist. But PrometheusSerializer is exported and used by non-SDK packages.
// In case semantic conventions are not adhered to, add empty as a fallback.
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
foo: 'bar',
'service.instance.id': '123'
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{foo="bar",job="",instance="123"} 1\n'
);
});

it('with defined service.namespace and undefined service.name should add empty "job"', () => {
// service.name MUST exist. But PrometheusSerializer is exported and used by non-SDK packages.
// In case semantic conventions are not adhered to, add empty as a fallback.
const serializer = new PrometheusSerializer(undefined, true);
const result = serializer['_serializeResource'](new Resource({
foo: 'bar',
'service.instance.id': '123',
'service.namespace': 'namespace'
}));

assert.strictEqual(
result,
'# HELP target_info Target metadata\n' +
'# TYPE target_info gauge\n' +
'target_info{foo="bar",job="",instance="123"} 1\n'
'target_info{env="prod",hostname="myhost",datacenter="sdc",region="europe",owner="frontend"} 1\n'
);
});
});
Expand Down