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
3 changes: 2 additions & 1 deletion .nycrc.unit.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"exclude": [
"**/*.spec.js",
"src/index.js",
"src/server.js"
"src/server.js",
"src/utils/logger.js"
],
"branches": 90,
"lines": 90,
Expand Down
23 changes: 8 additions & 15 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,8 @@ server.addHook('onRequest', (request, reply, done) => {
request.log.info({
reqId: request.id,
correlationId: request.correlationId,
message: 'Request size',
size: `${request.headers['content-length']} bytes`
});
}, 'Request size');
}
done();
});
Expand All @@ -108,32 +107,29 @@ server.addHook('onRequest', (request, reply, done) => {
request.log.error({
reqId: request.id,
correlationId: request.correlationId,
message: "Route not found",
url: sanitizedUrl,
method: request.method,
ip: request.ips
});
}, "Route not found");
reply.code(HTTP_STATUS_CODES.NOT_FOUND);
return reply.send({error: 'Not Found'});
} else if (!isAuthenticated(request)) {
request.log.warn({
reqId: request.id,
correlationId: request.correlationId,
message: "Unauthorized access attempt",
url: sanitizedUrl,
method: request.method,
ip: request.ips
});
}, "Unauthorized access attempt");
reply.code(HTTP_STATUS_CODES.UNAUTHORIZED);
return reply.send({error: 'Unauthorized'});
}
request.log.info({
reqId: request.id,
correlationId: request.correlationId,
message: "Request authenticated",
url: sanitizedUrl,
method: request.method
});
}, "Request authenticated");
done();
});

Expand All @@ -142,27 +138,24 @@ server.addHook('onRequest', (request, reply, done) => {
request.log.info({
reqId: request.id,
correlationId: request.correlationId,
message: 'Incoming request',
url: request.url,
method: request.method,
ip: request.ips
});
}, 'Incoming request');
done();
});

// Response logging hook with correlation ID
server.addHook('onResponse', (request, reply, done) => {
const duration = Date.now() - request.startTime;
const logData = {
request.log.info({
reqId: request.id,
correlationId: request.correlationId,
message: 'Request completed',
url: request.url,
method: request.method,
statusCode: reply.statusCode,
duration: `${duration}ms`
};
request.log.info(logData);
}, 'Request completed');
done();
});

Expand Down Expand Up @@ -208,7 +201,7 @@ server.get('/health', async (request, reply) => {

reply.send(health);
} catch (error) {
request.log.error({message: 'Health check failed', error});
request.log.error(error, 'Health check failed');
reply.code(503).send({
status: 'ERROR',
timestamp: new Date().toISOString(),
Expand Down
37 changes: 1 addition & 36 deletions src/utils/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,6 @@ import {pino} from 'pino';
import {ecsFormat} from '@elastic/ecs-pino-format';
import {getStage} from './configs.js';

/**
* Creates a logger instance based on the stage configuration
* - Development: Pretty formatted, colorized logs
* - Production/Staging: ECS JSON format for Elasticsearch
* @returns {pino.Logger} Configured Pino logger instance
*/
export function createLogger() {
// This sets NODE_ENV internally based on app.json stage
getStage();

const isDevelopment = process.env.NODE_ENV === 'development';

if (isDevelopment) {
// Pretty logs for development
return pino({
messageKey: 'message',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
singleLine: false,
messageKey: 'message'
}
}
});
}

// ECS format for production/staging
return pino(ecsFormat());
}

/**
* Creates a Fastify logger configuration based on the stage
* - Development: Pretty formatted, colorized logs
Expand All @@ -55,15 +22,13 @@ export function createFastifyLogger() {
if (isDevelopment) {
// Pretty logs for development
return {
messageKey: 'message',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'HH:MM:ss',
ignore: 'pid,hostname',
singleLine: false,
messageKey: 'message'
singleLine: false
}
}
};
Expand Down
35 changes: 5 additions & 30 deletions test/unit/utils/logger-test.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*global describe, it, beforeEach, afterEach*/
import * as chai from 'chai';
import {createLogger, createFastifyLogger} from "../../../src/utils/logger.js";
import {createFastifyLogger} from "../../../src/utils/logger.js";

let expect = chai.expect;

Expand Down Expand Up @@ -32,42 +32,16 @@ describe('unit Tests for logger', function () {
}
});

describe('createLogger', function () {
it('should create development logger with pino-pretty', function () {
delete process.env.NODE_ENV;
const logger = createLogger();

expect(logger).to.exist;
expect(typeof logger.info).to.equal('function');
expect(typeof logger.error).to.equal('function');
expect(typeof logger.warn).to.equal('function');
expect(typeof logger.debug).to.equal('function');
});

it('should create production logger with ECS format', function () {
process.env.NODE_ENV = 'production';
const logger = createLogger();

expect(logger).to.exist;
expect(typeof logger.info).to.equal('function');
expect(typeof logger.error).to.equal('function');
expect(typeof logger.warn).to.equal('function');
expect(typeof logger.debug).to.equal('function');
});
});

describe('createFastifyLogger', function () {
it('should create development Fastify logger config with pino-pretty', function () {
delete process.env.NODE_ENV;
const loggerConfig = createFastifyLogger();

expect(loggerConfig).to.exist;
expect(loggerConfig.messageKey).to.equal('message');
expect(loggerConfig.transport).to.exist;
expect(loggerConfig.transport.target).to.equal('pino-pretty');
expect(loggerConfig.transport.options).to.exist;
expect(loggerConfig.transport.options.colorize).to.equal(true);
expect(loggerConfig.transport.options.messageKey).to.equal('message');
});

it('should create production Fastify logger config with ECS format', function () {
Expand All @@ -88,12 +62,13 @@ describe('unit Tests for logger', function () {
});

describe('logger message field consistency', function () {
it('development logger should use message field', function () {
it('development logger should use standard Pino API', function () {
delete process.env.NODE_ENV;
const loggerConfig = createFastifyLogger();

expect(loggerConfig.messageKey).to.equal('message');
expect(loggerConfig.transport.options.messageKey).to.equal('message');
// Standard Pino API - no messageKey override needed
expect(loggerConfig.transport).to.exist;
expect(loggerConfig.transport.target).to.equal('pino-pretty');
});

it('should maintain ECS compliance in production', function () {
Expand Down
Loading