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): implementation of metric operation duration #2380

Merged
merged 27 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
143c81b
operation duration metric
maryliag Jul 31, 2024
c58027b
use temporary const for sem attr
maryliag Jul 31, 2024
bbac895
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 13, 2024
962e4e8
fix lint
maryliag Aug 13, 2024
194ca30
remove unsed type
maryliag Aug 13, 2024
eaabf75
add test
maryliag Aug 20, 2024
a3c518e
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 20, 2024
c344db5
add more tests
maryliag Aug 20, 2024
a66144d
fix test
maryliag Aug 20, 2024
4adc2a6
cleanup
maryliag Aug 20, 2024
41577ea
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 20, 2024
3be63a3
cleanup
maryliag Aug 21, 2024
fbf14a8
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 21, 2024
bed20e1
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Aug 27, 2024
ceb6fad
add timeout for test
maryliag Aug 27, 2024
1aa1838
add missing done
maryliag Aug 27, 2024
0d89c87
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Sep 4, 2024
d7a78d0
use sem conv
maryliag Sep 4, 2024
6af47fa
fix lint
maryliag Sep 4, 2024
426bcc5
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Sep 12, 2024
aed321d
remove error attribute
maryliag Sep 13, 2024
bc87a34
cleanup
maryliag Sep 13, 2024
8467309
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Sep 13, 2024
942b5be
Merge branch 'main' into db-metric-duration
pichlermarc Sep 18, 2024
26075d1
Merge branch 'main' of github.com:open-telemetry/opentelemetry-js-con…
maryliag Sep 23, 2024
e04e8d0
Merge branch 'db-metric-duration' of github.com:maryliag/opentelemetr…
maryliag Sep 23, 2024
fa0b0fd
Merge branch 'main' into db-metric-duration
pichlermarc Sep 24, 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,11 @@
Span,
SpanStatusCode,
SpanKind,
Histogram,
MeterProvider,
ValueType,
Attributes,
HrTime,
} from '@opentelemetry/api';
import type * as pgTypes from 'pg';
import type * as pgPoolTypes from 'pg-pool';
Expand All @@ -41,10 +46,49 @@
import { addSqlCommenterComment } from '@opentelemetry/sql-common';
import { PACKAGE_NAME, PACKAGE_VERSION } from './version';
import { SpanNames } from './enums/SpanNames';
import {
hrTime,
hrTimeDuration,
hrTimeToMilliseconds,
} from '@opentelemetry/core';
import {
DBSYSTEMVALUES_POSTGRESQL,
SEMATTRS_DB_SYSTEM,
} from '@opentelemetry/semantic-conventions';

// TODO: Replace these constants once a new version of the semantic conventions
// package is created
const SEMATTRS_DB_NAMESPACE = 'db.namespace';
const SEMATTRS_ERROR_TYPE = 'error.type';
const SEMATTRS_SERVER_PORT = 'server.port';
const SEMATTRS_SERVER_ADDRESS = 'server.address';

export class PgInstrumentation extends InstrumentationBase<PgInstrumentationConfig> {
private _operationDuration!: Histogram;
constructor(config: PgInstrumentationConfig = {}) {
super(PACKAGE_NAME, PACKAGE_VERSION, config);
this._setMetricInstruments();
}

override setMeterProvider(meterProvider: MeterProvider) {
super.setMeterProvider(meterProvider);
this._setMetricInstruments();

Check warning on line 75 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#L73-L75

Added lines #L73 - L75 were not covered by tests
}

private _setMetricInstruments() {
this._operationDuration = this.meter.createHistogram(
'db.client.operation.duration',
{
description: 'Duration of database client operations.',
unit: 's',
valueType: ValueType.DOUBLE,
advice: {
explicitBucketBoundaries: [
0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10,
],
},
}
);
}

protected init() {
Expand Down Expand Up @@ -146,6 +190,29 @@
};
}

private recordOperationDuration(attributes: Attributes, startTime: HrTime) {
// Time to record metrics
const metricsAttributes: Attributes = {};
// Get the attribs already in span attributes
const keysToCopy = [
SEMATTRS_DB_SYSTEM,
SEMATTRS_DB_NAMESPACE,
SEMATTRS_ERROR_TYPE,
SEMATTRS_SERVER_PORT,
SEMATTRS_SERVER_ADDRESS,
];

keysToCopy.forEach(key => {
if (key in attributes) {
metricsAttributes[key] = attributes[key];
}
});

const durationSeconds =
hrTimeToMilliseconds(hrTimeDuration(startTime, hrTime())) / 1000;
this._operationDuration.record(durationSeconds, metricsAttributes);
}

