Skip to content

Commit

Permalink
log enhancement and fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Thierry DEGREMONT committed Aug 2, 2024
1 parent 6af9ad6 commit 242180c
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 195 deletions.
51 changes: 0 additions & 51 deletions packages/graphql-mesh/custom-plugins/fetch.ts

This file was deleted.

23 changes: 11 additions & 12 deletions packages/graphql-mesh/custom-plugins/monitor-envelop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { GraphQLError } from 'graphql';
* monitor plugin in order to get event contextual log and add some security rules
* useful to
* - log the graphql Query event
* - add desabled introspection alidation rule
* - remove suggestion
* - add desabled introspection validation rule
* - remove suggestion message
* - log the execute result summary with executes duration
* - remove not allowed introspection in result
*/
Expand All @@ -31,11 +31,10 @@ export default ({ options }): Plugin => {
const isMaskSuggestion = options?.maskSuggestion?.enabled || false
const maskSuggestionMessage = options?.maskSuggestion?.message || ""
return {
onParse({ params, context }) {
Logger.graphqlQuery(context['request']['headers'], context['params'])
/*return ({ result, context, replaceParseResult }) => {
Logger.info('LOG', 'onParse', 'result', result)
}*/
onParse({ context }) {
if (options.logOnParse) {
Logger.graphqlQuery(context['request']['headers'], context['params'])
}
},

onValidate: ({ addValidationRule, context }) => {
Expand Down Expand Up @@ -80,13 +79,12 @@ export default ({ options }): Plugin => {

return function onValidateEnd({ valid, result, setResult }) {
if (isMaskSuggestion && !valid) {
setResult(result.map((error) => formatter(error, maskSuggestionMessage)));
setResult(result.map((error: GraphQLError) => formatter(error, maskSuggestionMessage)));
}
};
},

onExecute({ args, extendContext }) {

onExecute(/*{ args, extendContext }*/) {
let timestampDebut = new Date().getTime()
return {
before() {
Expand All @@ -95,14 +93,15 @@ export default ({ options }): Plugin => {
},
onExecuteDone({ result, args }) {
const timestampDone = new Date().getTime();

// short cut to desabled introspection response in case of bad configuration rule
if (!allowIntrospection && args.contextValue['params'].query.includes('__schema')) {
result['data'] = {}
result['errors'] = [{ message: "Fordidden" }]
Logger.error('SECU', 'onExecute', 'Introspection query deteted not allowed', args.contextValue['params'])
}
Logger.endExec(args.contextValue['request']['headers'], result, timestampDone - timestampDebut, resultLogInfoLevel)
if (options.loOnExecuteDone) {
Logger.endExec(args.contextValue['request']['headers'], result, timestampDone - timestampDebut, resultLogInfoLevel)
}
}
}
}
Expand Down
98 changes: 40 additions & 58 deletions packages/graphql-mesh/custom-plugins/monitor-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type Plugin } from '@envelop/core';

import { Logger } from '../utils/logger'

/**@
/**
* monitor fetch source
* use to :
* - add log event for each fetch like, url, response status, duration
Expand All @@ -11,73 +11,55 @@ import { Logger } from '../utils/logger'
export default ({ options }) => {
return <Plugin>{
onFetch({ context, info }) {

if (!info) {
Logger.warn("noFeychInfo","onFetch","no info in on fetch")
Logger.warn("noFeychInfo", "onFetch", "no info in on fetch")
return;
}
const start = Date.now();
let rawSource = context[info.sourceName]
let description = info.parentType._fields[info.path.key].description

return (fetch: any) => {
const duration = Date.now() - start;
let fetchInfo = {}
let httpStatus=null
let url=null
if (options.fullFetchInfo) {
fetchInfo = {
fieldName: info.fieldName,
sourceName: info.sourceName,
pathKey: info.path.key,
operation: info.operation.name,
variables: info.variables,
endpoint: rawSource.rawSource.handler.config.endpoint,
description: description
}
} else {
fetchInfo = {
fieldName: info.fieldName,
pathKey: info.path.key,
operation: info.operation.name,
variables: info.variableValues,
endpoint: rawSource.rawSource.handler.config.endpoint,
if (options.logOnFetch) {
const duration = Date.now() - start;
let fetchInfo = {}
let httpStatus = null
let url = null
if (options.fullFetchInfo) {
fetchInfo = {
fieldName: info.fieldName,
sourceName: info.sourceName,
pathKey: info.path.key,
operation: info.operation.name,
variables: info.variables,
endpoint: rawSource.rawSource.handler.config.endpoint,
description: description
}
} else {
fetchInfo = {
fieldName: info.fieldName,
pathKey: info.path.key,
operation: info.operation.name,
variables: info.variableValues,
endpoint: rawSource.rawSource.handler.config.endpoint,
}
}
}
//const fetchResponseInfo = {}
if (fetch.response) {
//const fetchResponseInfo = {}
if (fetch.response) {

httpStatus=fetch.response.status
url=fetch.response.url
const options = fetch.response.options
if (options) {
fetchInfo['options'] = {
requestId: options.headers['x-request-id'],
server: options.headers['server']
httpStatus = fetch.response.status
url = fetch.response.url
const options = fetch.response.options
if (options) {
fetchInfo['options'] = {
requestId: options.headers['x-request-id'],
server: options.headers['server']
}
}
}
Logger.onFetch(context.request, url, httpStatus, duration, fetchInfo)
}
Logger.onFetch(context.request, url, httpStatus,duration,fetchInfo)
};
},

onExecute() {
return {
onExecuteDone({ args }) {
// @ts-ignore
const { timings } = args.contextValue;
if (!timings) {
return;
}

// @ts-ignore
args.contextValue.res.setHeader?.(
'Server-Timing',
timings.join(', ')
);
},
};
},

};
};
}
}
}
}
62 changes: 45 additions & 17 deletions packages/graphql-mesh/custom-plugins/monitor-yoga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,63 @@ import { v4 as uuidv4 } from 'uuid'
* - log response sumary event
* - remove a eventualy not allowed instropection data in result
*/

export function useYagaMonitoring({ options }): Plugin {
const isMaskErrors = options?.maskError?.enabled || process.env['MASK_ERRORS'] || false
// filter in production anyway
// filter error in production anyway
const isFilterError = options?.filterError?.enabled || process.env['FILTER_ERRORS'] == 'true' || process.env['IS_PROUCTION_ENV'] == 'true' || false

const errorMaskMessage = options?.maskError?.message ? options.maskError.message : "something goes wrong"
const responseLogInfoLevel = options?.responseLogInfoLevel ? options.responseLogInfoLevel : "low"
const resultLogInfoLevel = options?.resultLogInfoLevel ? options.resultLogInfoLevel : "low"


const resultLogInfoLevel = options?.resultLogInfoLevel ? options.resultLogInfoLevel : "medium"

return {
onRequest({ request/*, fetchAPI, endResponse */ }) {
Logger.onRequest(request)
if (options.LogOnRequest) {
// log only graphql request, avoid log other request like metric requests
if (request.url.includes("/graphql")) {
Logger.onRequest(request)
}
}

// add resuestTimestamp in headers
const timestamp = new Date().getTime();
request.headers.append("requestTimestamp", String(timestamp))

// add x-request-id in header if not present
if (!request.headers.get('x-request-id')){
if (!request.headers.get('x-request-id')) {
request.headers.append("x-request-id", uuidv4())
}

},
onRequestParse(args) {
const beforeTimestamp = new Date().getTime();
let requestHeaders = args.request.headers
return {
onRequestParseDone(nRequestParseDoneEventPayload) {
const timestamp = new Date().getTime();
Logger.onRequestParseDone(requestHeaders, nRequestParseDoneEventPayload.requestParserResult['query'], nRequestParseDoneEventPayload.requestParserResult['operationName'], nRequestParseDoneEventPayload.requestParserResult['variables'], timestamp - beforeTimestamp)
if (options.logOnRequestParseDone) {
Logger.onRequestParseDone(requestHeaders, nRequestParseDoneEventPayload.requestParserResult['query'], nRequestParseDoneEventPayload.requestParserResult['operationName'], nRequestParseDoneEventPayload.requestParserResult['variables'], timestamp - beforeTimestamp)
}
if (nRequestParseDoneEventPayload.requestParserResult['query'].includes('__schema')) {
Logger.introspection("onRequestParseDone", requestHeaders,nRequestParseDoneEventPayload.requestParserResult['query'])
Logger.introspection("onRequestParseDone", requestHeaders, nRequestParseDoneEventPayload.requestParserResult['query'])
}
}
}
},
onResultProcess(args) {
Logger.onResultProcess(args.request, args.result, resultLogInfoLevel)
if (options.logOnResultProcess) {
// calculate duration from request timestamp
let requestTimestamp: number = 0
if (args.request['headers']) {
const requestTimestampString = args.request['headers'].get('requesttimestamp')
if (requestTimestampString) {
requestTimestamp = parseInt(requestTimestampString)
}
}
const responseTimestamp = new Date().getTime();
Logger.onResultProcess(args.request, args.result, requestTimestamp > 0 ? responseTimestamp - requestTimestamp : 0, resultLogInfoLevel)
}
// if we want to replace all message with a generic message
if (isMaskErrors) {
if (args.result['errors']) {
let errors = args.result['errors']
Expand All @@ -60,12 +77,12 @@ export function useYagaMonitoring({ options }): Plugin {
}
}
} else {
// if we want to filter error to only return the message, don't return extend information like stacktrace
if (isFilterError) {
if (args.result['errors']) {
let errors = args.result['errors']

for (let i = 0; i < errors.length; i++) {
errors[i]= new GraphQLError(filter(errors[i]['message']))
errors[i] = new GraphQLError(filterErrorMessage(errors[i]['message']))
}

}
Expand All @@ -75,14 +92,25 @@ export function useYagaMonitoring({ options }): Plugin {
},

onResponse({ request, response }) {
if (request.method != 'OPTION') {
Logger.onResponse(request, response, responseLogInfoLevel)
// dont log options http
if (request.method != 'OPTIONS') {
if (options.logOnResponse) {
// only log graphql request don't log metrics or other requests
if (request.url.includes("/graphql")) {
Logger.onResponse(request, response, responseLogInfoLevel)
}
}
}
}
}
}

function filter(message: string) {
/** filterErrorMessage
* use to filter error message :
* - remove disabled introspection
* todo: add other filter rules to remove non expecting message
*/
function filterErrorMessage(message: string) {
if (message.includes("introspection has been disabled")) {
return "forbidden"
}
Expand Down
Loading

0 comments on commit 242180c

Please sign in to comment.