Skip to content

Commit 14b7e61

Browse files
authored
Merge pull request #9 from topcoder-platform/fix/wiston-logger-ecs-fix
#8 Winston Logger ECS Deployment Fix
2 parents 231a1c8 + f001894 commit 14b7e61

File tree

6 files changed

+115
-35
lines changed

6 files changed

+115
-35
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ LOG_LEVEL=info
1414
# The directory to store log files
1515
LOG_DIR=logs
1616

17+
# leave unset (defaults to false in production)
18+
ENABLE_FILE_LOGGING=true
19+
1720
# -------------------------------------
1821
# Kafka Configuration
1922
# -------------------------------------

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ Open the `.env` file and configure the variables for your environment. It is cru
4141
PORT=3000
4242
LOG_LEVEL=debug
4343
LOG_DIR=logs
44+
# Enable file logging in production environments (default: false)
45+
# In development environments, file logging is enabled by default
46+
ENABLE_FILE_LOGGING=false
4447
4548
# -------------------------------------
4649
# Kafka Configuration
@@ -206,6 +209,31 @@ tsconfig.json # TypeScript config
206209
README.md # Documentation
207210
```
208211

212+
## Logging Configuration
213+
214+
The autopilot service uses a flexible logging strategy that adapts to different deployment environments:
215+
216+
### Development Environment
217+
- **File Logging**: Enabled by default for debugging purposes
218+
- **Console Logging**: Always enabled with colorized output
219+
- **Log Files**: Created in the `logs/` directory (configurable via `LOG_DIR`)
220+
221+
### Production Environment (ECS)
222+
- **File Logging**: Disabled by default to support read-only filesystems
223+
- **Console Logging**: Primary logging method, suitable for container log collection
224+
- **Override**: Set `ENABLE_FILE_LOGGING=true` to enable file logging if the filesystem is writable
225+
226+
### Environment Variables
227+
- `LOG_LEVEL`: Controls log verbosity (`error`, `warn`, `info`, `debug`, `verbose`)
228+
- `LOG_DIR`: Directory for log files (default: `logs`)
229+
- `ENABLE_FILE_LOGGING`: Boolean flag to control file logging in production
230+
231+
### ECS Deployment Notes
232+
When deploying to ECS, ensure:
233+
1. `NODE_ENV=production` is set
234+
2. `ENABLE_FILE_LOGGING` is either unset or set to `false` for read-only filesystems
235+
3. Container logging is configured to collect stdout/stderr logs
236+
209237
## Further Documentation
210238

211239
For more detailed technical information, please see the documents in the `docs/` directory:
@@ -228,3 +256,13 @@ For more detailed technical information, please see the documents in the `docs/`
228256
```
229257

230258
- **API Authentication Errors**: Ensure Auth0 credentials (`AUTH0_URL`, `AUTH0_CLIENT_ID`, `AUTH0_CLIENT_SECRET`, `AUTH0_AUDIENCE`) in your `.env` file are valid and not expired.
259+
260+
- **ECS Deployment - Read-only Filesystem Error**: If the application fails to start in ECS with errors about creating a 'logs' directory:
261+
- Ensure `NODE_ENV=production` is set in your ECS task definition
262+
- Verify `ENABLE_FILE_LOGGING` is not set or is set to `false`
263+
- Check that your ECS task definition is configured to collect logs from stdout/stderr
264+
265+
- **Logging Issues**:
266+
- **No log files in development**: Check that `ENABLE_FILE_LOGGING` is not set to `false`
267+
- **File permission errors**: Ensure the application has write permissions to the `LOG_DIR` directory
268+
- **Missing logs in production**: Verify your container logging configuration is collecting stdout/stderr output

src/common/constants/config.constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export interface AppConfig {
33
DEFAULT_NODE_ENV: 'development' | 'production' | 'test';
44
DEFAULT_LOG_LEVEL: 'error' | 'warn' | 'info' | 'debug' | 'verbose';
55
DEFAULT_LOG_DIR: string;
6+
DEFAULT_ENABLE_FILE_LOGGING: boolean;
67
}
78

