@mwcp/kmore
Version:
midway component for knex, supports declarative transaction and OpenTelemetry
356 lines • 14.8 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import assert from 'node:assert';
import { App, ApplicationContext, Inject, Singleton, } from '@midwayjs/core';
import { AttrNames, SpanStatusCode, Trace, TraceLog, getSpan, } from '@mwcp/otel';
import { MConfig, getWebContext } from '@mwcp/share';
import { humanMemoryUsage } from '@waiting/shared-core';
import { eventNeedTrace, genCommonAttr } from './trace.helper.js';
import { TrxStatusService } from './trx-status.service.js';
import { ConfigKey, KmoreAttrNames } from './types.js';
let DbEvent = class DbEvent {
sourceConfig;
app;
applicationContext;
appDir;
baseDir;
trxStatusSvc;
traceService;
getDbConfigByDbId(dbId) {
assert(dbId);
const dbConfig = this.sourceConfig.dataSource[dbId];
return dbConfig;
}
getWebContext() {
return getWebContext(this.applicationContext);
}
getWebContextThenApp() {
try {
const webContext = getWebContext(this.applicationContext);
assert(webContext, 'getActiveContext() webContext should not be null, maybe this calling is not in a request context');
return webContext;
}
catch (ex) {
console.warn('getWebContextThenApp() failed', ex);
return this.app;
}
}
// #region onStart
onStart(options) {
const { dbConfig, event, kmore } = options;
assert(dbConfig);
assert(event.type === 'start', event.type);
assert(event.queryBuilder);
// if (event.queryBuilder) {
// void Object.defineProperty(event.queryBuilder, 'eventProcessed', {
// value: true,
// })
// }
const cb = dbConfig.eventCallbacks?.start;
return cb?.(event, kmore);
}
// #region onResp
onResp(options) {
const { dbConfig, event, kmore } = options;
assert(dbConfig);
assert(event.type === 'queryResponse', event.type);
assert(event.respRaw);
const cb = dbConfig.eventCallbacks?.queryResponse;
return cb?.(event, kmore);
}
// #region onQuery
onQuery(options) {
const { dbConfig, event, kmore } = options;
assert(dbConfig);
assert(event.type === 'query', event.type);
assert(event.data);
const cb = dbConfig.eventCallbacks?.query;
return cb?.(event, kmore);
}
// #region onError
async onError(options) {
const { dbConfig, event, kmore } = options;
assert(dbConfig);
assert(event.type === 'queryError', event.type);
const cb = dbConfig.eventCallbacks?.queryError;
return cb?.(event, kmore);
}
retrieveTraceScope(kmore, kmoreQueryId, builder) {
const { pagingGroupKey, pagingType } = builder;
const traceScope = this.getTrxTraceScopeByQueryId(kmore, kmoreQueryId);
if (pagingType) { // paging
if (pagingType === 'counter') { // counter
if (traceScope) {
return traceScope;
}
}
// pager
return pagingGroupKey ?? kmoreQueryId;
}
return traceScope ?? kmoreQueryId;
}
getTrxTraceScopeByQueryId(db, queryId) {
const trx = db.getTrxByQueryId(queryId);
return trx?.kmoreTrxId;
}
};
__decorate([
MConfig(ConfigKey.config),
__metadata("design:type", Object)
], DbEvent.prototype, "sourceConfig", void 0);
__decorate([
App(),
__metadata("design:type", Object)
], DbEvent.prototype, "app", void 0);
__decorate([
ApplicationContext(),
__metadata("design:type", Object)
], DbEvent.prototype, "applicationContext", void 0);
__decorate([
Inject(),
__metadata("design:type", String)
], DbEvent.prototype, "appDir", void 0);
__decorate([
Inject(),
__metadata("design:type", String)
], DbEvent.prototype, "baseDir", void 0);
__decorate([
Inject(),
__metadata("design:type", TrxStatusService)
], DbEvent.prototype, "trxStatusSvc", void 0);
__decorate([
Inject(),
__metadata("design:type", Function)
], DbEvent.prototype, "traceService", void 0);
__decorate([
Trace({
autoEndSpan: false,
spanName: ([options]) => {
const { kmore, event } = options;
// @ts-expect-error builder._method
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
let method = event.command?.toLowerCase() ?? event.queryBuilder._method ?? 'unknown';
if (method === 'del') {
method = 'delete';
}
const name = `Kmore ${kmore.dbId} ${method}`;
return name;
},
before([options], decoratorContext) {
if (!eventNeedTrace(KmoreAttrNames.BuilderCompile, options.dbConfig)) {
return;
}
const traceContext = decoratorContext.traceContext ?? this.traceService.getActiveContext();
const { kmore, event } = options;
const { kmoreQueryId, queryBuilder } = event;
const traceScope = this.retrieveTraceScope(kmore, kmoreQueryId, queryBuilder);
if (!this.trxStatusSvc.getTraceContextByScope(traceScope)) {
this.trxStatusSvc.setTraceContextByScope(traceScope, traceContext);
}
this.trxStatusSvc.setTraceContextByScope(kmoreQueryId, traceContext);
const { pagingType } = queryBuilder;
const traceSpan = getSpan(traceContext);
const ret = {};
if (pagingType && traceSpan) {
if (pagingType === 'counter') {
assert(queryBuilder.pagingGroupKey, 'queryBuilder.pagingGroupKey is empty');
if (!this.trxStatusSvc.getTraceContextByScope(queryBuilder.pagingGroupKey)) {
this.trxStatusSvc.setTraceContextByScope(queryBuilder.pagingGroupKey, traceContext);
}
// @ts-expect-error name
const spanName = traceSpan.name;
const spanName2 = `${spanName} AutoPaging`;
if (!spanName.endsWith('AutoPaging')) {
traceSpan.updateName(spanName2);
}
const opts = {
name: 'Kmore Counter',
scope: traceScope,
traceContext,
};
const { traceContext: traceCtx2 } = this.traceService.startScopeSpan(opts);
this.trxStatusSvc.setTraceContextByScope(kmoreQueryId, traceCtx2);
ret.traceContext = traceCtx2; // necessary
}
else {
const spanName2 = 'Kmore Pager';
traceSpan.updateName(spanName2);
}
}
const events = genCommonAttr(KmoreAttrNames.BuilderCompile);
ret.events = events;
return ret;
},
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], DbEvent.prototype, "onStart", null);
__decorate([
TraceLog({
before([options]) {
if (!eventNeedTrace(KmoreAttrNames.QueryResponse, options.dbConfig)) {
return;
}
const { respRaw } = options.event;
const attrs = {};
if (respRaw?.response?.command) {
attrs['db.operation'] = respRaw.response.command;
attrs[AttrNames.QueryRowCount] = respRaw.response.rowCount ?? 0;
// attrs[AttrNames.QueryResponse] = JSON.stringify(respRaw.response.rows, null, 2)
}
const events = genCommonAttr(KmoreAttrNames.QueryResponse);
if (respRaw?.response) {
events[AttrNames.QueryRowCount] = respRaw.response.rowCount ?? 0;
events[AttrNames.QueryResponse] = JSON.stringify(respRaw.response.rows, null, 2);
}
return { attrs, events };
},
after([options]) {
if (!eventNeedTrace(KmoreAttrNames.QueryResponse, options.dbConfig)) {
return;
}
const ret = {};
const { pagingType } = options.event.queryBuilder;
const { kmore, event } = options;
const { kmoreQueryId } = event;
const traceScope = this.retrieveTraceScope(kmore, kmoreQueryId, event.queryBuilder);
switch (pagingType) {
case 'counter': {
this.trxStatusSvc.removeTraceContextByScope(kmoreQueryId);
ret.endSpanAfterTraceLog = true;
break;
}
case 'pager': {
const spans = [];
const scopeCtx = this.trxStatusSvc.getTraceContextByScope(traceScope);
if (scopeCtx) {
const span = getSpan(scopeCtx);
if (span?.isRecording()) {
spans.push(span);
}
}
const activeCtx = this.trxStatusSvc.getTraceContextByScope(event.kmoreQueryId);
if (activeCtx) {
const span = getSpan(activeCtx);
if (span?.isRecording()) {
spans.push(span);
}
}
this.trxStatusSvc.removeTraceContextByScope(kmoreQueryId);
const { pagingGroupKey } = event.queryBuilder;
if (pagingGroupKey) {
this.trxStatusSvc.removeTraceContextByScope(pagingGroupKey);
}
this.trxStatusSvc.removeTraceContextByScope(traceScope);
ret.endSpanAfterTraceLog = spans;
break;
}
default: {
ret.endSpanAfterTraceLog = true;
}
}
return ret;
},
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], DbEvent.prototype, "onResp", null);
__decorate([
TraceLog({
before([options], decoratorContext) {
if (!eventNeedTrace(KmoreAttrNames.QueryQuerying, options.dbConfig)) {
return;
}
const { traceService, traceSpan } = decoratorContext;
assert(traceService, 'traceService is empty');
assert(traceSpan, 'traceSpan is empty');
const { config: knexConfig } = options.dbConfig;
const conn = knexConfig.connection;
const { dbId, kUid, queryUid, trxId, data } = options.event;
const attrs = {
dbId,
['db.system']: typeof knexConfig.client === 'string'
? knexConfig.client
: JSON.stringify(knexConfig.client, null, 2),
['net.peer.name']: conn.host,
['db.name']: conn.database,
['net.peer.port']: conn.port ?? '',
['db.user']: conn.user,
// [AttrNames.QueryCostThrottleInMS]: sampleThrottleMs,
kUid,
queryUid,
trxId: trxId ?? '',
['db.statement']: data?.sql ?? '',
};
const bindings = data?.bindings?.length ? JSON.stringify(data.bindings, null, 2) : void 0;
const events = genCommonAttr(KmoreAttrNames.QueryQuerying, { bindings, kUid });
return { attrs, events };
},
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], DbEvent.prototype, "onQuery", null);
__decorate([
TraceLog({
before([options]) {
if (!eventNeedTrace(KmoreAttrNames.QueryError, options.dbConfig)) {
return;
}
// if (! decoratorContext.traceScope) {
// const { kmore } = options
// const { kmoreQueryId, queryBuilder } = options.event
// decoratorContext.traceScope = this.retrieveTraceScope(kmore, kmoreQueryId, queryBuilder)
// }
const { dbId, kUid, queryUid, trxId, exData, exError, } = options.event;
const events = genCommonAttr(KmoreAttrNames.QueryError, {
level: 'error',
dbId,
kUid,
queryUid,
trxId,
[AttrNames.ServiceMemoryUsage]: JSON.stringify(humanMemoryUsage(), null, 2),
exData: JSON.stringify(exData, null, 2),
exError: JSON.stringify(exError, null, 2),
});
const attrs = {
[AttrNames.LogLevel]: 'error',
};
return { attrs, events };
},
after([options], _result, decoratorContext) {
if (!eventNeedTrace(KmoreAttrNames.QueryError, options.dbConfig)) {
return;
}
const { traceService, traceScope, traceSpan } = decoratorContext;
assert(traceService, 'traceService is empty');
// assert(traceScope, 'onResp.after() traceScope is empty')
assert(traceSpan, 'traceSpan is empty');
if (traceScope) {
const scopeRootSpan = traceService.getRootSpan(traceScope);
if (scopeRootSpan && scopeRootSpan === traceSpan) {
const spanStatusOptions = { code: SpanStatusCode.ERROR };
return { endSpanAfterTraceLog: true, spanStatusOptions };
}
}
return null;
},
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], DbEvent.prototype, "onError", null);
DbEvent = __decorate([
Singleton()
], DbEvent);
export { DbEvent };
//# sourceMappingURL=db-event.js.map