OpenTelemetry support (#9584)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2025-07-23 13:49:14 +07:00
committed by GitHub
parent b1258d56c2
commit 8fef0d7f17
60 changed files with 2200 additions and 180 deletions
+2 -1
View File
@@ -202,8 +202,9 @@
// "WORKSPACE_LIMIT_PER_USER": "1",
"MINIO_ACCESS_KEY": "minioadmin",
"MINIO_SECRET_KEY": "minioadmin",
"MINIO_ENDPOINT": "localhost"
"MINIO_ENDPOINT": "localhost",
// "DISABLE_SIGNUP": "true",
"OTLP_ENDPOINT": "http://huly.local:4318/v1/traces"
},
"runtimeVersion": "20",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
+1434 -6
View File
File diff suppressed because it is too large Load Diff
+33 -1
View File
@@ -160,6 +160,7 @@ services:
- ACCOUNTS_URL=http://huly.local:3000
- BRANDING_PATH=/var/cfg/branding.json
# - DISABLE_SIGNUP=true
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
stats:
image: hardcoreeng/stats
@@ -170,6 +171,7 @@ services:
environment:
- PORT=4900
- SERVER_SECRET=secret
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
workspace_cockroach:
image: hardcoreeng/workspace
@@ -198,6 +200,7 @@ services:
- BACKUP_BUCKET=${BACKUP_BUCKET_NAME}
- MAIL_URL=
# - INIT_WORKSPACE=staging-dev
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
collaborator:
image: hardcoreeng/collaborator
@@ -215,6 +218,7 @@ services:
- ACCOUNTS_URL=http://huly.local:3000
- STORAGE_CONFIG=${STORAGE_CONFIG}
- STATS_URL=http://huly.local:4900
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
front:
image: hardcoreeng/front
@@ -246,7 +250,7 @@ services:
- GITHUB_URL=http://huly.local:3500
- PRINT_URL=http://huly.local:4005
- SIGN_URL=http://huly.local:4006
# - ANALYTICS_COLLECTOR_URL=http://huly.local:4017
# - ANALYTICS_COLLECTOR_URL=http://huly.local:4017
- DESKTOP_UPDATES_URL=https://dist.huly.io
- DESKTOP_UPDATES_CHANNEL=dev
- BRANDING_URL=http://huly.local:8087/branding.json
@@ -255,6 +259,7 @@ services:
- BACKUP_URL=http://huly.local:4039/api/backup,
- EXCLUDED_APPLICATIONS_FOR_ANONYMOUS=["chunter", "notification"]
# - DISABLE_SIGNUP=true
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
transactor_cockroach:
image: hardcoreeng/transactor
@@ -296,6 +301,7 @@ services:
- RATE_LIMIT_WINDOW=30000
- FILES_URL=http://huly.local:4030/blob/:workspace/:blobId/:filename
- COMMUNICATION_API_ENABLED=true
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
rekoni:
image: hardcoreeng/rekoni-service
@@ -306,6 +312,7 @@ services:
- 4004:4004
environment:
- STATS_URL=http://huly.local:4901
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
fulltext_cockroach:
image: hardcoreeng/fulltext
extra_hosts:
@@ -332,6 +339,7 @@ services:
- STATS_URL=http://huly.local:4900
- REKONI_URL=http://huly.local:4004
- ACCOUNTS_URL=http://huly.local:3000
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
print:
image: hardcoreeng/print
extra_hosts:
@@ -346,6 +354,7 @@ services:
- STORAGE_CONFIG=${STORAGE_CONFIG}
- STATS_URL=http://huly.local:4900
- ACCOUNTS_URL=http://huly.local:3000
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
sign:
image: hardcoreeng/sign
extra_hosts:
@@ -368,6 +377,7 @@ services:
- SERVICE_ID=sign-service
- BRANDING_PATH=/var/cfg/branding.json
- STATS_URL=http://huly.local:4900
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
# aiBot:
# image: hardcoreeng/ai-bot
# ports:
@@ -418,6 +428,7 @@ services:
- SECRET=secret
- SERVICE_ID=msg2file-service
- STORAGE_CONFIG=${STORAGE_CONFIG}
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
export:
image: hardcoreeng/export
extra_hosts:
@@ -435,6 +446,7 @@ services:
- STATS_URL=http://huly.local:4900
- STORAGE_CONFIG=${STORAGE_CONFIG}
- ACCOUNTS_URL=http://huly.local:3000
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
datalake:
image: hardcoreeng/datalake
extra_hosts:
@@ -459,6 +471,7 @@ services:
- BUCKETS=blobs,eu|http://minio:9000?accessKey=minioadmin&secretKey=minioadmin
- REGION=cockroach
- QUEUE_CONFIG=${QUEUE_CONFIG}
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
hulykvs:
image: hardcoreeng/hulykvs
@@ -511,6 +524,7 @@ services:
- INTERVAL=60
- STORAGE=minio|minio?accessKey=minioadmin&secretKey=minioadmin
- WORKSPACE_STORAGE=${STORAGE_CONFIG}
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
backup-api:
image: hardcoreeng/backup-api
@@ -532,6 +546,23 @@ services:
- STATS_URL=http://huly.local:4900
- BUCKET_NAME=backups
- STORAGE=minio|minio?accessKey=minioadmin&secretKey=minioadmin
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
restart: unless-stopped
jaeger:
image: jaegertracing/all-in-one:latest
extra_hosts:
- 'huly.local:host-gateway'
ports:
- 16686:16686 # Web UI
- 4317:4317
- 4318:4318
volumes:
- telemetry:/badger
environment:
- COLLECTOR_OTLP_ENABLED=true
- SPAN_STORAGE_TYPE=badger
- BADGER_DIRECTORY_VALUE=/badger/data
- BADGER_DIRECTORY_KEY=/badger/key
restart: unless-stopped
volumes:
db:
@@ -540,3 +571,4 @@ volumes:
elastic:
cockroach_db:
redpanda:
telemetry:
@@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
+1
View File
@@ -41,6 +41,7 @@
"@hcengineering/platform": "^0.6.11",
"@hcengineering/core": "^0.6.32",
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/measurements-otlp": "^0.6.0",
"winston": "^3.11.0",
"winston-daily-rotate-file": "^5.0.0",
"@sentry/node": "^9.22.0"
+1
View File
@@ -7,6 +7,7 @@ import { SentryAnalyticProvider } from './sentry'
export * from './logging'
export * from './sentry'
export * from '@hcengineering/measurements-otlp'
export function configureAnalytics (sentryDSN: string | undefined, config?: Record<string, any>): void {
const providers: AnalyticProvider[] = []
+1
View File
@@ -41,6 +41,7 @@
"dependencies": {
"@hcengineering/platform": "^0.6.11",
"@hcengineering/analytics": "^0.6.0",
"@hcengineering/measurements": "^0.6.0",
"fast-equals": "^5.2.2"
},
"repository": "https://github.com/hcengineering/platform",
+1 -1
View File
@@ -1,4 +1,4 @@
import { MeasureMetricsContext } from '../measurements'
import { MeasureMetricsContext } from '@hcengineering/measurements'
describe('context tests', () => {
it('check withLog proper catch', async () => {
+1 -1
View File
@@ -26,7 +26,7 @@ import {
} from './classes'
import core from './component'
import { Hierarchy } from './hierarchy'
import { type MeasureContext, MeasureMetricsContext } from './measurements'
import { type MeasureContext, MeasureMetricsContext } from '@hcengineering/measurements'
import { ModelDb } from './memdb'
import type {
DocumentQuery,
+1 -1
View File
@@ -26,7 +26,7 @@ export {
configUserAccountUuid
} from './component'
export * from './hierarchy'
export * from './measurements'
export * from '@hcengineering/measurements'
export * from './memdb'
export * from './objvalue'
export * from './operations'
-3
View File
@@ -1,3 +0,0 @@
export * from './context'
export * from './metrics'
export * from './types'
+1 -1
View File
@@ -14,7 +14,7 @@
//
import type { Account, AccountRole, AccountUuid, Doc, Domain, PersonId, Ref } from './classes'
import { type MeasureContext } from './measurements'
import { type MeasureContext } from '@hcengineering/measurements'
import { type DocumentQuery, type FindOptions } from './storage'
import type { DocumentUpdate, Tx } from './tx'
import { type WorkspaceIds } from './utils'
+1 -10
View File
@@ -968,13 +968,4 @@ export function unique<T> (arr: T[]): T[] {
export function uniqueNotEmpty<T extends NonNullable<unknown>> (arr: Array<T | undefined | null>): T[] {
return unique(arr).filter(notEmpty)
}
/**
* Return a current performance timestamp
*/
export const platformNow: () => number = () => performance.now()
/**
* Return a diff with previous performance snapshot with 2 digits after . max.
*/
export const platformNowDiff = (old: number): number => Math.round((performance.now() - old) * 100) / 100
export { platformNow, platformNowDiff } from '@hcengineering/measurements'
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
+4
View File
@@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/
@@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
+60
View File
@@ -0,0 +1,60 @@
{
"name": "@hcengineering/measurements-otlp",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"files": [
"lib/**/*",
"types/**/*",
"tsconfig.json"
],
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"test": "jest --passWithNoTests --silent",
"format": "format src",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"simplytyped": "^3.3.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"typescript": "^5.8.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5"
},
"dependencies": {
"@hcengineering/measurements": "^0.6.0",
"@sentry/node": "^9.22.0",
"@opentelemetry/sdk-node": "^0.203.0",
"@opentelemetry/sdk-logs": "^0.203.0",
"@opentelemetry/auto-instrumentations-node": "^0.62.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-trace-node": "^2.0.1",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/core": "^2.0.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
"@opentelemetry/otlp-exporter-base": "^0.203.0",
"@opentelemetry/id-generator-aws-xray": "^2.0.0",
"@opentelemetry/exporter-metrics-otlp-http": "^0.203.0"
},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
+1
View File
@@ -0,0 +1 @@
export * from './telemetry'
+355
View File
@@ -0,0 +1,355 @@
import {
childMetrics,
consoleLogger,
MeasureContext,
MeasureMetricsContext,
newMetrics,
noParamsLogger,
nullPromise,
platformNow,
platformNowDiff,
updateMeasure,
type FullParamsType,
type MeasureLogger,
type Metrics,
type ParamsType
} from '@hcengineering/measurements'
import { context, Span, trace, type Context, type Tracer } from '@opentelemetry/api'
import { suppressTracing } from '@opentelemetry/core'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { AWSXRayIdGenerator } from '@opentelemetry/id-generator-aws-xray'
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node'
/**
* @public
*/
export class OpenTelemetryMetricsContext implements MeasureContext {
private readonly name: string
private readonly params: ParamsType
private readonly fullParams: FullParamsType | (() => FullParamsType) = {}
logger: MeasureLogger
metrics: Metrics
id?: string
st = platformNow()
contextData: object = {}
private done (value?: number, override?: boolean): void {
updateMeasure(this.metrics, this.st, this.params, this.fullParams, (spend) => {}, value, override)
this.span?.end()
}
constructor (
name: string,
readonly tracer: Tracer,
readonly context: Context,
readonly span: Span | undefined,
params: ParamsType,
fullParams: FullParamsType | (() => FullParamsType) = {},
metrics: Metrics = newMetrics(),
logger?: MeasureLogger,
readonly parent?: MeasureContext,
readonly logParams?: ParamsType
) {
this.name = name
this.params = params
this.fullParams = fullParams
this.metrics = metrics
this.metrics.namedParams = this.metrics.namedParams ?? {}
for (const [k, v] of Object.entries(params)) {
if (this.metrics.namedParams[k] !== v) {
this.metrics.namedParams[k] = v
} else {
this.metrics.namedParams[k] = '*'
}
}
this.logger = logger ?? (this.logParams != null ? consoleLogger(this.logParams ?? {}) : noParamsLogger)
}
measure (name: string, value: number, override?: boolean): void {
this.span?.addEvent(name, value)
}
newChild (
name: string,
params: ParamsType,
fullParams?: FullParamsType | (() => FullParamsType),
logger?: MeasureLogger,
useContextParent: boolean = true
): MeasureContext {
const childContext = useContextParent
? context.active()
: this.span !== undefined
? trace.setSpan(this.context, this.span)
: this.context
const span = this.tracer.startSpan(name, undefined, childContext)
const spanParams = [...Object.entries(params)]
for (const [k, v] of spanParams) {
span?.setAttribute(k, v as any)
}
const result = new OpenTelemetryMetricsContext(
name,
this.tracer,
childContext,
span,
params,
fullParams ?? {},
childMetrics(this.metrics, [name]),
logger ?? this.logger,
this,
this.logParams
)
result.id = this.id
result.contextData = this.contextData
return result
}
with<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: ParamsType | (() => FullParamsType)
): Promise<T> {
const c = this.newChild(name, params, fullParams, this.logger, false)
let needFinally = true
try {
const _context = (c as OpenTelemetryMetricsContext).context ?? context.active()
const span = (c as OpenTelemetryMetricsContext).span
const value = context.with(_context, () => op(c))
if (value instanceof Promise) {
needFinally = false
if (span !== undefined) {
void value.catch((err) => {
span?.recordException(err)
})
}
return value.finally(() => {
if (span !== undefined) {
const fParams = typeof fullParams === 'function' ? fullParams() : fullParams
const spanParams = [...Object.entries(fParams ?? {})]
for (const [k, v] of spanParams) {
span?.setAttribute(k, v)
}
}
c.end()
})
} else {
if (value == null) {
return nullPromise as Promise<T>
}
return Promise.resolve(value)
}
} finally {
if (needFinally) {
c.end()
}
}
}
withoutTracing<T>(op: () => T): T {
return context.with(suppressTracing(this.context), op)
}
withSync<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T,
fullParams?: ParamsType | (() => FullParamsType)
): T {
const c = this.newChild(name, params, fullParams, this.logger, false)
const _context = (c as OpenTelemetryMetricsContext).context ?? context.active()
const span = (c as OpenTelemetryMetricsContext).span
try {
return context.with(_context, () => op(c))
} catch (err: any) {
if (span !== undefined) {
span.recordException(err)
}
throw err
} finally {
c.end()
}
}
withLog<T>(
name: string,
params: ParamsType,
op: (ctx: MeasureContext) => T | Promise<T>,
fullParams?: ParamsType
): Promise<T> {
const st = platformNow()
const r = this.with(name, params, op, fullParams)
r.catch(() => {
// Ignore logging errors to prevent unhandled rejections
}).finally(() => {
this.logger.logOperation(name, platformNowDiff(st), { ...params, ...fullParams })
})
return r
}
error (message: string, args?: Record<string, any>): void {
this.logger.error(message, { ...this.params, ...args, ...(this.logParams ?? {}) })
}
info (message: string, args?: Record<string, any>): void {
this.logger.info(message, { ...this.params, ...args, ...(this.logParams ?? {}) })
}
warn (message: string, args?: Record<string, any>): void {
this.logger.warn(message, { ...this.params, ...args, ...(this.logParams ?? {}) })
}
end (): void {
this.done()
}
getParams (): ParamsType {
return this.params
}
}
/**
* Parse W3C baggage header format to Record<string, string>
*
* W3C baggage format: "key1=value1,key2=value2;property=value,key3=value3"
* Returns only the key-value pairs, ignoring properties
*/
function parseBaggage (baggageHeader?: string): Record<string, string> {
if (baggageHeader == null || typeof baggageHeader !== 'string') {
return {}
}
const result: Record<string, string> = {}
// Split by comma to get individual baggage members
const members = baggageHeader.split(',')
for (const member of members) {
const trimmedMember = member.trim()
if (trimmedMember === '') continue
// Split by semicolon to separate key=value from properties
const parts = trimmedMember.split(';')
const keyValuePart = parts[0]?.trim()
if (keyValuePart == null) continue
// Split by equals to get key and value
const equalIndex = keyValuePart.indexOf('=')
if (equalIndex === -1) continue
const key = keyValuePart.substring(0, equalIndex).trim()
const value = keyValuePart.substring(equalIndex + 1).trim()
if (key != null) {
// URL decode the key and value
try {
const decodedKey = decodeURIComponent(key)
const decodedValue = decodeURIComponent(value)
result[decodedKey] = decodedValue
} catch (error) {
// If decoding fails, use the original values
result[key] = value
}
}
}
return result
}
export function createOpenTelemetryMetricsContext (
name: string,
params: ParamsType,
fullParams: FullParamsType | (() => FullParamsType) = {},
metrics: Metrics = newMetrics(),
logger?: MeasureLogger
): MeasureContext {
let url = process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ?? process.env.OTEL_EXPORTER_OTLP_ENDPOINT
if (url !== undefined && !url.endsWith('/v1/traces')) {
if (url.endsWith('/')) {
url += 'v1/traces'
} else {
url += '/v1/traces'
}
}
if (url === undefined) {
console.warn('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is not set, OpenTelemetry metrics will not be sent')
return new MeasureMetricsContext(name, params, fullParams, metrics, logger)
}
const headers: Record<string, string> = parseBaggage(process.env.OTEL_EXPORTER_OTLP_HEADERS) ?? {}
if (process.env.OTEL_EXPORTER_OTLP_TRACES_HEADERS !== undefined) {
const extraHeaders = parseBaggage(process.env.OTEL_EXPORTER_OTLP_TRACES_HEADERS)
for (const [key, value] of Object.entries(extraHeaders)) {
headers[key] = value
}
}
const exporter = new OTLPTraceExporter({
url,
headers,
compression:
(process.env.OTEL_EXPORTER_OTLP_COMPRESSION as CompressionAlgorithm) ??
(process.env.OTEL_EXPORTER_OTLP_TRACES_COMPRESSION as CompressionAlgorithm) ??
CompressionAlgorithm.GZIP,
keepAlive: true
})
const bsp = new BatchSpanProcessor(exporter, {
maxExportBatchSize: parseInt(process.env.OTEL_EXPORTER_OTLP_TRACES_MAX_EXPORT_BATCH_SIZE ?? '1000'),
maxQueueSize: parseInt(process.env.OTEL_EXPORTER_OTLP_TRACES_MAX_QUEUE_SIZE ?? '1000')
})
const sdk = new NodeSDK({
spanProcessor: bsp,
serviceName: name,
traceExporter: exporter,
instrumentations: [getNodeAutoInstrumentations()],
idGenerator: new AWSXRayIdGenerator()
})
sdk.start()
// Graceful shutdown
process.on('SIGTERM', () => {
sdk
.shutdown()
.then(() => {
console.log('Tracing terminated')
})
.catch((error) => {
console.log('Error terminating tracing', error)
})
.finally(() => process.exit(0))
})
const tracer = trace.getTracer(name)
const currentConext = context.active()
const ctx = new OpenTelemetryMetricsContext(
name,
tracer,
currentConext,
undefined,
params,
fullParams,
metrics,
logger
)
ctx.info('Using open telemetry metrics context', {
endpoint: url
})
return ctx
}
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib", "dist", "types", "bundle"]
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'],
parserOptions: {
tsconfigRootDir: __dirname,
project: './tsconfig.json'
}
}
+4
View File
@@ -0,0 +1,4 @@
*
!/lib/**
!CHANGELOG.md
/lib/**/__tests__/
+4
View File
@@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@hcengineering/platform-rig"
}
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
roots: ["./src"],
coverageReporters: ["text-summary", "html"]
}
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@hcengineering/measurements",
"version": "0.6.0",
"main": "lib/index.js",
"svelte": "src/index.ts",
"types": "types/index.d.ts",
"files": [
"lib/**/*",
"types/**/*",
"tsconfig.json"
],
"author": "Anticrm Platform Contributors",
"license": "EPL-2.0",
"scripts": {
"build": "compile",
"build:watch": "compile",
"test": "jest --passWithNoTests --silent",
"format": "format src",
"_phase:build": "compile transpile src",
"_phase:test": "jest --passWithNoTests --silent",
"_phase:format": "format src",
"_phase:validate": "compile validate"
},
"devDependencies": {
"@hcengineering/platform-rig": "^0.6.0",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-n": "^15.4.0",
"eslint": "^8.54.0",
"simplytyped": "^3.3.0",
"@typescript-eslint/parser": "^6.11.0",
"eslint-config-standard-with-typescript": "^40.0.0",
"prettier": "^3.1.0",
"typescript": "^5.8.3",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"@types/jest": "^29.5.5"
},
"dependencies": {},
"repository": "https://github.com/hcengineering/platform",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
"exports": {
".": {
"types": "./types/index.d.ts",
"require": "./lib/index.js",
"import": "./lib/index.js"
}
}
}
@@ -1,6 +1,6 @@
// Basic performance metrics suite.
import { generateId, platformNow, platformNowDiff } from '../utils'
import { platformNow, platformNowDiff } from '.'
import { childMetrics, newMetrics, updateMeasure } from './metrics'
import {
type FullParamsType,
@@ -21,7 +21,7 @@ function replacer (value: any): any {
return value instanceof Error ? errorPrinter(value) : value
}
const consoleLogger = (logParams: Record<string, any>): MeasureLogger => ({
export const consoleLogger = (logParams: Record<string, any>): MeasureLogger => ({
info: (msg, args) => {
console.info(
msg,
@@ -45,9 +45,9 @@ const consoleLogger = (logParams: Record<string, any>): MeasureLogger => ({
logOperation: (operation, time, params) => {}
})
const noParamsLogger = consoleLogger({})
export const noParamsLogger = consoleLogger({})
const nullPromise = Promise.resolve()
export const nullPromise = Promise.resolve()
/**
* @public
@@ -146,6 +146,10 @@ export class MeasureMetricsContext implements MeasureContext {
}
}
withoutTracing<T>(op: () => T): T {
return op()
}
withSync<T>(
name: string,
params: ParamsType,
@@ -231,6 +235,10 @@ export class NoMetricsContext implements MeasureContext {
return r instanceof Promise ? r : Promise.resolve(r)
}
withoutTracing<T>(op: () => T): T {
return op()
}
withSync<T>(
name: string,
params: ParamsType,
@@ -290,6 +298,8 @@ export function setOperationLogProfiling (value: boolean): void {
operationProfiling = value
}
let globalId: number = 0
export function registerOperationLog (ctx: MeasureContext): { opLogMetrics?: Metrics, op?: OperationLog } {
if (!operationProfiling) {
return {}
@@ -298,7 +308,7 @@ export function registerOperationLog (ctx: MeasureContext): { opLogMetrics?: Met
let opLogMetrics: Metrics | undefined
if (ctx.id === undefined) {
ctx.id = 'op_' + generateId()
ctx.id = 'op_' + (++globalId).toString(16)
}
if (ctx.metrics !== undefined) {
if (ctx.metrics.opLog === undefined) {
+13
View File
@@ -0,0 +1,13 @@
export * from './context'
export * from './metrics'
export * from './types'
/**
* Return a current performance timestamp
*/
export const platformNow: () => number = () => performance.now()
/**
* Return a diff with previous performance snapshot with 2 digits after . max.
*/
export const platformNowDiff = (old: number): number => Math.round((performance.now() - old) * 100) / 100
@@ -1,7 +1,6 @@
// Basic performance metrics suite.
import { type MetricsData } from '.'
import { platformNow } from '../utils'
import { platformNow, type MetricsData } from '.'
import { type FullParamsType, type Metrics, type ParamsType } from './types'
/**
@@ -84,6 +84,8 @@ export interface MeasureContext<Q = any> {
fullParams?: FullParamsType | (() => FullParamsType)
) => Promise<T>
withoutTracing: <T>(op: () => T) => T
withSync: <T>(
name: string,
params: ParamsType,
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"declarationDir": "./types",
"tsBuildInfoFile": ".build/build.tsbuildinfo"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib", "dist", "types", "bundle"]
}
+3 -3
View File
@@ -3,8 +3,8 @@
//
import { serveAccount } from '@hcengineering/account-service'
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { initStatisticsContext, loadBrandingMap } from '@hcengineering/server-core'
import { join } from 'path'
@@ -13,7 +13,7 @@ Analytics.setTag('application', 'account')
const metricsContext = initStatisticsContext('account', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'account',
{},
{},
+3 -3
View File
@@ -14,9 +14,9 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { startBackup } from '@hcengineering/backup-service'
import { MeasureMetricsContext, newMetrics, type Tx } from '@hcengineering/core'
import { newMetrics, type Tx } from '@hcengineering/core'
import { initStatisticsContext, type PipelineFactory } from '@hcengineering/server-core'
import {
createBackupPipeline,
@@ -56,7 +56,7 @@ process.on('exit', () => {
const metricsContext = initStatisticsContext('backup', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'backup',
{},
{},
+3 -3
View File
@@ -14,9 +14,9 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { startCollaborator } from '@hcengineering/collaborator'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { newMetrics } from '@hcengineering/core'
import { initStatisticsContext } from '@hcengineering/server-core'
import { join } from 'path'
@@ -25,7 +25,7 @@ Analytics.setTag('application', 'collaborator')
const metricsContext = initStatisticsContext('collaborator', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'collaborator',
{},
{},
+3 -3
View File
@@ -3,8 +3,8 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { startFront } from '@hcengineering/front/src/starter'
import { initStatisticsContext } from '@hcengineering/server-core'
import { join } from 'path'
@@ -14,7 +14,7 @@ Analytics.setTag('application', 'front')
const metricsContext = initStatisticsContext('front', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'front',
{},
{},
+3 -3
View File
@@ -14,8 +14,8 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics, type Tx } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics, type Tx } from '@hcengineering/core'
import { initStatisticsContext, type StorageConfiguration } from '@hcengineering/server-core'
import { join } from 'path'
@@ -41,7 +41,7 @@ setMetadata(serverToken.metadata.Service, 'fulltext')
const metricsContext = initStatisticsContext('fulltext', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'fulltext',
{},
{},
+3 -3
View File
@@ -14,8 +14,8 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { Doc, MeasureMetricsContext, TxCUD, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { Doc, newMetrics, TxCUD } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
@@ -46,7 +46,7 @@ async function main (): Promise<void> {
const ctx = initStatisticsContext(application, {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
application,
{},
{},
+3 -3
View File
@@ -4,9 +4,9 @@
// Add this to the VERY top of the first file loaded in your app
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import contactPlugin from '@hcengineering/contact'
import { MeasureMetricsContext, newMetrics, setOperationLogProfiling } from '@hcengineering/core'
import { newMetrics, setOperationLogProfiling } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import { setMetadata } from '@hcengineering/platform'
import { setDBExtraOptions } from '@hcengineering/postgres'
@@ -50,7 +50,7 @@ const metricsContext = initStatisticsContext('transactor', {
return getStats()
},
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'server',
{},
{},
+3 -3
View File
@@ -2,8 +2,8 @@
// Copyright © 2023 Hardcore Engineering Inc.
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics, type Tx } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics, type Tx } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import builder, { getModelVersion, migrateOperations } from '@hcengineering/model-all'
import { initStatisticsContext, loadBrandingMap } from '@hcengineering/server-core'
@@ -18,7 +18,7 @@ Analytics.setTag('application', 'workspace')
// Force create server metrics context with proper logging
const metricsContext = initStatisticsContext('workspace', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'workspace',
{},
{},
+10
View File
@@ -422,6 +422,16 @@
"projectFolder": "packages/platform",
"shouldPublish": true
},
{
"packageName": "@hcengineering/measurements",
"projectFolder": "packages/measurements",
"shouldPublish": true
},
{
"packageName": "@hcengineering/measurements-otlp",
"projectFolder": "packages/measurements-otlp",
"shouldPublish": true
},
{
"packageName": "@hcengineering/core",
"projectFolder": "packages/core",
+10 -6
View File
@@ -385,7 +385,9 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap
// Ignore
}
const result = await measureCtx.with(request.method, { source }, (mctx) => {
const userCtx = measureCtx.newChild(request.method, { source })
try {
if (method === undefined || typeof method !== 'function') {
const response = {
id: request.id,
@@ -396,12 +398,14 @@ export function serveAccount (measureCtx: MeasureContext, brandings: BrandingMap
return
}
return method(mctx, db, branding, request, token, meta)
})
const result = await method(userCtx, db, branding, request, token, meta)
const body = JSON.stringify(result)
ctx.res.writeHead(200, KEEP_ALIVE_HEADERS)
ctx.res.end(body)
const body = JSON.stringify(result)
ctx.res.writeHead(200, KEEP_ALIVE_HEADERS)
ctx.res.end(body)
} finally {
userCtx.end()
}
})
app.use(router.routes()).use(router.allowedMethods())
+15 -34
View File
@@ -1,7 +1,6 @@
import type { MeasureContext, Metrics } from '@hcengineering/core'
import { concatLink, MeasureMetricsContext, metricsToString, newMetrics, systemAccountUuid } from '@hcengineering/core'
import { concatLink, MeasureMetricsContext, newMetrics, systemAccountUuid } from '@hcengineering/core'
import { generateToken } from '@hcengineering/server-token'
import { writeFile } from 'fs/promises'
import os from 'os'
export interface MemoryStatistics {
@@ -79,14 +78,13 @@ export function initStatisticsContext (
serviceName: string,
ops?: {
logFile?: string
logConsole?: boolean
factory?: () => MeasureMetricsContext
factory?: () => MeasureContext
getStats?: () => WorkspaceStatistics[]
statsUrl?: string
serviceName?: () => string
}
): MeasureContext {
let metricsContext: MeasureMetricsContext
let metricsContext: MeasureContext
if (ops?.factory !== undefined) {
metricsContext = ops.factory()
} else {
@@ -95,16 +93,10 @@ export function initStatisticsContext (
const statsUrl = ops?.statsUrl ?? process.env.STATS_URL
const metricsFile = ops?.logFile
let errorToSend = 0
if (metricsFile !== undefined || ops?.logConsole === true || statsUrl !== undefined) {
if (statsUrl !== undefined) {
metricsContext.info('using stats url', { statsUrl, service: serviceName ?? '' })
if (metricsFile !== undefined) {
console.info('storing measurements into local file', metricsFile)
}
let oldMetricsValue = ''
const serviceId = encodeURIComponent(os.hostname() + '-' + serviceName)
let prev: Promise<void> | Promise<any> | undefined
@@ -120,20 +112,6 @@ export function initStatisticsContext (
const intTimer = setInterval(() => {
try {
if (metricsFile !== undefined || ops?.logConsole === true) {
const val = metricsToString(metricsContext.metrics, serviceName, 140)
if (val !== oldMetricsValue) {
oldMetricsValue = val
if (metricsFile !== undefined) {
void writeFile(metricsFile, val).catch((err) => {
console.error(err)
})
}
if (ops?.logConsole === true) {
console.info('METRICS:', val)
}
}
}
if (prev !== undefined) {
// In case of high load, skip
return
@@ -150,14 +128,17 @@ export function initStatisticsContext (
const statData = JSON.stringify(data)
prev = fetch(concatLink(statsUrl, '/api/v1/statistics') + `/?name=${serviceId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: statData
})
prev = metricsContext
.withoutTracing(() =>
fetch(concatLink(statsUrl, '/api/v1/statistics') + `/?name=${serviceId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: statData
})
)
.finally(() => {
prev = undefined
})
+3 -3
View File
@@ -18,8 +18,8 @@ import { initStatisticsContext } from '@hcengineering/server-core'
import serverToken, { generateToken } from '@hcengineering/server-token'
import { getClient as getAccountClient } from '@hcengineering/account-client'
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics, type SocialId } from '@hcengineering/core'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics, type SocialId } from '@hcengineering/core'
import { join } from 'path'
import config from './config'
import { AIControl } from './controller'
@@ -38,7 +38,7 @@ export const start = async (): Promise<void> => {
const ctx = initStatisticsContext('ai-bot-service', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'ai-bot-service',
{},
{},
@@ -14,21 +14,21 @@
//
import { setMetadata } from '@hcengineering/platform'
import serverToken from '@hcengineering/server-token'
import serverClient from '@hcengineering/server-client'
import serverToken from '@hcengineering/server-token'
import { Analytics } from '@hcengineering/analytics'
import { SplitLogger, configureAnalytics } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { SplitLogger, configureAnalytics, createOpenTelemetryMetricsContext } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { join } from 'path'
import config from './config'
import { initStatisticsContext } from '@hcengineering/server-core'
import config from './config'
import { createServer, listen } from './server'
const ctx = initStatisticsContext('analytics-collector', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'analytics-collector-service',
{},
{},
+3 -3
View File
@@ -14,8 +14,8 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
@@ -37,7 +37,7 @@ export const main = async (): Promise<void> => {
const metricsContext = initStatisticsContext('backup-api', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'backup-api',
{},
{},
+7 -7
View File
@@ -13,16 +13,16 @@
// limitations under the License.
//
import { createServer, listen } from './server'
import config from './config'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { Analytics } from '@hcengineering/analytics'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { join } from 'path'
import { initStatisticsContext } from '@hcengineering/server-core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
import { join } from 'path'
import config from './config'
import { createServer, listen } from './server'
const setupMetadata = (): void => {
setMetadata(serverToken.metadata.Secret, config.Secret)
@@ -38,7 +38,7 @@ export const main = async (): Promise<void> => {
const metricsContext = initStatisticsContext('billing', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'billing',
{},
{},
@@ -13,22 +13,22 @@
// limitations under the License.
//
import { join } from 'path'
import { Analytics } from '@hcengineering/analytics'
import { SplitLogger, configureAnalytics } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext, QueueTopic } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
import { getPlatformQueue } from '@hcengineering/kafka'
import { join } from 'path'
import config from './config'
import { eventCreated, eventDeleted, eventMixin, eventUpdated } from './handlers'
import { EventCUDMessage } from './types'
import { eventCreated, eventUpdated, eventDeleted, eventMixin } from './handlers'
async function main (): Promise<void> {
const ctx = initStatisticsContext('calendar-mailer', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'calendar-mailer',
{},
{},
+3 -2
View File
@@ -14,7 +14,8 @@
//
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { createOpenTelemetryMetricsContext } from '@hcengineering/analytics-service/src'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import serverClient, { getAccountClient } from '@hcengineering/server-client'
import { initStatisticsContext } from '@hcengineering/server-core'
@@ -43,7 +44,7 @@ const extractToken = (header: IncomingHttpHeaders): any => {
export const main = async (): Promise<void> => {
const ctx = initStatisticsContext(CALENDAR_INTEGRATION, {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'calendar',
{},
{},
+3 -3
View File
@@ -14,8 +14,8 @@
//
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext } from '@hcengineering/server-core'
@@ -38,7 +38,7 @@ export const main = async (): Promise<void> => {
const metricsContext = initStatisticsContext('datalake', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'datalake',
{},
{},
+5 -5
View File
@@ -3,14 +3,14 @@
//
import { Analytics } from '@hcengineering/analytics'
import { SplitLogger, configureAnalytics } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { SplitLogger, configureAnalytics, createOpenTelemetryMetricsContext } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext, loadBrandingMap } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
import { join } from 'path'
import config from './config'
import { start } from './server'
import { setMetadata } from '@hcengineering/platform'
import serverToken from '@hcengineering/server-token'
// Load and inc startID, to have easy logs.
@@ -19,7 +19,7 @@ setMetadata(serverToken.metadata.Service, 'github')
const metricsContext = initStatisticsContext('github', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'github',
{},
{},
@@ -28,6 +28,7 @@ describe('AttachmentHandler', () => {
with: jest.fn(),
withSync: jest.fn(),
withLog: jest.fn(),
withoutTracing: jest.fn(),
logger: {
info: jest.fn(),
error: jest.fn(),
+5 -5
View File
@@ -15,15 +15,15 @@
// limitations under the License.
//
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { isWorkspaceLoginInfo } from '@hcengineering/account-client'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { closeQueue, initQueue } from '@hcengineering/mail-common'
import { setMetadata } from '@hcengineering/platform'
import serverClient, { getAccountClient } from '@hcengineering/server-client'
import { isWorkspaceLoginInfo } from '@hcengineering/account-client'
import { initStatisticsContext, type StorageConfiguration } from '@hcengineering/server-core'
import { buildStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage'
import serverToken, { decodeToken } from '@hcengineering/server-token'
import { initQueue, closeQueue } from '@hcengineering/mail-common'
import { type IncomingHttpHeaders } from 'http'
import { join } from 'path'
@@ -44,7 +44,7 @@ const extractToken = (header: IncomingHttpHeaders): any => {
export const main = async (): Promise<void> => {
const ctx = initStatisticsContext('gmail', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'gmail',
{},
{},
+10 -10
View File
@@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, MeasureMetricsContext, newMetrics, Ref, WorkspaceIds } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext, StorageConfig, StorageConfiguration } from '@hcengineering/server-core'
import { storageConfigFromEnv } from '@hcengineering/server-storage'
import serverToken, { decodeToken, Token } from '@hcengineering/server-token'
import {
getClient as getAccountClientRaw,
isWorkspaceLoginInfo,
type AccountClient
} from '@hcengineering/account-client'
import { RoomMetadata, TranscriptionStatus, MeetingMinutes } from '@hcengineering/love'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, newMetrics, Ref, WorkspaceIds } from '@hcengineering/core'
import { MeetingMinutes, RoomMetadata, TranscriptionStatus } from '@hcengineering/love'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext, StorageConfig, StorageConfiguration } from '@hcengineering/server-core'
import { storageConfigFromEnv } from '@hcengineering/server-storage'
import serverToken, { decodeToken, Token } from '@hcengineering/server-token'
import cors from 'cors'
import express, { type Request } from 'express'
import { IncomingHttpHeaders } from 'http'
@@ -38,8 +38,8 @@ import {
WebhookReceiver
} from 'livekit-server-sdk'
import { join } from 'path'
import config from './config'
import { saveLiveKitEgressBilling, updateLiveKitSessions } from './billing'
import config from './config'
import { getRecordingPreset } from './preset'
import { getS3UploadParams, saveFile } from './storage'
import { WorkspaceClient } from './workspaceClient'
@@ -68,7 +68,7 @@ export const main = async (): Promise<void> => {
const ctx = initStatisticsContext('love', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'love',
{},
{},
+9 -9
View File
@@ -13,26 +13,26 @@
// limitations under the License.
//
import cors from 'cors'
import express, { NextFunction, Request, Response } from 'express'
import { join } from 'path'
import { SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, newMetrics } from '@hcengineering/core'
import { closeQueue, initQueue } from '@hcengineering/mail-common'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
import { initQueue, closeQueue } from '@hcengineering/mail-common'
import cors from 'cors'
import express, { NextFunction, Request, Response } from 'express'
import { join } from 'path'
import { handleMtaHook } from './handlerMta'
import config from './config'
import { baseConfig } from './client'
import config from './config'
import { handleMtaHook } from './handlerMta'
type RequestHandler = (req: Request, res: Response, ctx: MeasureContext, next?: NextFunction) => Promise<void>
async function main (): Promise<void> {
const ctx = initStatisticsContext('inbound-mail', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'inbound-mail',
{},
{},
+6 -6
View File
@@ -13,19 +13,19 @@
// limitations under the License.
//
import { type SendMailOptions } from 'nodemailer'
import { Request, Response } from 'express'
import { join } from 'path'
import { type SendMailOptions } from 'nodemailer'
import Mail from 'nodemailer/lib/mailer'
import { join } from 'path'
import { Analytics } from '@hcengineering/analytics'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { MeasureContext, newMetrics } from '@hcengineering/core'
import { initStatisticsContext } from '@hcengineering/server-core'
import { MeasureContext, MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, SplitLogger } from '@hcengineering/analytics-service'
import config from './config'
import { createServer, listen } from './server'
import { MailClient } from './mail'
import { createServer, listen } from './server'
import { Endpoint } from './types'
export const main = async (): Promise<void> => {
@@ -33,7 +33,7 @@ export const main = async (): Promise<void> => {
Analytics.setTag('application', 'mail')
const measureCtx = initStatisticsContext('mail', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'mail',
{},
{},
+6 -6
View File
@@ -13,7 +13,11 @@
// limitations under the License.
//
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import { initStatisticsContext } from '@hcengineering/server-core'
import serverToken from '@hcengineering/server-token'
import bodyParser from 'body-parser'
import cors from 'cors'
import express from 'express'
@@ -21,6 +25,7 @@ import formData from 'express-form-data'
import { type IncomingHttpHeaders, type Server } from 'http'
import morgan from 'morgan'
import os from 'os'
import { join } from 'path'
import { type Readable } from 'stream'
import config from './config'
import { ApiError } from './error'
@@ -29,11 +34,6 @@ import { parseGenericResume } from './generic'
import { decode } from './jwt'
import { extractDocument } from './process'
import { type ReconiDocument } from './types'
import serverToken from '@hcengineering/server-token'
import { setMetadata } from '@hcengineering/platform'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { join } from 'path'
import { SplitLogger } from '@hcengineering/analytics-service'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const extractToken = (header: IncomingHttpHeaders): any => {
@@ -51,7 +51,7 @@ export const startServer = async (): Promise<void> => {
setMetadata(serverToken.metadata.Service, 'rekoni')
const ctx = initStatisticsContext('rekoni', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'rekoni',
{},
{},
+6 -6
View File
@@ -14,21 +14,21 @@
// limitations under the License.
//
import { generateId, MeasureMetricsContext, newMetrics, type WorkspaceIds } from '@hcengineering/core'
import { AccountClient, getClient as getAccountClientRaw, isWorkspaceLoginInfo } from '@hcengineering/account-client'
import { generateId, newMetrics, type WorkspaceIds } from '@hcengineering/core'
import { initStatisticsContext, StorageConfiguration } from '@hcengineering/server-core'
import { buildStorageFromConfig } from '@hcengineering/server-storage'
import { getClient as getAccountClientRaw, AccountClient, isWorkspaceLoginInfo } from '@hcengineering/account-client'
import cors from 'cors'
import express, { type Express, type NextFunction, type Request, type Response } from 'express'
import { type Server } from 'http'
import { type Branding, type BrandingMap, extractBranding } from './branding'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { join } from 'path'
import { extractBranding, type Branding, type BrandingMap } from './branding'
import config from './config'
import { ApiError } from './error'
import { signPDF } from './sign'
import { extractToken } from './token'
import { join } from 'path'
import { SplitLogger } from '@hcengineering/analytics-service'
function getAccountClient (token: string): AccountClient {
return getAccountClientRaw(config.AccountsUrl, token)
@@ -77,7 +77,7 @@ export function createServer (storageConfig: StorageConfiguration, brandings: Br
const storageAdapter = buildStorageFromConfig(storageConfig)
const measureCtx = initStatisticsContext('sign', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'sign',
{},
{},
@@ -14,15 +14,15 @@
//
import { Analytics } from '@hcengineering/analytics'
import { SplitLogger, configureAnalytics } from '@hcengineering/analytics-service'
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { newMetrics } from '@hcengineering/core'
import { getPlatformQueue } from '@hcengineering/kafka'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext, QueueTopic } from '@hcengineering/server-core'
import { TelegramQueueMessage, TelegramQueueMessageType } from '@hcengineering/server-telegram'
import serverToken from '@hcengineering/server-token'
import { join } from 'path'
import { getPlatformQueue } from '@hcengineering/kafka'
import { TelegramQueueMessage, TelegramQueueMessageType } from '@hcengineering/server-telegram'
import config from './config'
import { registerLoaders } from './loaders'
@@ -32,7 +32,7 @@ import { PlatformWorker } from './worker'
const ctx = initStatisticsContext('telegram-bot', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'telegram-bot-service',
{},
{},
+3 -3
View File
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { MeasureMetricsContext, newMetrics } from '@hcengineering/core'
import { newMetrics } from '@hcengineering/core'
import { setMetadata } from '@hcengineering/platform'
import serverClient from '@hcengineering/server-client'
import { initStatisticsContext, type StorageConfiguration } from '@hcengineering/server-core'
@@ -7,7 +7,7 @@ import { buildStorageFromConfig, storageConfigFromEnv } from '@hcengineering/ser
import serverToken, { decodeToken, type Token } from '@hcengineering/server-token'
import { IncomingHttpHeaders } from 'http'
import { SplitLogger } from '@hcengineering/analytics-service'
import { createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service'
import { join } from 'path'
import config from './config'
import { PlatformWorker } from './platform'
@@ -28,7 +28,7 @@ const extractToken = (headers: IncomingHttpHeaders): Token | undefined => {
export const main = async (): Promise<void> => {
const ctx = initStatisticsContext('telegram', {
factory: () =>
new MeasureMetricsContext(
createOpenTelemetryMetricsContext(
'telegram',
{},
{},