UNPKG

@mwcp/kmore

Version:

midway component for knex, supports declarative transaction and OpenTelemetry

356 lines 14.8 kB
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