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(instrumentation-pg): initial implementation of DB metrics #2349

Merged
merged 48 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
19567fd
initial implementation of prototype
maryliag Jul 24, 2024
6d6070f
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Jul 24, 2024
99fc72d
update after rebase
maryliag Jul 24, 2024
87da206
fix lint
maryliag Jul 24, 2024
db622c0
fix lint
maryliag Jul 24, 2024
b4f0d5a
fix lint
maryliag Jul 24, 2024
8ecd3bc
add poolname test
maryliag Jul 24, 2024
0526375
add callback for release
maryliag Jul 24, 2024
9b47999
remove no longer valid comment
maryliag Jul 24, 2024
7d502d3
fix parameter used for idle
maryliag Jul 24, 2024
66bb463
fix lint
maryliag Jul 25, 2024
99941c4
add tests for new metric
maryliag Jul 26, 2024
c785caf
add comments
maryliag Jul 26, 2024
ce4ae04
update package-lock
maryliag Jul 26, 2024
55f8fb9
add metric db.client.connection.pending_requests
maryliag Jul 29, 2024
21db334
remove regex
maryliag Jul 29, 2024
cfddb8d
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Jul 29, 2024
87af5fb
remove username from poolname
maryliag Jul 31, 2024
b73543d
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Jul 31, 2024
d0d5574
use sem conv for attribute names
maryliag Aug 1, 2024
1c21f4f
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 1, 2024
24ba863
fix separator for unkown case
maryliag Aug 12, 2024
5bd13e3
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 12, 2024
88d7a77
add assert to test
maryliag Aug 13, 2024
524fed9
fix lint
maryliag Aug 13, 2024
6495bc8
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 13, 2024
9cee17f
remove not needed update
maryliag Aug 13, 2024
9359c0c
test updates
maryliag Aug 19, 2024
7fab03c
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 19, 2024
18a9bef
fix lint
maryliag Aug 19, 2024
d6c8a97
remove unused imports
maryliag Aug 19, 2024
427e0be
fix lint
maryliag Aug 19, 2024
c72f392
cleanup
maryliag Aug 19, 2024
7850e2e
cleanup
maryliag Aug 19, 2024
8110004
reset counters
maryliag Aug 20, 2024
c91919b
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 20, 2024
1fec645
remove unused span on test
maryliag Aug 21, 2024
1f93fc4
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 21, 2024
c60fed0
use updateMetric instead of setMetric
maryliag Aug 21, 2024
2ad7fe6
fix lint
maryliag Aug 21, 2024
b91bc27
use semantic conventions 1.27
maryliag Aug 29, 2024
4aa4d34
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 30, 2024
3f75b6e
Merge branch 'main' into db-metrics
pichlermarc Sep 4, 2024
17fc873
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Sep 4, 2024
23efae8
use sem conv
maryliag Sep 4, 2024
79bb0c8
Merge branch 'db-metrics' of github.com:maryliag/opentelemetry-js-con…
maryliag Sep 4, 2024
22c4112
use test metrics utils
maryliag Sep 4, 2024
c095d5a
Merge branch 'main' into db-metrics
pichlermarc Sep 11, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
Span,
SpanStatusCode,
SpanKind,
MeterProvider,
UpDownCounter,
} from '@opentelemetry/api';
import type * as pgTypes from 'pg';
import type * as pgPoolTypes from 'pg-pool';
Expand All @@ -43,8 +45,46 @@
import { SpanNames } from './enums/SpanNames';

export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConfig> {
private _connectionsCount!: UpDownCounter;
private _connectionPendingRequests!: UpDownCounter;
// Pool events connect, acquire, release and remove can be called
// multiple times without changing the values of total, idle and waiting
// connections. The _connectionsCounter is used to keep track of latest
// values and only update the metrics _connectionsCount and _connectionPendingRequests
// when the value change.
private _connectionsCounter: utils.poolConnectionsCounter = {
used: 0,
idle: 0,
pending: 0,
};

constructor(config: PgInstrumentationConfig = {}) {
super(PACKAGE_NAME, PACKAGE_VERSION, config);
this._setMetricInstruments();
maryliag marked this conversation as resolved.
Show resolved Hide resolved
}

override setMeterProvider(meterProvider: MeterProvider) {
super.setMeterProvider(meterProvider);
this._setMetricInstruments();
}
maryliag marked this conversation as resolved.
Show resolved Hide resolved

private _setMetricInstruments() {
this._connectionsCount = this.meter.createUpDownCounter(
'db.client.connection.count',
{
description:
'The number of connections that are currently in state described by the state attribute.',
unit: '{connection}',
}
);
pichlermarc marked this conversation as resolved.
Show resolved Hide resolved
this._connectionPendingRequests = this.meter.createUpDownCounter(
'db.client.connection.pending_requests',
{
description:
'The number of current pending requests for an open connection.',
unit: '{connection}',
}
);
}

protected init() {
Expand Down Expand Up @@ -334,6 +374,42 @@
attributes: utils.getSemanticAttributesFromPool(this.options),
});

