Skip to content
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
26 changes: 14 additions & 12 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,37 @@ import { DataSource } from 'typeorm';
import { AppController } from './app.controller.js';
import { GlobalDatabaseContext } from './common/application/global-database-context.js';
import { BaseType, UseCaseType } from './common/data-injection.tokens.js';
import { AIModule } from './entities/ai/ai.module.js';
import { ApiKeyModule } from './entities/api-key/api-key.module.js';
import { CompanyFaviconModule } from './entities/company-favicon/company-favicon.module.js';
import { CompanyInfoModule } from './entities/company-info/company-info.module.js';
import { CompanyLogoModule } from './entities/company-logo/company-logo.module.js';
import { CompanyTabTitleModule } from './entities/company-tab-title/company-tab-title.module.js';
import { ConnectionPropertiesModule } from './entities/connection-properties/connection-properties.module.js';
import { ConnectionModule } from './entities/connection/connection.module.js';
import { ConversionModule } from './entities/convention/conversion.module.js';
import { CronJobsModule } from './entities/cron-jobs/cron-jobs.module.js';
import { CustomFieldModule } from './entities/custom-field/custom-field.module.js';
import { DemoDataModule } from './entities/demo-data/demo-deta.module.js';
import { EmailModule } from './entities/email/email/email.module.js';
import { GroupModule } from './entities/group/group.module.js';
import { LoggingModule } from './entities/logging/logging.module.js';
import { PermissionModule } from './entities/permission/permission.module.js';
import { TableTriggersModule } from './entities/table-actions/table-action-rules-module/action-rules.module.js';
import { TableActionModule } from './entities/table-actions/table-actions-module/table-action.module.js';
import { TableFiltersModule } from './entities/table-filters/table-filters.module.js';
import { TableLogsModule } from './entities/table-logs/table-logs.module.js';
import { TableSettingsModule } from './entities/table-settings/table-settings.module.js';
import { TableModule } from './entities/table/table.module.js';
import { UserActionModule } from './entities/user-actions/user-action.module.js';
import { UserModule } from './entities/user/user.module.js';
import { TableWidgetModule } from './entities/widget/table-widget.module.js';
import { TimeoutInterceptor } from './interceptors/index.js';
import { SaaSGatewayModule } from './microservices/gateways/saas-gateway.ts/saas-gateway.module.js';
import { SaasModule } from './microservices/saas-microservice/saas.module.js';
import { AppLoggerMiddleware } from './middlewares/logging-middleware/app-logger-middlewate.js';
import { DatabaseModule } from './shared/database/database.module.js';
import { GetHelloUseCase } from './use-cases-app/get-hello.use.case.js';
import { SaasModule } from './microservices/saas-microservice/saas.module.js';
import { SaaSGatewayModule } from './microservices/gateways/saas-gateway.ts/saas-gateway.module.js';
import { CompanyInfoModule } from './entities/company-info/company-info.module.js';
import { TableTriggersModule } from './entities/table-actions/table-action-rules-module/action-rules.module.js';
import { ApiKeyModule } from './entities/api-key/api-key.module.js';
import { AIModule } from './entities/ai/ai.module.js';
import { EmailModule } from './entities/email/email/email.module.js';
import { CompanyLogoModule } from './entities/company-logo/company-logo.module.js';
import { CompanyFaviconModule } from './entities/company-favicon/company-favicon.module.js';
import { CompanyTabTitleModule } from './entities/company-tab-title/company-tab-title.module.js';
import { TableFiltersModule } from './entities/table-filters/table-filters.module.js';
import { DemoDataModule } from './entities/demo-data/demo-deta.module.js';

@Module({
imports: [
Expand Down Expand Up @@ -66,6 +67,7 @@ import { DemoDataModule } from './entities/demo-data/demo-deta.module.js';
CompanyTabTitleModule,
TableFiltersModule,
DemoDataModule,
LoggingModule,
],
controllers: [AppController],
providers: [
Expand Down
30 changes: 30 additions & 0 deletions backend/src/entities/logging/logger-transports.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import winston from 'winston';

export const LoggerTransports = {
default: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json(),
),
}),
],
debug: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.colorize({
all: true,
colors: { info: 'blue', error: 'red', warn: 'yellow', debug: 'cyan' },
}),
winston.format.printf(({ timestamp, level, message, context, trace }) => {
const logContext = context ? ` [${context}]` : '';
const logTrace = trace ? `\nTrace: ${trace}` : '';
return `${timestamp} [${level}]${logContext}: ${message}${logTrace}`;
}),
),
}),
],
};
9 changes: 9 additions & 0 deletions backend/src/entities/logging/logging.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Global, Module } from '@nestjs/common';
import { WinstonLogger } from './winston-logger.js';

