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

Feature/improve instrumentation #179

Merged
merged 6 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 19 additions & 3 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,29 @@ const nextConfig = {

return config
},

experimental: {
instrumentationHook: true,
serverComponentsExternalPackages: [
'pino',
'pino-pretty',
'@opentelemetry/instrumentation'
],
instrumentationHook: true
'@opentelemetry/instrumentation',
'@opentelemetry/api',
'@opentelemetry/api-logs',
'@opentelemetry/exporter-logs-otlp-http',
'@opentelemetry/exporter-metrics-otlp-http',
'@opentelemetry/exporter-trace-otlp-http',
'@opentelemetry/instrumentation',
'@opentelemetry/instrumentation-http',
'@opentelemetry/instrumentation-pino',
'@opentelemetry/instrumentation-runtime-node',
'@opentelemetry/resources',
'@opentelemetry/sdk-logs',
'@opentelemetry/sdk-metrics',
'@opentelemetry/sdk-node',
'@opentelemetry/sdk-trace-base',
'@opentelemetry/instrumentation-undici'
]
}
}

Expand Down
31,487 changes: 16,207 additions & 15,280 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 27 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,41 @@
"url": "git+https://github.com/LerianStudio/midaz-console.git"
},
"scripts": {
"dev": "next dev -p 8081",
"build": "next build",
"start": "next start -p 8081",
"lint": "next lint --fix",
"storybook": "storybook dev -p 6007",
"build-storybook": "storybook build",
"extract:i18n": "tsx ./scripts/i18n-extract.ts",
"compile:i18n": "formatjs compile-folder \"./locales/extracted\" --format simple \"./locales/compiled\"",
"i18n": "npm run extract:i18n & npm run compile:i18n",
"test": "jest",
"test:e2e": "playwright test --ui",
"set-local-env": "shx cp .env.local.example .env",
"set-env": "shx cp .env.example .env",
"docker-up": "npm run set-env && docker compose up -d",
"prepare": "husky install",
"format": "prettier --write ."
},
"dev": "next dev -p 8081",
"build": "next build",
"start": "next start -p 8081",
"lint": "next lint --fix",
"storybook": "storybook dev -p 6007",
"build-storybook": "storybook build",
"extract:i18n": "tsx ./scripts/i18n-extract.ts",
"compile:i18n": "formatjs compile-folder \"./locales/extracted\" --format simple \"./locales/compiled\"",
"i18n": "npm run extract:i18n & npm run compile:i18n",
"test": "jest",
"test:e2e": "playwright test --ui",
"set-local-env": "shx cp .env.local.example .env",
"set-env": "shx cp .env.example .env",
"docker-up": "npm run set-env && docker compose up -d",
"prepare": "husky install",
"format": "prettier --write ."
},
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.6",
"@hookform/resolvers": "^3.9.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/api-logs": "^0.57.2",
"@opentelemetry/exporter-logs-otlp-http": "^0.57.2",
"@opentelemetry/exporter-metrics-otlp-http": "^0.56.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.57.2",
"@opentelemetry/instrumentation": "^0.56.0",
"@opentelemetry/instrumentation-http": "^0.56.0",
"@opentelemetry/instrumentation-pino": "^0.46.1",
"@opentelemetry/instrumentation-runtime-node": "^0.11.0",
"@opentelemetry/instrumentation-undici": "^0.10.1",
"@opentelemetry/resources": "^1.29.0",
"@opentelemetry/sdk-logs": "^0.57.2",
"@opentelemetry/sdk-metrics": "^1.29.0",
"@opentelemetry/sdk-node": "^0.57.2",
"@opentelemetry/sdk-trace-node": "^1.30.1",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.1",
Expand Down Expand Up @@ -67,6 +75,7 @@
"next": "^14.1.3",
"next-auth": "^4.24.7",
"pino": "^9.5.0",
"pino-loki": "^2.5.0",
"pino-pretty": "^13.0.0",
"react": "^18.3.1",
"react-chartjs-2": "^5.2.0",
Expand Down Expand Up @@ -117,7 +126,7 @@
"@types/react-dom": "^18",
"autoprefixer": "^10.4.20",
"conventional-changelog-conventionalcommits": "^8.0.0",
"esbuild": "^0.24.0",
"esbuild": "^0.25.0",
"eslint": "^8",
"eslint-config-next": "15.0.1",
"eslint-config-prettier": "^9.1.0",
Expand Down
19 changes: 19 additions & 0 deletions src/core/application/logger/logger-aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ export class LoggerAggregator {
// const logLevel = this.determineLogLevel(context.events)
// Log the final timeline using the logger repository

const logEventInfo = `${context.method} ${context.path}`
const LogEventDetails = {
events: context.events.map((event) => ({
timestamp: new Date(event.timestamp).toISOString(),
level: event.level?.toUpperCase() || 'INFO',
message: event.message,
layer: event.layer,
operation: event.operation,
...(event.error && { error: event.error }),
...(event.context && { context: event.context })
}))
}
const logEventExecution = {
duration: `${duration}s`,
path: context.path,
method: context.method,
...context.metadata
}

this.loggerRepository.info(
`${context.method} ${context.path}`,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { MidazRequestContext } from '../logger/decorators/midaz-id'
import { TransactionUseCaseModule } from './use-cases/transactions-module'
import { MidazHttpFetchModule } from './midaz-http-fetch-module'
import { OnboardingUseCaseModule } from './use-cases/onboarding-module'
import { OtelModule } from './observability/otel-module'

export const container = new Container()

Expand All @@ -36,6 +37,7 @@ container.load(LoggerApplicationModule)
container.load(TransactionUseCaseModule)

container.load(MidazHttpFetchModule)
container.load(OtelModule)

container
.bind<MidazRequestContext>(MidazRequestContext)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { OtelTracerProvider } from '../../observability/otel-tracer-provider'
import { Container, ContainerModule } from '../../utils/di/container'

export const OtelModule = new ContainerModule((container: Container) => {
container.bind<OtelTracerProvider>(OtelTracerProvider).toSelf()
})
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
import { Container, ContainerModule } from '../../utils/di/container'

import {
FetchAllAccounts,
FetchAllAccountsUseCase
} from '@/core/application/use-cases/accounts/fetch-all-account-use-case'
import {
CreateAccount,
CreateAccountUseCase
} from '@/core/application/use-cases/accounts/create-account-use-case'
import {
FetchAccountById,
FetchAccountByIdUseCase
} from '@/core/application/use-cases/accounts/fetch-account-by-id-use-case'
import {
UpdateAccount,
UpdateAccountUseCase
} from '@/core/application/use-cases/accounts/update-account-use-case'
import {
DeleteAccount,
DeleteAccountUseCase
} from '@/core/application/use-cases/accounts/delete-account-use-case'
import {
FetchAccountsWithPortfolios,
FetchAccountsWithPortfoliosUseCase
} from '@/core/application/use-cases/accounts-with-portfolios/fetch-accounts-with-portfolios-use-case'

export const AccountUseCaseModule = new ContainerModule(
(container: Container) => {
container.bind<FetchAllAccounts>(FetchAllAccountsUseCase).toSelf()
container.bind<CreateAccount>(CreateAccountUseCase).toSelf()
container.bind<FetchAccountById>(FetchAccountByIdUseCase).toSelf()
container.bind<UpdateAccount>(UpdateAccountUseCase).toSelf()
container.bind<DeleteAccount>(DeleteAccountUseCase).toSelf()
container
.bind<FetchAccountsWithPortfolios>(FetchAccountsWithPortfoliosUseCase)
.toSelf()
}
)
import { Container, ContainerModule } from '../../utils/di/container'
import {
FetchAllAccounts,
FetchAllAccountsUseCase
} from '@/core/application/use-cases/accounts/fetch-all-account-use-case'
import {
CreateAccount,
CreateAccountUseCase
} from '@/core/application/use-cases/accounts/create-account-use-case'
import {
FetchAccountById,
FetchAccountByIdUseCase
} from '@/core/application/use-cases/accounts/fetch-account-by-id-use-case'
import {
UpdateAccount,
UpdateAccountUseCase
} from '@/core/application/use-cases/accounts/update-account-use-case'
import {
DeleteAccount,
DeleteAccountUseCase
} from '@/core/application/use-cases/accounts/delete-account-use-case'
import {
FetchAccountsWithPortfolios,
FetchAccountsWithPortfoliosUseCase
} from '@/core/application/use-cases/accounts-with-portfolios/fetch-accounts-with-portfolios-use-case'
export const AccountUseCaseModule = new ContainerModule(
(container: Container) => {
container.bind<FetchAllAccounts>(FetchAllAccountsUseCase).toSelf()
container.bind<CreateAccount>(CreateAccountUseCase).toSelf()
container.bind<FetchAccountById>(FetchAccountByIdUseCase).toSelf()
container.bind<UpdateAccount>(UpdateAccountUseCase).toSelf()
container.bind<DeleteAccount>(DeleteAccountUseCase).toSelf()
container
.bind<FetchAccountsWithPortfolios>(FetchAccountsWithPortfoliosUseCase)
.toSelf()
}
)
16 changes: 8 additions & 8 deletions src/core/infrastructure/logger/pino-logger-repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import pino, { Logger, LoggerOptions } from 'pino'
import pino, { BaseLogger, LoggerOptions } from 'pino'
import { injectable } from 'inversify'
import {
LogContext,
Expand All @@ -10,13 +10,13 @@ import pretty from 'pino-pretty'

@injectable()
export class PinoLoggerRepository implements LoggerRepository {
private logger: Logger
private logger: BaseLogger

constructor() {
this.logger = this.initializeLogger()
}

private initializeLogger(): Logger {
private initializeLogger(): BaseLogger {
const isDebugEnabled = process.env.ENABLE_DEBUG === 'true'
const loggerOptions: LoggerOptions = {
level: isDebugEnabled ? 'debug' : 'info',
Expand Down Expand Up @@ -60,26 +60,26 @@ export class PinoLoggerRepository implements LoggerRepository {

info(message: string, context: LogContext, metadata?: LogMetadata): void {
const logEntry = this.createLogEntry('INFO', message, metadata, context)
this.logger.info(logEntry)
this.logger.info(JSON.stringify(logEntry))
}
Copy link
Preview

Copilot AI Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using JSON.stringify for logging structured objects may hinder downstream processing that expects structured data. It is recommended to pass the log object directly.

Suggested change
this.logger.info(JSON.stringify(logEntry))
this.logger.info(logEntry)

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options

error(message: string, context: LogContext, metadata?: LogMetadata): void {
const logEntry = this.createLogEntry('ERROR', message, metadata, context)
this.logger.error(logEntry)
this.logger.error(JSON.stringify(logEntry))
}

warn(message: string, context: LogContext, metadata?: LogMetadata): void {
const logEntry = this.createLogEntry('WARN', message, metadata, context)
this.logger.warn(logEntry)
this.logger.warn(JSON.stringify(logEntry))
}

debug(message: string, context: LogContext, metadata?: LogMetadata): void {
const logEntry = this.createLogEntry('DEBUG', message, metadata, context)
this.logger.debug(logEntry)
this.logger.debug(JSON.stringify(logEntry))
}

audit(message: string, context: LogContext, metadata?: LogMetadata): void {
const logEntry = this.createLogEntry('AUDIT', message, metadata, context)
this.logger.info(logEntry)
this.logger.info(JSON.stringify(logEntry))
}
}
62 changes: 37 additions & 25 deletions src/core/infrastructure/observability/instrumentation-config.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
import { Resource } from '@opentelemetry/resources'
import {
MeterProvider,
PeriodicExportingMetricReader
} from '@opentelemetry/sdk-metrics'
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'
import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino'
import { RuntimeNodeInstrumentation } from '@opentelemetry/instrumentation-runtime-node'
import { metrics } from '@opentelemetry/api'

const metricExporter = new OTLPMetricExporter({
url: process.env.OTEL_URL
})

const metricReader = new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 5000
})
import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici'
import { Resource } from '@opentelemetry/resources'
import { SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs'
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'

const meterProvider = new MeterProvider({
const sdk = new NodeSDK({
resource: new Resource({
'service.name': 'midaz-console'
[ATTR_SERVICE_NAME]: 'midaz-console'
}),
readers: [metricReader]
})

metrics.setGlobalMeterProvider(meterProvider)

registerInstrumentations({
spanProcessors: [
new SimpleSpanProcessor(
new OTLPTraceExporter({
url: process.env.OTEL_URL_TRACES
})
)
],
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: process.env.OTEL_URL_METRICS
}),
exportIntervalMillis: 5000
}),
logRecordProcessors: [
new SimpleLogRecordProcessor(
new OTLPLogExporter({
url: process.env.OTEL_URL_LOGS
})
)
],
instrumentations: [
new HttpInstrumentation(),
new RuntimeNodeInstrumentation()
new RuntimeNodeInstrumentation(),
new PinoInstrumentation(),
new UndiciInstrumentation()
]
})

sdk.start()
Loading
Loading