private _getClientQueryPatch() {
const plugin = this;
return (original: typeof pgTypes.Client.prototype.query) => {
Expand All @@ -154,6 +221,7 @@
if (utils.shouldSkipInstrumentation(plugin.getConfig())) {
return original.apply(this, args as never);
}
const startTime = hrTime();

// client.query(text, cb?), client.query(text, values, cb?), and
// client.query(configObj, cb?) are all valid signatures. We construct
Expand All @@ -179,6 +247,16 @@
? (arg0 as utils.ObjectWithText)
: undefined;

const attributes: Attributes = {
[SEMATTRS_DB_SYSTEM]: DBSYSTEMVALUES_POSTGRESQL,
[SEMATTRS_DB_NAMESPACE]: this.database,
[SEMATTRS_SERVER_PORT]: this.connectionParameters.port,
[SEMATTRS_SERVER_ADDRESS]: this.connectionParameters.host,
};
this.on('end', () => {
plugin.recordOperationDuration(attributes, startTime);
});

const instrumentationConfig = plugin.getConfig();

const span = utils.handleConfigQuery.call(
Expand Down Expand Up @@ -209,7 +287,8 @@
args[args.length - 1] = utils.patchCallback(
instrumentationConfig,
span,
args[args.length - 1] as PostgresCallback // nb: not type safe.
args[args.length - 1] as PostgresCallback, // nb: not type safe.
attributes
);

// If a parent span exists, bind the callback
Expand All @@ -224,7 +303,8 @@
let callback = utils.patchCallback(
plugin.getConfig(),
span,
queryConfig.callback as PostgresCallback // nb: not type safe.
queryConfig.callback as PostgresCallback, // nb: not type safe.
attributes
);

// If a parent span existed, bind the callback
Expand Down Expand Up @@ -282,12 +362,12 @@
try {
result = original.apply(this, args as never);
} catch (e: unknown) {
// span.recordException(e);
span.setStatus({
code: SpanStatusCode.ERROR,
message: utils.getErrorMessage(e),
});
span.end();
attributes[SEMATTRS_ERROR_TYPE] = utils.getErrorName(e);
throw e;
}

Expand All @@ -309,6 +389,7 @@
message: error.message,
});
span.end();
attributes[SEMATTRS_ERROR_TYPE] = error.name;

Check warning on line 392 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#L392

Added line #L392 was not covered by tests
reject(error);
});
});
Expand Down
17 changes: 15 additions & 2 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 @@
Tracer,
SpanKind,
diag,
Attributes,
} from '@opentelemetry/api';
import { AttributeNames } from './enums/AttributeNames';
import {
Expand Down Expand Up @@ -236,15 +237,16 @@
export function patchCallback(
instrumentationConfig: PgInstrumentationConfig,
span: Span,
cb: PostgresCallback
cb: PostgresCallback,
attributes: Attributes
): PostgresCallback {
return function patchedCallback(
this: PgClientExtended,
err: Error,
res: object
) {
if (err) {
// span.recordException(err);
attributes['error.type'] = err.message;
maryliag marked this conversation as resolved.
Show resolved Hide resolved
span.setStatus({
code: SpanStatusCode.ERROR,
message: err.message,
Expand Down Expand Up @@ -306,6 +308,17 @@
: undefined;
}

/**
* Attempt to get a name string from a thrown value, while being quite
* defensive, to recognize the fact that, in JS, any kind of value (even
* primitives) can be thrown.
*/
export function getErrorName(e: unknown) {
return typeof e === 'object' && e !== null && 'name' in e
? String((e as { name?: unknown }).name)
: undefined;

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

View check run for this annotation

Codecov / codecov/patch

plugins/node/opentelemetry-instrumentation-pg/src/utils.ts#L319

Added line #L319 was not covered by tests
}

export function isObjectWithTextString(it: unknown): it is ObjectWithText {
return (
typeof it === 'object' &&
Expand Down