From 7b38786ee65333d3c184470a8a736bbd3a1b0a8e Mon Sep 17 00:00:00 2001 From: Enzo Mercanti Date: Thu, 11 Jul 2024 16:03:11 -0300 Subject: [PATCH] fix: add with session metric --- CHANGELOG.md | 3 +++ node/directives/withSession.ts | 31 ++++++++++++++++++++++- node/metrics/session.ts | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 node/metrics/session.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index af8091f..0c74794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added +- Session audit metrics + ## [1.41.0] - 2024-07-01 ### Added diff --git a/node/directives/withSession.ts b/node/directives/withSession.ts index dda5c27..327ac70 100644 --- a/node/directives/withSession.ts +++ b/node/directives/withSession.ts @@ -4,13 +4,15 @@ import type { GraphQLField } from 'graphql' import { defaultFieldResolver } from 'graphql' import { SchemaDirectiveVisitor } from 'graphql-tools' +import sendSessionMetric, { SessionMetric } from '../metrics/session' + export class WithSession extends SchemaDirectiveVisitor { public visitFieldDefinition(field: GraphQLField) { const { resolve = defaultFieldResolver } = field field.resolve = async (root: any, args: any, context: any, info: any) => { const { - clients: { session }, + clients: { session, logger }, } = context const token = @@ -23,6 +25,33 @@ export class WithSession extends SchemaDirectiveVisitor { }) .catch(() => null) + // we emit a metric for cases where the sessionData is null + // so we can identify use cases that are supposed to have sessionData + // but do not have it. We currently have a high volume of logs generated + // by such cases, so we need to identify and fix them. + const operation = field.astNode?.name?.value ?? context.request.url + const userAgent = context?.request?.headers['user-agent'] as string + const caller = context?.request?.headers['x-vtex-caller'] as string + const forwardedHost = context?.request?.headers[ + 'x-forwarded-host' + ] as string + + const hasSessionToken = !!context.vtex.sessionToken + const hasSessionTokenOnHeader = !!context.request.header?.sessiontoken + const hasSessionData = !!context.vtex.sessionData + + const auditMetric = new SessionMetric(context?.vtex?.account, { + operation, + forwardedHost, + caller, + userAgent, + hasSessionToken, + hasSessionTokenOnHeader, + hasSessionData, + }) + + sendSessionMetric(logger, auditMetric) + return resolve(root, args, context, info) } } diff --git a/node/metrics/session.ts b/node/metrics/session.ts new file mode 100644 index 0000000..4e2ebe2 --- /dev/null +++ b/node/metrics/session.ts @@ -0,0 +1,46 @@ +import type { Logger } from '@vtex/api/lib/service/logger/logger' + +import type { Metric } from '../clients/metrics' +import { B2B_METRIC_NAME, sendMetric } from '../clients/metrics' + +export interface SessionAuditMetric { + operation: string + forwardedHost: string + caller: string + userAgent: string + hasSessionToken: boolean + hasSessionTokenOnHeader: boolean + hasSessionData: boolean +} + +export class SessionMetric implements Metric { + public readonly description: string + public readonly kind: string + public readonly account: string + public readonly fields: SessionAuditMetric + public readonly name = B2B_METRIC_NAME + public error?: string + + constructor(account: string, fields: SessionAuditMetric) { + this.account = account + this.fields = fields + this.kind = 'b2b-storefront-permissions-session-event' + this.description = 'Session metric event' + } +} + +const sendSessionMetric = async ( + logger: Logger, + sessionMetric: SessionMetric +) => { + try { + await sendMetric(sessionMetric) + } catch (error) { + logger.error({ + error, + message: `Error to send metrics from session metric`, + }) + } +} + +export default sendSessionMetric