Skip to content

Commit

Permalink
feat: improve midaz log lib to improve dev experience
Browse files Browse the repository at this point in the history
  • Loading branch information
Paulo Barbosa authored and Paulo Barbosa committed Jan 15, 2025
1 parent 95bba8e commit c23a36d
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { NextRequest, NextResponse } from 'next/server'
import { applyMiddleware } from '@/lib/applymiddleware/apply-middleware'
import { loggerMiddleware } from '@/utils/logger-middleware-config'
import { LoggerAggregator } from '@/core/application/logger/logger-aggregator'

const createPortfolioUseCase: CreatePortfolio = container.get<CreatePortfolio>(
CreatePortfolioUseCase
Expand All @@ -19,6 +20,7 @@ const createPortfolioUseCase: CreatePortfolio = container.get<CreatePortfolio>(
const fetchAllPortfoliosUseCase: FetchAllPortfolios =
container.get<FetchAllPortfolios>(FetchAllPortfoliosUseCase)

const midazLogger = container.get(LoggerAggregator)
interface PortfolioParams {
id: string
ledgerId: string
Expand All @@ -29,8 +31,6 @@ export const GET = applyMiddleware(
loggerMiddleware({
operationName: 'fetchAllPortfolios',
method: 'GET',
useCase: 'FetchAllPortfoliosUseCase',
logLevel: 'debug'
})
],
async (request: NextRequest, { params }: { params: PortfolioParams }) => {
Expand All @@ -48,6 +48,8 @@ export const GET = applyMiddleware(
limit
)



return NextResponse.json(portfolios)
} catch (error: any) {
const { message, status } = await apiErrorHandler(error)
Expand All @@ -61,23 +63,30 @@ export const POST = applyMiddleware(
loggerMiddleware({
operationName: 'createPortfolio',
method: 'POST',
useCase: 'CreatePortfolioUseCase',
logLevel: 'info'
})
],
async (request: NextRequest, { params }: { params: PortfolioParams }) => {
try {
const { id: organizationId, ledgerId } = params

const body = await request.json()
const portfolio = await createPortfolioUseCase.execute(
organizationId,
ledgerId,
body
)

midazLogger.info(`organization logger ${organizationId}`, {teste: 123})

midazLogger.debug(`body ${JSON.stringify(body)}`)

return NextResponse.json(portfolio)
} catch (error) {
} catch (error: any) {
const { message, status } = await apiErrorHandler(error)
midazLogger.error({
message: error.message,
context: error
})
return NextResponse.json({ message }, { status })
}
}
Expand Down
111 changes: 77 additions & 34 deletions src/core/application/logger/logger-aggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ type Level = 'info' | 'error' | 'warn' | 'debug' | 'audit'

interface LogEvent {
timestamp: number
layer: Layer
operation: string
level: Level
layer?: Layer
operation?: string
level?: Level
message: string
metadata?: Record<string, any>
context?: any
error?: Error
}

Expand Down Expand Up @@ -111,46 +111,32 @@ export class LoggerAggregator {
// Calculate the total request duration in seconds
const duration = (Date.now() - context.startTime) / 1000
// Determine the most severe log level from all collected events
const logLevel = this.determineLogLevel(context.events)
// const logLevel = this.determineLogLevel(context.events)
// Log the final timeline using the logger repository
this.loggerRepository[logLevel](
'Request Timeline',

this.loggerRepository.info(
`${context.method} ${context.path}`,
{
// Transform all events timestamps to ISO string format
// while preserving all other event properties
events: context.events.map((event) => ({
...event,
timestamp: new Date(event.timestamp).toISOString()
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 })
}))
},
{
// Add request metadata to help with debugging and monitoring
layer: 'api',
operation: 'request_timeline',
path: context.path, // The API endpoint path
method: context.method, // HTTP method used (GET, POST, etc)
duration, // Total request duration in seconds
metadata: context.metadata // Any additional request metadata
duration: `${duration}s`,
path: context.path,
method: context.method,
...context.metadata
}
)
}
}

/**
* Determines the overall log level for the request based on event severity
* Prioritizes errors over warnings, warnings over audit, etc.
* @param events - List of log events to analyze
*/
private determineLogLevel(events: LogEvent[]): Level {
const levelPriority: Level[] = ['error', 'warn', 'audit', 'info', 'debug']
for (const level of levelPriority) {
if (events.some((event) => event.level === level)) {
return level
}
}
return 'debug'
}

/**
* Retrieves the current request context from storage
* @returns The current RequestContext or undefined if not in a request context
Expand Down Expand Up @@ -178,4 +164,61 @@ export class LoggerAggregator {
context.events.push(fullEvent)
}
}

/**
* Helper method to create a standardized log event
*/
private createLogEvent(
level: Level,
message: any,
context?: any,
defaultLayer?: Layer
): Omit<LogEvent, 'timestamp'> {
if (typeof message === 'string') {
return {
level,
message,
context,
layer: defaultLayer
}
}

return {
message: message.message || '',
...message,
level,
layer: message.layer || defaultLayer,
context: {
...message.context
}
}
}

debug(message: any, context?: any) {
if (!this.shouldLogDebug()) {
return
}
const event = this.createLogEvent('debug', message, context)
this.addEvent(event)
}

info(message: any, context?: any) {
const event = this.createLogEvent('info', message, context)
this.addEvent(event)
}

error(message: any, context?: any) {
const event = this.createLogEvent('error', message, context)
this.addEvent(event)
}

warn(message: any, context?: any) {
const event = this.createLogEvent('warn', message, context)
this.addEvent(event)
}

audit(message: any, context?: any) {
const event = this.createLogEvent('audit', message, context)
this.addEvent(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
} from '../../dto/portfolios-dto'
import { PortfolioEntity } from '@/core/domain/entities/portfolios-entity'
import { inject, injectable } from 'inversify'
import { LoggerAggregator } from '../../logger/logger-aggregator'

export interface CreatePortfolio {
execute: (
Expand All @@ -19,7 +20,7 @@ export interface CreatePortfolio {
export class CreatePortfolioUseCase implements CreatePortfolio {
constructor(
@inject(CreatePortfolioRepository)
private readonly createPortfolioRepository: CreatePortfolioRepository
private readonly createPortfolioRepository: CreatePortfolioRepository,
) {}

async execute(
Expand Down
4 changes: 2 additions & 2 deletions src/core/domain/entities/log-entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export interface LogEntry {
message: string
timestamp: string
midazId?: string
metadata: LogMetadata
context: LogContext
metadata?: LogMetadata
context?: LogContext
}
10 changes: 5 additions & 5 deletions src/core/domain/repositories/logger/logger-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,27 @@ import { LogContext, LogMetadata } from '@/core/domain/entities/log-entities'
export abstract class LoggerRepository {
abstract info(
message: string,
context: LogContext,
context?: LogContext,
metadata?: LogMetadata
): void
abstract error(
message: string,
context: LogContext,
context?: LogContext,
metadata?: LogMetadata
): void
abstract warn(
message: string,
context: LogContext,
context?: LogContext,
metadata?: LogMetadata
): void
abstract debug(
message: string,
context: LogContext,
context?: LogContext,
metadata?: LogMetadata
): void
abstract audit(
message: string,
context: LogContext,
context?: LogContext,
metadata?: LogMetadata
): void
}
5 changes: 2 additions & 3 deletions src/core/infrastructure/logger/pino-logger-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ export class PinoLoggerRepository implements LoggerRepository {
},
timestamp: pino.stdTimeFunctions.isoTime,
base: {
env: process.env.NODE_ENV || 'production',
version: process.env.APP_VERSION || '1.0.0'
env: process.env.NODE_ENV || 'production'
}
}

Expand All @@ -35,7 +34,7 @@ export class PinoLoggerRepository implements LoggerRepository {
loggerOptions,
pretty({
colorize: true,
ignore: 'pid,hostname',
ignore: 'pid,hostname,level',
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l'
})
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HTTP_METHODS } from '../../utils/http-fetch-utils'
import { injectable, inject } from 'inversify'
import { MidazHttpFetchUtils } from '../../utils/http-fetch-utils'
import { ContainerTypeMidazHttpFetch } from '../../container-registry/midaz-http-fetch-module'
import { LoggerAggregator } from '@/core/application/logger/logger-aggregator'

@injectable()
export class MidazCreatePortfolioRepository
Expand All @@ -13,7 +14,9 @@ export class MidazCreatePortfolioRepository

constructor(
@inject(ContainerTypeMidazHttpFetch.MidazHttpFetchUtils)
private readonly midazHttpFetchUtils: MidazHttpFetchUtils
private readonly midazHttpFetchUtils: MidazHttpFetchUtils,
@inject(LoggerAggregator)
private readonly loggerAggregator: LoggerAggregator
) {}

async create(
Expand All @@ -29,7 +32,7 @@ export class MidazCreatePortfolioRepository
method: HTTP_METHODS.POST,
body: JSON.stringify(portfolio)
})

return response
}
}
20 changes: 10 additions & 10 deletions src/utils/logger-middleware-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { NextRequest } from 'next/server'
interface LoggerMiddlewareConfig {
operationName: string
method: string
useCase: string
useCase?: string
action?: string
logLevel?: 'info' | 'error' | 'warn' | 'debug' | 'audit'
}
Expand Down Expand Up @@ -36,15 +36,15 @@ export function loggerMiddleware(config: LoggerMiddlewareConfig) {
midazId: midazRequestContext.getMidazId()
},
async () => {
loggerAggregator.addEvent({
message: `${config.operationName} operation`,
...(shouldIncludePayload && {
metadata: { body }
}),
layer: 'application',
operation: config.operationName,
level: config.logLevel || 'info'
})
// loggerAggregator.addEvent({
// message: `${config.operationName} operation`,
// ...(shouldIncludePayload && {
// metadata: { body }
// }),
// layer: 'application',
// operation: config.operationName,
// level: config.logLevel || 'info'
// })
const response = await next()
return response
}
Expand Down

0 comments on commit c23a36d

Please sign in to comment.