89
export interface KafkaConfig {
@@ -51,6 +52,7 @@ export const CONFIG: Config = {
5152
DEFAULT_NODE_ENV: 'development',
5253
DEFAULT_LOG_LEVEL: 'info',
5354
DEFAULT_LOG_DIR: 'logs',
55+
DEFAULT_ENABLE_FILE_LOGGING: false,
5456
},
5557
KAFKA: {
5658
DEFAULT_CLIENT_ID: 'autopilot-service',

src/common/services/logger.service.ts

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,51 +17,86 @@ export class LoggerService implements NestLoggerService {
1717
}
1818

1919
private createWinstonLogger(): Logger {
20+
const baseTransports: any[] = [
21+
new transports.Console({
22+
level: process.env.LOG_LEVEL || CONFIG.APP.DEFAULT_LOG_LEVEL,
23+
format: format.combine(
24+
format.colorize(),
25+
format.printf(({ timestamp, level, message, context, ...meta }) => {
26+
const formattedTimestamp =
27+
timestamp instanceof Date
28+
? timestamp.toISOString()
29+
: String(timestamp);
30+
const formattedContext =
31+
typeof context === 'string' ? context : 'App';
32+
const formattedMessage =
33+
typeof message === 'string'
34+
? message
35+
: typeof message === 'object' && message !== null
36+
? JSON.stringify(message)
37+
: String(message);
38+
39+
return `${formattedTimestamp} [${formattedContext}] ${String(level)}: ${formattedMessage}${
40+
Object.keys(meta).length
41+
? ' ' + JSON.stringify(meta, null, 1)
42+
: ''
43+
}`;
44+
}),
45+
),
46+
}),
47+
];
48+
49+
// Add file transports conditionally based on environment
50+
const shouldEnableFileLogging = this.shouldEnableFileLogging();
51+
if (shouldEnableFileLogging) {
52+
try {
53+
baseTransports.push(
54+
new transports.File({
55+
filename: `${process.env.LOG_DIR || CONFIG.APP.DEFAULT_LOG_DIR}/error.log`,
56+
level: 'error',
57+
}),
58+
new transports.File({
59+
filename: `${process.env.LOG_DIR || CONFIG.APP.DEFAULT_LOG_DIR}/combined.log`,
60+
}),
61+
);
62+
} catch (error) {
63+
// If file transport creation fails (e.g., read-only filesystem),
64+
// log a warning but continue with console-only logging
65+
console.warn(
66+
'Failed to initialize file transports, falling back to console-only logging:',
67+
error instanceof Error ? error.message : String(error),
68+
);
69+
}
70+
}
71+
2072
return createLogger({
2173
format: format.combine(
2274
format.timestamp(),
2375
format.errors({ stack: true }),
2476
format.json(),
2577
),
2678
defaultMeta: { context: this.context },
27-
transports: [
28-
new transports.Console({
29-
level: process.env.LOG_LEVEL || CONFIG.APP.DEFAULT_LOG_LEVEL,
30-
format: format.combine(
31-
format.colorize(),
32-
format.printf(({ timestamp, level, message, context, ...meta }) => {
33-
const formattedTimestamp =
34-
timestamp instanceof Date
35-
? timestamp.toISOString()
36-
: String(timestamp);
37-
const formattedContext =
38-
typeof context === 'string' ? context : 'App';
39-
const formattedMessage =
40-
typeof message === 'string'
41-
? message
42-
: typeof message === 'object' && message !== null
43-
? JSON.stringify(message)
44-
: String(message);
45-
46-
return `${formattedTimestamp} [${formattedContext}] ${String(level)}: ${formattedMessage}${
47-
Object.keys(meta).length
48-
? ' ' + JSON.stringify(meta, null, 1)
49-
: ''
50-
}`;
51-
}),
52-
),
53-
}),
54-
new transports.File({
55-
filename: `${process.env.LOG_DIR || CONFIG.APP.DEFAULT_LOG_DIR}/error.log`,
56-
level: 'error',
57-
}),
58-
new transports.File({
59-
filename: `${process.env.LOG_DIR || CONFIG.APP.DEFAULT_LOG_DIR}/combined.log`,
60-
}),
61-
],
79+
transports: baseTransports,
6280
});
6381
}
6482

83+
/**
84+
* Determines whether file logging should be enabled based on environment configuration
85+
*/
86+
private shouldEnableFileLogging(): boolean {
87+
const nodeEnv = process.env.NODE_ENV;
88+
const enableFileLogging = process.env.ENABLE_FILE_LOGGING;
89+
90+
// In production, only enable file logging if explicitly requested
91+
if (nodeEnv === 'production') {
92+
return enableFileLogging === 'true';
93+
}
94+
95+
// In non-production environments (development, test), enable file logging by default
96+
// unless explicitly disabled
97+
return enableFileLogging !== 'false';
98+
}
99+
65100
private formatMessage(message: unknown): string {
66101
if (typeof message === 'string') {
67102
return message;

src/config/sections/app.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ export default registerAs('app', () => ({
66
logging: {
77
level: process.env.LOG_LEVEL || 'info',
88
directory: process.env.LOG_DIR || 'logs',
9+
enableFileLogging: process.env.ENABLE_FILE_LOGGING === 'true',
910
},
1011
}));

src/config/validation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const validationSchema = Joi.object({
1111
.valid('error', 'warn', 'info', 'debug', 'verbose')
1212
.default('info'),
1313
LOG_DIR: Joi.string().default('logs'),
14+
ENABLE_FILE_LOGGING: Joi.boolean().default(false),
1415

1516
// Kafka Configuration
1617
KAFKA_BROKERS: Joi.string().required(),

0 commit comments

Comments
 (0)