-
Notifications
You must be signed in to change notification settings - Fork 1
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: logging wrapper tests #157
Merged
SamSalvatico
merged 13 commits into
feature/configure-logging-wrapper
from
feature/logging-wrapper-tests
Apr 29, 2024
Merged
Changes from 9 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
c8c3693
feature(logging-wrapper): added tests helper
SamSalvatico 2667404
Merge branch 'feature/configure-logging-wrapper' into feature/logging…
SamSalvatico 79479bb
Added README for logging-wrapper
SamSalvatico febd775
Merge branch 'feature/configure-logging-wrapper' into feature/logging…
SamSalvatico ade033c
Added tap
SamSalvatico 974039d
Added gitignore
SamSalvatico eb5959a
Tap config
SamSalvatico c30059c
Added tests for logging-wrapper.test.ts
SamSalvatico 4294491
Added fastify tests
SamSalvatico 322141b
Merge branch 'feature/configure-logging-wrapper' into feature/logging…
SamSalvatico 26fb262
Merge branch 'feature/configure-logging-wrapper' into feature/logging…
SamSalvatico 7196595
Merge branch 'feature/configure-logging-wrapper' into feature/logging…
SamSalvatico 5488ebd
Chore: Use logging-wrapper in payments-api (#160)
SamSalvatico File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,26 @@ | ||
{ | ||
"parser": "@typescript-eslint/parser", | ||
"extends": [ | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:prettier/recommended", | ||
"eslint:recommended" | ||
], | ||
"parserOptions": { | ||
"ecmaVersion": 2020, | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
}, | ||
"env": { | ||
"node": true | ||
} | ||
"parser": "@typescript-eslint/parser", | ||
"extends": [ | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:prettier/recommended", | ||
"eslint:recommended" | ||
], | ||
"parserOptions": { | ||
"ecmaVersion": 2020, | ||
"sourceType": "module" | ||
}, | ||
"rules": { | ||
"no-unused-vars": "off", | ||
"@typescript-eslint/no-unused-vars": [ | ||
"warn", | ||
{ | ||
"argsIgnorePattern": "^_", | ||
"varsIgnorePattern": "^_", | ||
"caughtErrorsIgnorePattern": "^_" | ||
} | ||
] | ||
}, | ||
"env": { | ||
"node": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
.tap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Logging Wrapper | ||
|
||
This logging wrapper goal is to standardize the records written by our services. | ||
|
||
## How to | ||
|
||
To use this package two steps are needed: | ||
- use the `getLoggingConfiguration()` method to get the configuration for the `fastify` server. | ||
``` | ||
const server = fastify({ | ||
...getLoggingConfiguration() | ||
}); | ||
``` | ||
- after the server is initialized, invoke the `initializeLoggingHooks(server)` to setup the needed `fastify` hooks | ||
``` | ||
initializeLoggingHooks(server); | ||
``` | ||
|
||
That's it! Just log as you usually do! | ||
|
||
## Default records | ||
|
||
We will have 3 mandatory log entries that will be written for each request the service manages. | ||
|
||
Those 3 records are: | ||
- **New Request**, written when a request is received | ||
``` | ||
{"level":30,"level_name":"INFO","hostname":"hostname","request_id":"q9Y6NwwbRimle4TxcXRPkQ-0000000000","timestamp":1713868947766,"request":{"scheme":"http","method":"GET","path":"/ping","hostname":"localhost:80","query_params":{},"headers":{"user-agent":"lightMyRequest","host":"localhost:80"},"client_ip":"127.0.0.1","user_agent":"lightMyRequest"},"message":"NEW_REQUEST"} | ||
``` | ||
- **Response**, containing most of the response data | ||
``` | ||
{"level":30,"level_name":"INFO","hostname":"hostname","request_id":"q9Y6NwwbRimle4TxcXRPkQ-0000000000","timestamp":1713868947769,"request":{"scheme":"http","method":"GET","path":"/ping","hostname":"localhost:80","query_params":{}},"response":{"status_code":200,"headers":{"content-type":"application/json; charset=utf-8","content-length":"17"}},"message":"RESPONSE"} | ||
``` | ||
- **API Track**, it contains data about the lifecycle of the request, including errors, if any | ||
``` | ||
{"level":30,"level_name":"INFO","hostname":"hostname","request_id":"5c_RLAnSS4y9-Q5STsJyiQ-0000000008","timestamp":1713869128434,"request":{"scheme":"http","method":"GET","path":"/this-path-must-not-exist","hostname":"localhost:80","query_params":{"status_code":"404","error_message":"Not Found"}},"response":{"status_code":404,"headers":{"content-type":"application/json; charset=utf-8","content-length":"107"}},"error":{"class":"REQUEST_ERROR","message":"Not Found","code":"FST_ERR_NOT_FOUND"},"message":"API_TRACK"} | ||
``` | ||
|
||
### Error record | ||
|
||
If an error is thrown, a log entry is automatically written. | ||
|
||
``` | ||
{"level":50,"level_name":"ERROR","hostname":"hostname","request_id":"1kPptKhMSeyZ9OwcSwBxhg-0000000008","timestamp":1713869258238,"request":{"scheme":"http","method":"GET","path":"/this-path-must-not-exist","hostname":"localhost:80","query_params":{"status_code":"404","error_message":"Not Found"}},"error":{"class":"REQUEST_ERROR","message":"Not Found","trace":"FastifyError: Not Found..... The whole trace here","code":"FST_ERR_NOT_FOUND"},"message":"ERROR"} | ||
``` | ||
|
||
## Additional entries | ||
|
||
Additional log entries can be added as needed, but they will include, thanks to this package, common info about the request context that will be useful for future troubleshooting. |
80 changes: 80 additions & 0 deletions
80
packages/logging-wrapper/__tests__/fastify-logging-wrapper.errors.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { LogErrorClasses } from "../src/logging-wrapper-entities"; | ||
import { t } from "tap"; | ||
import { initializeServer, runErrorTest } from "./helpers/fastify-test-helpers"; | ||
|
||
t.test("Error data are correctly set", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
await runErrorTest({ | ||
server, | ||
loggingDestination, | ||
inputStatusCode: "500", | ||
expectedStatusCode: 500, | ||
errorMessage: "WHoooopS!", | ||
expectedClass: LogErrorClasses.ServerError, | ||
}); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("Unknown Error route logs expected values", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
await runErrorTest({ | ||
server, | ||
loggingDestination, | ||
inputStatusCode: "399", | ||
expectedStatusCode: 500, | ||
errorMessage: "Unknown!", | ||
expectedClass: LogErrorClasses.UnknownError, | ||
}); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("400 Error route logs expected values", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
await runErrorTest({ | ||
server, | ||
loggingDestination, | ||
inputStatusCode: "400", | ||
expectedStatusCode: 400, | ||
errorMessage: "Bad request!", | ||
expectedClass: LogErrorClasses.RequestError, | ||
}); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("422 Validation Error route logs expected values", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
await runErrorTest({ | ||
server, | ||
loggingDestination, | ||
inputStatusCode: "422", | ||
expectedStatusCode: 422, | ||
errorMessage: "Bad request!", | ||
expectedClass: LogErrorClasses.ValidationError, | ||
}); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("Not found route logs expected values", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
await runErrorTest({ | ||
server, | ||
loggingDestination, | ||
inputStatusCode: "404", | ||
expectedStatusCode: 404, | ||
errorMessage: "Not Found", | ||
expectedClass: LogErrorClasses.RequestError, | ||
path: "/this-path-must-not-exist", | ||
expectedFastifyCode: "FST_ERR_NOT_FOUND", | ||
}); | ||
|
||
t.end(); | ||
}); |
123 changes: 123 additions & 0 deletions
123
packages/logging-wrapper/__tests__/fastify-logging-wrapper.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { LogMessages } from "../src/logging-wrapper-entities"; | ||
import { t } from "tap"; | ||
import { | ||
initializeServer, | ||
DEFAULT_METHOD, | ||
DEFAULT_PATH, | ||
checkExpectedRequestEntry, | ||
checkExpectedResponseEntry, | ||
parseLogEntry, | ||
checkGenericEntryFields, | ||
} from "./helpers/fastify-test-helpers"; | ||
|
||
t.test( | ||
"Logging entries when all works fine are the expected ones", | ||
async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
|
||
const response = await server.inject({ | ||
method: DEFAULT_METHOD, | ||
url: DEFAULT_PATH, | ||
}); | ||
t.ok(typeof response !== "undefined"); | ||
t.equal(response?.statusCode, 200); | ||
const loggedRecords = loggingDestination.getLoggedRecords(); | ||
t.equal(loggedRecords.length, 3); | ||
checkExpectedRequestEntry({ | ||
requestLogEntry: loggedRecords[0], | ||
}); | ||
checkExpectedResponseEntry({ | ||
responseLogEntry: loggedRecords[1], | ||
responseStatusCode: 200, | ||
}); | ||
checkExpectedResponseEntry({ | ||
responseLogEntry: loggedRecords[2], | ||
responseStatusCode: 200, | ||
expectedMessage: LogMessages.ApiTrack, | ||
}); | ||
|
||
t.end(); | ||
}, | ||
); | ||
|
||
t.test("Request id is not overriden by header", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
const customRequestId = "Another request id"; | ||
const response = await server.inject({ | ||
method: DEFAULT_METHOD, | ||
url: DEFAULT_PATH, | ||
headers: { "request-id": customRequestId }, | ||
}); | ||
t.ok(typeof response !== "undefined"); | ||
t.equal(response?.statusCode, 200); | ||
const logged = loggingDestination.getLoggedRecords(); | ||
checkExpectedRequestEntry({ | ||
requestLogEntry: logged[0], | ||
inputHeaders: { "request-id": customRequestId }, | ||
}); | ||
const parsedEntry = parseLogEntry(logged[0]); | ||
t.notSame(parsedEntry.request_id, customRequestId); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("Logging context is reset between requests", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
|
||
let response = await server.inject({ | ||
method: DEFAULT_METHOD, | ||
url: DEFAULT_PATH, | ||
}); | ||
|
||
t.ok(typeof response !== "undefined"); | ||
t.equal(response?.statusCode, 200); | ||
let loggedRecords = loggingDestination.getLoggedRecords(); | ||
t.equal(loggedRecords.length, 3); | ||
let parsedResponse = parseLogEntry(loggedRecords[1]); | ||
t.ok(typeof parsedResponse.response !== "undefined"); | ||
|
||
response = await server.inject({ | ||
method: DEFAULT_METHOD, | ||
url: DEFAULT_PATH, | ||
}); | ||
t.ok(typeof response !== "undefined"); | ||
t.equal(response?.statusCode, 200); | ||
loggedRecords = loggingDestination.getLoggedRecords(); | ||
t.equal(loggedRecords.length, 6); | ||
// 3 is the New Request for 2nd call | ||
parsedResponse = parseLogEntry(loggedRecords[3]); | ||
// if undefined it means that the logging context | ||
// has been reset between requests | ||
t.ok(typeof parsedResponse.response === "undefined"); | ||
|
||
t.end(); | ||
}); | ||
|
||
t.test("Additional logs are correctly written", async () => { | ||
const { server, loggingDestination } = initializeServer(); | ||
t.teardown(() => server.close()); | ||
const logMessage = "Testing additional logs"; | ||
|
||
const response = await server.inject({ | ||
method: "POST", | ||
url: "/logs", | ||
body: { log_entry: logMessage }, | ||
}); | ||
|
||
t.ok(typeof response !== "undefined"); | ||
t.equal(response?.statusCode, 200); | ||
const loggedRecords = loggingDestination.getLoggedRecords(); | ||
t.equal(loggedRecords.length, 4); | ||
const parsedAdditional = parseLogEntry(loggedRecords[1]); | ||
checkGenericEntryFields({ | ||
parsedEntry: parsedAdditional, | ||
expectedLevelName: "INFO", | ||
expectedMessage: logMessage, | ||
}); | ||
t.ok(typeof parsedAdditional.request !== "undefined"); | ||
|
||
t.end(); | ||
}); |
42 changes: 42 additions & 0 deletions
42
packages/logging-wrapper/__tests__/helpers/build-fastify.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Fastify from "fastify"; | ||
import { | ||
getLoggingConfiguration, | ||
initializeLoggingHooks, | ||
} from "../../src/fastify-logging-wrapper"; | ||
import { DestinationStream } from "pino"; | ||
import { createError } from "@fastify/error"; | ||
|
||
export const buildFastify = (loggerDestination?: DestinationStream) => { | ||
const server = Fastify({ | ||
...getLoggingConfiguration(loggerDestination), | ||
}); | ||
|
||
initializeLoggingHooks(server); | ||
|
||
server.get("/ping", async (_request, _reply) => { | ||
return { data: "pong\n" }; | ||
}); | ||
|
||
server.get("/error", async (request, _reply) => { | ||
const parsed = request.query as { [x: string]: unknown }; | ||
const requestedStatusCode = Number(parsed["status_code"] ?? "500"); | ||
const requestedMessage = parsed["error_message"] ?? "WHOOOPS"; | ||
|
||
throw createError( | ||
"CUSTOM_CODE", | ||
requestedMessage as string, | ||
requestedStatusCode as number, | ||
)(); | ||
}); | ||
|
||
server.post("/logs", async (request, _reply) => { | ||
const body = request.body as { [x: string]: unknown }; | ||
const logMessage = body.log_entry ?? "Default additional message"; | ||
|
||
request.log.info(logMessage); | ||
|
||
return { data: { message: logMessage } }; | ||
}); | ||
|
||
return server; | ||
}; |
28 changes: 28 additions & 0 deletions
28
packages/logging-wrapper/__tests__/helpers/build-logger.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { PinoLoggerOptions } from "fastify/types/logger"; | ||
import { pino } from "pino"; | ||
|
||
export const buildLogger = (loggerConfiguration: PinoLoggerOptions) => { | ||
const { loggerDestination, getLoggedRecords } = getTestingDestinationLogger(); | ||
|
||
return { | ||
logger: pino(loggerConfiguration, loggerDestination), | ||
loggedRecordsMethod: getLoggedRecords, | ||
}; | ||
}; | ||
|
||
export interface TestingLoggerDestination { | ||
loggerDestination: { | ||
write: (_data: string) => number; | ||
}; | ||
getLoggedRecords: () => string[]; | ||
} | ||
|
||
export const getTestingDestinationLogger = (): TestingLoggerDestination => { | ||
const testCaseRecords: string[] = []; | ||
const getLoggedRecords = () => testCaseRecords; | ||
const loggerDestination = { | ||
write: (data: string) => testCaseRecords.push(data), | ||
}; | ||
|
||
return { loggerDestination, getLoggedRecords }; | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've splitted the
fastify-logging-wrapper
tests in 2 files to make them run parallel and lower latency