this.on('connect', () => {
plugin._connectionsCounter = utils.updateCounter(
this,
plugin._connectionsCount,
plugin._connectionPendingRequests,
plugin._connectionsCounter
);
});

this.on('acquire', () => {
plugin._connectionsCounter = utils.updateCounter(
this,
plugin._connectionsCount,
plugin._connectionPendingRequests,
plugin._connectionsCounter
);
});

this.on('remove', () => {
plugin._connectionsCounter = utils.updateCounter(
this,
plugin._connectionsCount,
plugin._connectionPendingRequests,
plugin._connectionsCounter
);
});

this.on('release' as any, () => {
plugin._connectionsCounter = utils.updateCounter(

Check warning on line 405 in plugins/node/opentelemetry-instrumentation-pg/src/instrumentation.ts

View check run for this annotation

Codecov / codecov/patch

plugins/node/opentelemetry-instrumentation-pg/src/instrumentation.ts#L405

Added line #L405 was not covered by tests
this,
plugin._connectionsCount,
plugin._connectionPendingRequests,
plugin._connectionsCounter
);
});

if (callback) {
const parentSpan = trace.getSpan(context.active());
callback = utils.patchCallbackPGPool(
Expand Down
50 changes: 50 additions & 0 deletions plugins/node/opentelemetry-instrumentation-pg/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Tracer,
SpanKind,
diag,
UpDownCounter,
} from '@opentelemetry/api';
import { AttributeNames } from './enums/AttributeNames';
import {
Expand All @@ -47,6 +48,11 @@ import type * as pgTypes from 'pg';
import { safeExecuteInTheMiddle } from '@opentelemetry/instrumentation';
import { SpanNames } from './enums/SpanNames';

// TODO: Replace these constants once a new version of the semantic conventions
// package is created
const SEMATTRS_CLIENT_CONNECTION_POOL_NAME = 'db.client.connection.pool.name';
const SEMATTRS_CLIENT_CONNECTION_STATE = 'db.client.connection.state';

/**
* Helper function to get a low cardinality span name from whatever info we have
* about the query.
Expand Down Expand Up @@ -258,6 +264,50 @@ export function patchCallback(
};
}

export function getPoolName(pool: PgPoolOptionsParams): string {
let poolName = '';
poolName += (pool?.host ? `${pool.host}` : 'unknown_host') + ':';
poolName += (pool?.port ? `${pool.port}` : 'unknown_port') + '/';
poolName += pool?.database ? `${pool.database}` : 'unknown_database';

return poolName.trim();
}

export interface poolConnectionsCounter {
used: number;
idle: number;
pending: number;
}

export function updateCounter(
pool: PgPoolExtended,
connectionCount: UpDownCounter,
connectionPendingRequests: UpDownCounter,
latestCounter: poolConnectionsCounter
): poolConnectionsCounter {
const poolName = getPoolName(pool.options);
const all = pool.totalCount;
const pending = pool.waitingCount;
const idle = pool.idleCount;
const used = all - idle;

connectionCount.add(used - latestCounter.used, {
[SEMATTRS_CLIENT_CONNECTION_STATE]: 'used',
[SEMATTRS_CLIENT_CONNECTION_POOL_NAME]: poolName,
});

connectionCount.add(idle - latestCounter.idle, {
[SEMATTRS_CLIENT_CONNECTION_STATE]: 'idle',
[SEMATTRS_CLIENT_CONNECTION_POOL_NAME]: poolName,
});

connectionPendingRequests.add(pending - latestCounter.pending, {
[SEMATTRS_CLIENT_CONNECTION_POOL_NAME]: poolName,
});

return { used: used, idle: idle, pending: pending };
}

maryliag marked this conversation as resolved.
Show resolved Hide resolved
export function patchCallbackPGPool(
span: Span,
cb: PgPoolCallback
Expand Down
Loading
Loading