Skip to content
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

health check updates #85

Merged
merged 7 commits into from
Oct 13, 2024
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
8 changes: 5 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PORT=3000
LOG_LEVEL=debug
ALL_SCHEMA_ON=true
ENVIRONMENT= # Set to name of environment

# Mock server
PORT_MOCK=3100
Expand Down Expand Up @@ -42,9 +43,10 @@ RP_INTERNAL_APIM_ACCESS_TOKEN_URL=http://127.0.0.1:3100/apim/
RP_INTERNAL_APIM_URL=http://127.0.0.1:3100/v1/

# Health checks
HEALTH_CHECK_ENABLED=true # Enable health checks
RP_INTERNAL_HEALTH_CHECK_ORGANISATION_ID=some-org-id # Organisation ID for health check
ENTRA_HEALTH_CHECK_USER_OBJECT_ID=some-user-object-id # User object ID for health check

HEALTH_CHECK_AUTHENTICATE_DATABASE_THROTTLE_TIME_MS= # time in ms
HEALTH_CHECK_RURAL_PAYMENTS_APIM_THROTTLE_TIME_MS= # time in ms
HEALTH_CHECK_ENTRA_THROTTLE_TIME_MS= # time in ms
HEALTH_CHECK_AUTHENTICATE_DATABASE_THROTTLE_TIME_MS=300000 # Only check authenticate database every 5 miutes
HEALTH_CHECK_RURAL_PAYMENTS_APIM_THROTTLE_TIME_MS=300000 # Only check rural payments and apim every 5 miutes
HEALTH_CHECK_ENTRA_THROTTLE_TIME_MS=300000 # Only check entra every 5 miutes
12 changes: 10 additions & 2 deletions app/data-sources/rural-payments/RuralPayments.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,11 @@ export class RuralPayments extends RESTDataSource {
this.logger.http('#datasource - Rural payments - response', {
code: RURALPAYMENTS_API_REQUEST_001,
requestTimeMs,
request: { ...request, path },
request: {
method: request.method.toUpperCase(),
headers: request.headers,
path
},
response: { statusCode: request.response?.status }
})
this.logger.debug('#datasource - Rural payments - response detail', {
Expand All @@ -242,7 +246,11 @@ export class RuralPayments extends RESTDataSource {
this.logger.http('#datasource - apim - response', {
code: APIM_APIM_REQUEST_001,
requestTimeMs,
request: { ...request, path },
request: {
method: request.method.toUpperCase(),
headers: request.headers,
path
},
response: { status: result.response?.status }
})
this.logger.debug('#datasource - apim - response detail', {
Expand Down
10 changes: 5 additions & 5 deletions app/data-sources/rural-payments/RuralPaymentsBusiness.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { NotFound } from '../../errors/graphql.js'
import { RURALPAYMENTS_API_NOT_FOUND_001 } from '../../logger/codes.js'
import { sampleResponse } from '../../logger/utils.js'
import { RuralPayments } from './RuralPayments.js'

export class RuralPaymentsBusiness extends RuralPayments {
Expand Down Expand Up @@ -39,9 +38,10 @@ export class RuralPaymentsBusiness extends RuralPayments {
throw new NotFound('Rural payments organisation not found')
}

this.logger.silly('Organisation by SBI', { response: { body: organisationResponse } })

const response = organisationResponse?._data?.pop() || {}

this.logger.silly('Organisation by SBI', { response: sampleResponse(response) })
return response?.id ? this.getOrganisationById(response.id) : null
}

Expand All @@ -51,7 +51,7 @@ export class RuralPaymentsBusiness extends RuralPayments {
const response = await this.get(
`authorisation/organisation/${organisationId}`
)
this.logger.silly('Organisation customers by organisation ID', { response: sampleResponse(response) })
this.logger.silly('Organisation customers by organisation ID', { response: { body: response } })
return response._data
}

Expand Down Expand Up @@ -89,7 +89,7 @@ export class RuralPaymentsBusiness extends RuralPayments {
`SitiAgriApi/cph/organisation/${organisationId}/cph-numbers`
)

this.logger.silly('Organisation CPH collection by organisation ID', { response: sampleResponse(response) })
this.logger.silly('Organisation CPH collection by organisation ID', { response: { body: response } })
return response.data
}

Expand All @@ -105,7 +105,7 @@ export class RuralPaymentsBusiness extends RuralPayments {
)}`
)

this.logger.silly('Organisation CPH info by organisation ID and CPH number', { response: sampleResponse(response) })
this.logger.silly('Organisation CPH info by organisation ID and CPH number', { response: { body: response } })

return response.data
}
Expand Down
12 changes: 6 additions & 6 deletions app/data-sources/rural-payments/RuralPaymentsCustomer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { NotFound } from '../../errors/graphql.js'
import { RURALPAYMENTS_API_NOT_FOUND_001 } from '../../logger/codes.js'
import { sampleResponse } from '../../logger/utils.js'
import { RuralPayments } from './RuralPayments.js'

export class RuralPaymentsCustomer extends RuralPayments {
Expand All @@ -20,11 +19,12 @@ export class RuralPaymentsCustomer extends RuralPayments {
'Content-Type': 'application/json'
}
})
this.logger.silly('Customer by CRN response', { response: { body: customerResponse } })

const response = customerResponse._data.pop() || {}
this.logger.silly('Customer by CRN response', { response: sampleResponse(response) })

if (!response?.id) {
this.logger.warn('#datasource - Rural payments - Customer not found for CRN', { crn, code: RURALPAYMENTS_API_NOT_FOUND_001 })
this.logger.warn('#datasource - Rural payments - Customer not found for CRN', { crn, code: RURALPAYMENTS_API_NOT_FOUND_001, response: { body: customerResponse } })
throw new NotFound('Rural payments customer not found')
}

Expand All @@ -37,11 +37,11 @@ export class RuralPaymentsCustomer extends RuralPayments {
const response = await this.get(`person/${personId}/summary`)

if (!response?._data?.id) {
this.logger.warn('#datasource - Rural payments - Customer not found for Person ID', { personId, code: RURALPAYMENTS_API_NOT_FOUND_001 })
this.logger.warn('#datasource - Rural payments - Customer not found for Person ID', { personId, code: RURALPAYMENTS_API_NOT_FOUND_001, response: { body: response } })
throw new NotFound('Rural payments customer not found')
}

this.logger.silly('Person by person ID response', { response: sampleResponse(response) })
this.logger.silly('Person by person ID response', { response: { body: response } })
return response._data
}

Expand All @@ -54,7 +54,7 @@ export class RuralPaymentsCustomer extends RuralPayments {
`organisation/person/${personId}/summary?search=&page-size=${process.env.VERSION_1_PAGE_SIZE || 100}`
)

this.logger.silly('Person businesses by person ID response', { response: sampleResponse(personBusinessSummaries) })
this.logger.silly('Person businesses by person ID response', { response: { body: personBusinessSummaries } })
return personBusinessSummaries._data
}

Expand Down
3 changes: 1 addition & 2 deletions app/graphql/resolvers/business/business.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NotFound } from '../../../errors/graphql.js'
import { RURALPAYMENTS_API_NOT_FOUND_001 } from '../../../logger/codes.js'
import { logger } from '../../../logger/logger.js'
import { sampleResponse } from '../../../logger/utils.js'
import { transformOrganisationCSApplicationToBusinessApplications } from '../../../transformers/rural-payments/applications-cs.js'
import { transformOrganisationCPH } from '../../../transformers/rural-payments/business-cph.js'
import {
Expand Down Expand Up @@ -32,7 +31,7 @@ export const Business = {
)
logger.silly('Got business customers', {
organisationId,
customers: sampleResponse(customers)
response: { body: customers }
})
return transformOrganisationCustomers(customers)
},
Expand Down
5 changes: 2 additions & 3 deletions app/graphql/resolvers/customer/customer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { logger } from '../../../logger/logger.js'
import { sampleResponse } from '../../../logger/utils.js'
import { transformAuthenticateQuestionsAnswers } from '../../../transformers/authenticate/question-answers.js'
import {
ruralPaymentsPortalCustomerTransformer,
Expand Down Expand Up @@ -35,7 +34,7 @@ export const Customer = {
sbi
)

logger.silly('Got customer business', { crn, personId, summary: sampleResponse(summary) })
logger.silly('Got customer business', { crn, personId, response: { body: summary } })
return transformPersonSummaryToCustomerAuthorisedFilteredBusiness(
{ personId, crn, sbi },
summary
Expand All @@ -49,7 +48,7 @@ export const Customer = {
personId
)

logger.silly('Got customer businesses', { crn, personId, summary: sampleResponse(summary) })
logger.silly('Got customer businesses', { crn, personId, response: { body: summary } })
return transformPersonSummaryToCustomerAuthorisedBusinesses(
{ personId, crn },
summary
Expand Down
1 change: 1 addition & 0 deletions app/logger/codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const DAL_APPLICATION_REQUEST_001 = 'DAL_APPLICATION_REQUEST_001'
export const DAL_APPLICATION_RESPONSE_001 = 'DAL_APPLICATION_RESPONSE_001'
export const DAL_UNHANDLED_ERROR_001 = 'DAL_UNHANDLED_ERROR_001'
export const DAL_RESOLVERS_BUSINESS_001 = 'DAL_RESOLVERS_BUSINESS_001'
export const DAL_HEALTH_CHECK_001 = 'DAL_HEALTH_CHECK_001'

export const RURALPAYMENTS_API_REQUEST_001 = 'RURALPAYMENTS_API_REQUEST_001'
export const RURALPAYMENTS_API_NOT_FOUND_001 = 'RURALPAYMENTS_API_NOT_FOUND_001'
Expand Down
19 changes: 11 additions & 8 deletions app/logger/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,37 @@
* error: // Used for errors that prevent the application from operating correctly
* warn: // Used for errors that do not prevent the application from operating correctly, but may cause issues
* info: // Application access logs
* http: // Third party response logs without body
* verbose: // Third party access logs
* debug: // Third party response logs
* debug: // Third party response logs with body
* silly: // Detailed function logs
*/

import { createLogger, format, transports } from 'winston'
import { jsonStringify } from './utils.js'
import { stackTraceFormatter, redactSensitiveData } from './winstonFormatters.js'
import { redactSensitiveData, sampleResponseBodyData } from './winstonFormatters.js'

const transportTypes = []
// If AppInsights is enabled, means we are running in Azure, format logs for AppInsights
if (process.env.APPINSIGHTS_CONNECTIONSTRING) {
transportTypes.push(
new transports.Console({
format: format.combine(redactSensitiveData, stackTraceFormatter, format.json())
format: format.combine(
sampleResponseBodyData(),
redactSensitiveData(),
format.json()
)
})
)
} else {
// if AppInsights is not enabled, send logs to console
transportTypes.push(new transports.Console({
format: format.combine(
sampleResponseBodyData(),
redactSensitiveData(),
format.align(),
format.colorize(),
redactSensitiveData,
stackTraceFormatter,
format.printf(info => {
return `${info.level}: ${info.message} ${jsonStringify(info)}`
})
format.printf(info => `${info.level}: ${info.message}${Object.keys(info).length > 2 ? `\n${jsonStringify(info)}` : ''}`)
)
}))
}
Expand Down
3 changes: 2 additions & 1 deletion app/logger/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export const sampleResponse = response => {
if (Array.isArray(response?._data)) {
return {
...response,
_data: sampleArray(response._data)
_data: sampleArray(response._data),
__sampled: true
}
}

Expand Down
48 changes: 19 additions & 29 deletions app/logger/winstonFormatters.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
import fastRedact from 'fast-redact'
import { format } from 'winston'
import { sampleResponse } from './utils.js'

const maxStackTrace = 50

Error.stackTraceFormatterLimit = Error.stackTraceFormatterLimit < maxStackTrace ? maxStackTrace : Error.stackTraceFormatterLimit
const serialize = info => {
const symbols = Object.getOwnPropertySymbols(info).reduce((symbols, symbol) => {
symbols[symbol] = info[symbol]
return symbols
}, {})
const infoCloned = structuredClone(info)
return {
...infoCloned,
...symbols
}
}

const redact = fastRedact({
paths: ['request.headers.authorization'],
serialize: false
paths: ['*.*.authorization', '*.*.Authorization'],
serialize
})

export const stackTraceFormatter = format.printf((info) => {
if (!info?.stack && !info?.error?.stack) {
try {
throw new Error()
} catch (e) {
info.stack = e.stack
.split('\n')
.filter(
line =>
!line.includes('node_modules') &&
!line.includes('logger.js') &&
!line.includes('winstonFormatters.js') &&
!line.includes('node:')
)
info.stack.shift()
if (!info?.stack?.length) {
delete info.stack
}
}
}
export const redactSensitiveData = format(redact)

export const sampleResponseBodyData = format(info => {
if (info?.response?.body) {
info.response.body = sampleResponse(info.response.body)
}
return info
})

export const redactSensitiveData = format.printf(info => {
return redact(JSON.parse(JSON.stringify(info)))
})
Loading