@Global()
@Module({
providers: [WinstonLogger],
exports: [WinstonLogger],
})
export class LoggingModule {}
50 changes: 50 additions & 0 deletions backend/src/entities/logging/winston-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Injectable, LoggerService } from '@nestjs/common';
import winston from 'winston';
import { slackPostMessage } from '../../helpers/index.js';
import { LoggerTransports } from './logger-transports.config.js';

@Injectable()
export class WinstonLogger implements LoggerService {
private readonly logger: winston.Logger;

constructor() {
this.logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
transports: LoggerTransports.default,
});
}

log(message: any, ...optionalParams: any[]) {
this.logger.info(message, ...optionalParams);
}

error(message: any, ...optionalParams: any[]) {
this.logger.error(message, ...optionalParams);
}

warn(message: any, ...optionalParams: any[]) {
this.logger.warn(message, ...optionalParams);
}

logWithSlack(message: any, ...optionalParams: any[]) {
this.logger.error(message, ...optionalParams);
slackPostMessage(message).catch((error) => {
this.logger.error('Failed to send Slack message', error);
});
}

debug(message: any, ...optionalParams: any[]) {
this.logger.debug(message, ...optionalParams);
}

verbose(message: any, ...optionalParams: any[]) {
this.logger.verbose(message, ...optionalParams);
}

fatal(message: any, ...optionalParams: any[]) {
this.logger.error(message, ...optionalParams);
slackPostMessage(message).catch((error) => {
this.logger.error('Failed to send Slack message', error);
});
}
}
19 changes: 10 additions & 9 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import { NestApplicationOptions, ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import * as Sentry from '@sentry/node';
import bodyParser from 'body-parser';
import { ValidationError } from 'class-validator';
import cookieParser from 'cookie-parser';
import rateLimit from 'express-rate-limit';
import session from 'express-session';
import helmet from 'helmet';
import { ApplicationModule } from './app.module.js';
import { WinstonLogger } from './entities/logging/winston-logger.js';
import { AllExceptionsFilter } from './exceptions/all-exceptions.filter.js';
import { ValidationException } from './exceptions/custom-exceptions/validation-exception.js';
import { Constants } from './helpers/constants/constants.js';
import { requiredEnvironmentVariablesValidator } from './helpers/validators/required-environment-variables.validator.js';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { NestApplicationOptions, ValidationPipe } from '@nestjs/common';
import { ValidationError } from 'class-validator';
import { ValidationException } from './exceptions/custom-exceptions/validation-exception.js';
import bodyParser from 'body-parser';
import { NestExpressApplication } from '@nestjs/platform-express';
import session from 'express-session';

async function bootstrap() {
try {
process.env.NO_COLOR = 'true';
requiredEnvironmentVariablesValidator();
const appOptions: NestApplicationOptions = {
rawBody: true,
logger: ['error', 'warn', 'fatal', 'warn'],
logger: new WinstonLogger(),
};

const app = await NestFactory.create<NestExpressApplication>(ApplicationModule, appOptions);
app.useLogger(app.get(WinstonLogger));
app.set('query parser', 'extended');

Sentry.init({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
import { Logger } from '../../helpers/logging/Logger.js';
import { WinstonLogger } from '../../entities/logging/winston-logger.js';

@Injectable()
export class AppLoggerMiddleware implements NestMiddleware {
constructor(private readonly logger: WinstonLogger) {}

use(request: Request, response: Response, next: NextFunction): void {
const { ip, method, path: url } = request;
const { ip, method, path: url, baseUrl } = request;
const userAgent = request.get('user-agent') || '';
Logger.logInfoString(`START ${method} ${url} - ${userAgent} ${ip}`);
this.logger.log(`START ${method} ${url}${baseUrl} - ${userAgent} ${ip}`, { context: 'HTTP' });
response.on('close', () => {
const { statusCode } = response;
const contentLength = response.get('content-length');
Logger.logInfoString(
this.logger.log(
`method: ${method}, url: ${url}, statusCode: ${statusCode}, contentLength: ${contentLength}, userAgent: ${userAgent}, ip: ${ip}`,
{ context: 'HTTP' },
);
});

Expand Down
Loading