Skip to content

Add diagnostics-semconv to standardize some attributes and headers #579

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@types/koa": "^2.11.0",
"@types/koa-compose": "^3.2.3",
"@vtex/diagnostics-nodejs": "0.1.0-beta.8",
"@vtex/diagnostics-semconv": "0.1.0-beta.8",
"@vtex/node-error-report": "^0.0.3",
"@wry/equality": "^0.1.9",
"agentkeepalive": "^4.0.2",
Expand Down
33 changes: 13 additions & 20 deletions src/HttpClient/HttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,9 @@ import { createHash } from 'crypto'
import { IncomingMessage } from 'http'
import compose from 'koa-compose'
import pLimit from 'p-limit'

import {
BINDING_HEADER,
HeaderKeys,
BODY_HASH,
FORWARDED_HOST_HEADER,
LOCALE_HEADER,
PRODUCT_HEADER,
SEGMENT_HEADER,
SESSION_HEADER,
TENANT_HEADER,
} from '../constants'
import { Logger } from '../service/logger'
import { IOContext } from '../service/worker/runtime/typings'
Expand Down Expand Up @@ -89,14 +82,14 @@ export class HttpClient {
...defaultHeaders,
'Accept-Encoding': 'gzip',
'User-Agent': userAgent,
...host ? { [FORWARDED_HOST_HEADER]: host } : null,
...tenant ? { [TENANT_HEADER]: formatTenantHeaderValue(tenant) } : null,
...binding ? { [BINDING_HEADER]: formatBindingHeaderValue(binding) } : null,
...locale ? { [LOCALE_HEADER]: locale } : null,
...operationId ? { 'x-vtex-operation-id': operationId } : null,
...product ? { [PRODUCT_HEADER]: product } : null,
...segmentToken ? { [SEGMENT_HEADER]: segmentToken } : null,
...sessionToken ? { [SESSION_HEADER]: sessionToken } : null,
...host ? { [HeaderKeys.FORWARDED_HOST]: host } : null,
...tenant ? { [HeaderKeys.TENANT]: formatTenantHeaderValue(tenant) } : null,
...binding ? { [HeaderKeys.BINDING]: formatBindingHeaderValue(binding) } : null,
...locale ? { [HeaderKeys.LOCALE]: locale } : null,
...operationId ? { [HeaderKeys.OPERATION_ID]: operationId } : null,
...product ? { [HeaderKeys.PRODUCT]: product } : null,
...segmentToken ? { [HeaderKeys.SEGMENT]: segmentToken } : null,
...sessionToken ? { [HeaderKeys.SESSION]: sessionToken } : null,
}

if (authType && authToken) {
Expand Down Expand Up @@ -139,16 +132,16 @@ export class HttpClient {
return typeof v !== 'object' || v === null || Array.isArray(v) ? v :
Object.fromEntries(Object.entries(v).sort(([ka], [kb]) =>
ka < kb ? -1 : ka > kb ? 1 : 0))
}
catch(error) {
}
catch(error) {
// I don't believe this will ever happen, but just in case
// Also, I didn't include error as I am unsure if it would have sensitive information
this.logger.warn({message: 'Error while sorting object for cache key'})
return v
}
}


const bodyHash = createHash('md5').update(JSON.stringify(data, deterministicReplacer)).digest('hex')
const cacheableConfig = this.getConfig(url, {
...config,
Expand Down
10 changes: 5 additions & 5 deletions src/HttpClient/middlewares/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AxiosRequestConfig, AxiosResponse } from 'axios'
import { Span } from 'opentracing'

import { CacheLayer } from '../../caches/CacheLayer'
import { LOCALE_HEADER, SEGMENT_HEADER, SESSION_HEADER } from '../../constants'
import { HeaderKeys } from '../../constants'
import { IOContext } from '../../service/worker/runtime/typings'
import { ErrorReport } from '../../tracing'
import { HttpLogEvents } from '../../tracing/LogEvents'
Expand All @@ -15,7 +15,7 @@ const cacheableStatusCodes = [200, 203, 204, 206, 300, 301, 404, 405, 410, 414,

export const cacheKey = (config: AxiosRequestConfig) => {
const {baseURL = '', url = '', params, headers} = config
const locale = headers[LOCALE_HEADER]
const locale = headers[HeaderKeys.LOCALE]

const encodedBaseURL = baseURL.replace(/\//g, '\\')
const encodedURL = url.replace(/\//g, '\\')
Expand Down Expand Up @@ -97,7 +97,7 @@ export const cacheMiddleware = ({ type, storage, asyncSet }: CacheOptions) => {
const { rootSpan: span, tracer, logger } = ctx.tracing ?? {}

const key = cacheKey(ctx.config)
const segmentToken = ctx.config.headers[SEGMENT_HEADER]
const segmentToken = ctx.config.headers[HeaderKeys.SEGMENT]
const keyWithSegment = key + segmentToken

span?.log({
Expand Down Expand Up @@ -201,11 +201,11 @@ export const cacheMiddleware = ({ type, storage, asyncSet }: CacheOptions) => {
}

const shouldCache = maxAge || etag
const varySession = ctx.response.headers.vary && ctx.response.headers.vary.includes(SESSION_HEADER)
const varySession = ctx.response.headers.vary && ctx.response.headers.vary.includes(HeaderKeys.SESSION)
if (shouldCache && !varySession) {
const {responseType, responseEncoding: configResponseEncoding} = ctx.config
const currentAge = revalidated ? 0 : age
const varySegment = ctx.response.headers.vary && ctx.response.headers.vary.includes(SEGMENT_HEADER)
const varySegment = ctx.response.headers.vary && ctx.response.headers.vary.includes(HeaderKeys.SEGMENT)
const setKey = varySegment ? keyWithSegment : key
const responseEncoding = configResponseEncoding || (responseType === 'arraybuffer' ? 'base64' : undefined)
const cacheableData = type === CacheType.Disk && responseType === 'arraybuffer'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import buildFullPath from 'axios/lib/core/buildFullPath'
import { Span } from 'opentracing'
import { ROUTER_CACHE_HEADER } from '../../../../../../constants'
import { HeaderKeys } from '../../../../../../constants'
import { CustomHttpTags, OpentracingTags } from '../../../../../../tracing/Tags'
import { cloneAndSanitizeHeaders } from '../../../../../../tracing/utils'

Expand All @@ -11,7 +11,6 @@ export const injectRequestInfoOnSpan = (span: Span | undefined, http: AxiosInsta
[OpentracingTags.HTTP_METHOD]: config.method,
[OpentracingTags.HTTP_URL]: buildFullPath(config.baseURL, http.getUri(config)),
})

span?.log({ 'request-headers': cloneAndSanitizeHeaders(config.headers) })
}

Expand All @@ -24,7 +23,8 @@ export const injectResponseInfoOnSpan = (span: Span | undefined, response: Axios

span?.log({ 'response-headers': cloneAndSanitizeHeaders(response.headers) })
span?.setTag(OpentracingTags.HTTP_STATUS_CODE, response.status)
if (response.headers[ROUTER_CACHE_HEADER]) {
span?.setTag(CustomHttpTags.HTTP_ROUTER_CACHE_RESULT, response.headers[ROUTER_CACHE_HEADER])

if (response.headers[HeaderKeys.ROUTER_CACHE]) {
span?.setTag(CustomHttpTags.HTTP_ROUTER_CACHE_RESULT, response.headers[HeaderKeys.ROUTER_CACHE])
}
}
5 changes: 2 additions & 3 deletions src/clients/janus/Segment.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import parseCookie from 'cookie'
import { prop } from 'ramda'

import { PRODUCT_HEADER } from '../../constants'
import { HeaderKeys } from '../../constants'
import { inflightUrlWithQuery, RequestTracingConfig } from '../../HttpClient'
import { JanusClient } from './JanusClient'

Expand Down Expand Up @@ -87,7 +86,7 @@ export class Segment extends JanusClient {
forceMaxAge: SEGMENT_MAX_AGE_S,
headers: {
'Content-Type': 'application/json',
[PRODUCT_HEADER]: product || '',
[HeaderKeys.PRODUCT]: product || '',
},
inflightKey: inflightUrlWithQuery,
metric,
Expand Down
88 changes: 59 additions & 29 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { versionToMajor } from './utils/app'
import {
ATTR_VTEX_OPERATION_ID,
ATTR_VTEX_ACCOUNT_NAME,
ATTR_VTEX_IO_WORKSPACE_NAME,
ATTR_VTEX_IO_WORKSPACE_TYPE,
ATTR_VTEX_IO_APP_ID,
ATTR_VTEX_IO_APP_AUTHOR_TYPE
} from '@vtex/diagnostics-semconv'

// tslint:disable-next-line
const pkg = require('../package.json')

Expand All @@ -7,36 +16,57 @@ export const DEFAULT_WORKSPACE = 'master'
export const IS_IO = process.env.VTEX_IO
export const PID = process.pid

export const CACHE_CONTROL_HEADER = 'cache-control'
export const SEGMENT_HEADER = 'x-vtex-segment'
export const SESSION_HEADER = 'x-vtex-session'
export const PRODUCT_HEADER = 'x-vtex-product'
export const LOCALE_HEADER = 'x-vtex-locale'
export const FORWARDED_HOST_HEADER = 'x-forwarded-host'
export const TENANT_HEADER = 'x-vtex-tenant'
export const BINDING_HEADER = 'x-vtex-binding'
export const META_HEADER = 'x-vtex-meta'
export const META_HEADER_BUCKET = 'x-vtex-meta-bucket'
export const ETAG_HEADER = 'etag'
export const ACCOUNT_HEADER = 'x-vtex-account'
export const CREDENTIAL_HEADER = 'x-vtex-credential'
export const REQUEST_ID_HEADER = 'x-request-id'
export const ROUTER_CACHE_HEADER = 'x-router-cache'
export const OPERATION_ID_HEADER = 'x-vtex-operation-id'
export const PLATFORM_HEADER = 'x-vtex-platform'
export const WORKSPACE_IS_PRODUCTION_HEADER = 'x-vtex-workspace-is-production'
export const WORKSPACE_HEADER = 'x-vtex-workspace'
export const EVENT_KEY_HEADER = 'x-event-key'
export const EVENT_SENDER_HEADER = 'x-event-sender'
export const EVENT_SUBJECT_HEADER = 'x-event-subject'
export const EVENT_HANDLER_ID_HEADER = 'x-event-handler-id'
export const COLOSSUS_ROUTE_DECLARER_HEADER = 'x-colossus-route-declarer'
export const COLOSSUS_ROUTE_ID_HEADER = 'x-colossus-route-id'
export const COLOSSUS_PARAMS_HEADER = 'x-colossus-params'
export const TRACE_ID_HEADER = 'x-trace-id'
export const PROVIDER_HEADER = 'x-vtex-provider'
export const HeaderKeys = {
CACHE_CONTROL: 'cache-control',
SEGMENT: 'x-vtex-segment',
SESSION: 'x-vtex-session',
PRODUCT: 'x-vtex-product',
LOCALE: 'x-vtex-locale',
FORWARDED_HOST: 'x-forwarded-host',
FORWARDED_FOR: 'x-forwarded-for',
TENANT: 'x-vtex-tenant',
BINDING: 'x-vtex-binding',
META: 'x-vtex-meta',
META_BUCKET: 'x-vtex-meta-bucket',
ETAG: 'etag',
ACCOUNT: 'x-vtex-account',
CREDENTIAL: 'x-vtex-credential',
REQUEST_ID: 'x-request-id',
ROUTER_CACHE: 'x-router-cache',
OPERATION_ID: 'x-vtex-operation-id',
PLATFORM: 'x-vtex-platform',
WORKSPACE_IS_PRODUCTION: 'x-vtex-workspace-is-production',
WORKSPACE: 'x-vtex-workspace',
EVENT_KEY: 'x-event-key',
EVENT_SENDER: 'x-event-sender',
EVENT_SUBJECT: 'x-event-subject',
EVENT_HANDLER_ID: 'x-event-handler-id',
COLOSSUS_ROUTE_DECLARER: 'x-colossus-route-declarer',
COLOSSUS_ROUTE_ID: 'x-colossus-route-id',
COLOSSUS_PARAMS: 'x-colossus-params',
TRACE_ID: 'x-trace-id',
PROVIDER: 'x-vtex-provider',
USER_AGENT: 'user-agent',
VTEX_USER_AGENT: 'x-vtex-user-agent',
VTEX_IO_CALLER: 'x-vtex-io-caller',
VTEX_APP_SERVICE: 'x-vtex-app-service',
VTEX_APP_KEY: 'x-vtex-app-key',
VTEX_RETRY_COUNT: 'x-vtex-retry-count'
}

export const AttributeKeys = {
// VTEX Semantic Attributes
VTEX_OPERATION_ID: ATTR_VTEX_OPERATION_ID,
VTEX_ACCOUNT_NAME: ATTR_VTEX_ACCOUNT_NAME,

// VTEX IO Semantic Attributes
VTEX_IO_WORKSPACE_NAME: ATTR_VTEX_IO_WORKSPACE_NAME,
VTEX_IO_WORKSPACE_TYPE: ATTR_VTEX_IO_WORKSPACE_TYPE,
VTEX_IO_APP_ID: ATTR_VTEX_IO_APP_ID,
VTEX_IO_APP_AUTHOR_TYPE: ATTR_VTEX_IO_APP_AUTHOR_TYPE,
}

export type VaryHeaders = typeof SEGMENT_HEADER | typeof SESSION_HEADER | typeof PRODUCT_HEADER | typeof LOCALE_HEADER
export type VaryHeaders = typeof HeaderKeys.SEGMENT | typeof HeaderKeys.SESSION | typeof HeaderKeys.PRODUCT | typeof HeaderKeys.LOCALE

export const BODY_HASH = '__graphqlBodyHash'

Expand Down
15 changes: 8 additions & 7 deletions src/service/logger/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { APP, LOG_CLIENT_INIT_TIMEOUT_MS } from '../../constants'
import { APP, LOG_CLIENT_INIT_TIMEOUT_MS, AttributeKeys } from '../../constants'
import { cleanError } from '../../utils/error'
import { cleanLog } from '../../utils/log'
import { LogClient } from '@vtex/diagnostics-nodejs/dist/types';
Expand Down Expand Up @@ -82,13 +82,14 @@ export class Logger {
const inflatedLog = {
__VTEX_IO_LOG: true,
level,
app,
account: this.account,
workspace: this.workspace,
production: this.production,
data,
operationId: this.operationId,
[AttributeKeys.VTEX_IO_APP_ID]: app,
[AttributeKeys.VTEX_ACCOUNT_NAME]: this.account,
[AttributeKeys.VTEX_IO_WORKSPACE_NAME]: this.workspace,
[AttributeKeys.VTEX_IO_WORKSPACE_TYPE]: this.production ? 'production' : 'development',
[AttributeKeys.VTEX_IO_APP_AUTHOR_TYPE]: APP.IS_THIRD_PARTY() ? '3p' : '1p',
[AttributeKeys.VTEX_OPERATION_ID]: this.operationId,
requestId: this.requestId,
data,
... (this.tracingState?.isTraceSampled ? { traceId: this.tracingState.traceId } : null),
}

Expand Down
10 changes: 5 additions & 5 deletions src/service/tracing/tracingMiddlewares.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FORMAT_HTTP_HEADERS, SpanContext, Tracer } from 'opentracing'
import { finished as onStreamFinished } from 'stream'
import { ACCOUNT_HEADER, REQUEST_ID_HEADER, TRACE_ID_HEADER, WORKSPACE_HEADER } from '../../constants'
import { HeaderKeys } from '../../constants'
import { ErrorReport, getTraceInfo } from '../../tracing'
import { RuntimeLogEvents } from '../../tracing/LogEvents'
import { RuntimeLogFields } from '../../tracing/LogFields'
Expand Down Expand Up @@ -52,14 +52,14 @@ export const addTracingMiddleware = (tracer: Tracer) => {
[OpentracingTags.HTTP_METHOD]: ctx.request.method,
[OpentracingTags.HTTP_STATUS_CODE]: ctx.response.status,
[CustomHttpTags.HTTP_PATH]: ctx.request.path,
[VTEXIncomingRequestTags.VTEX_REQUEST_ID]: ctx.get(REQUEST_ID_HEADER),
[VTEXIncomingRequestTags.VTEX_WORKSPACE]: ctx.get(WORKSPACE_HEADER),
[VTEXIncomingRequestTags.VTEX_ACCOUNT]: ctx.get(ACCOUNT_HEADER),
[VTEXIncomingRequestTags.VTEX_REQUEST_ID]: ctx.get(HeaderKeys.REQUEST_ID),
[VTEXIncomingRequestTags.VTEX_WORKSPACE]: ctx.get(HeaderKeys.WORKSPACE),
[VTEXIncomingRequestTags.VTEX_ACCOUNT]: ctx.get(HeaderKeys.ACCOUNT),
})

currentSpan?.log(cloneAndSanitizeHeaders(ctx.request.headers, 'req.headers.'))
currentSpan?.log(cloneAndSanitizeHeaders(ctx.response.headers, 'res.headers.'))
ctx.set(TRACE_ID_HEADER, traceInfo.traceId!)
ctx.set(HeaderKeys.TRACE_ID, traceInfo.traceId!)
}

const onResFinished = () => {
Expand Down
5 changes: 2 additions & 3 deletions src/service/worker/runtime/builtIn/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { collectDefaultMetrics, register } from 'prom-client'
import { COLOSSUS_ROUTE_ID_HEADER } from '../../../../constants'

import { HeaderKeys } from '../../../../constants'
import { MetricsLogger } from '../../../logger/metricsLogger'
import { EventLoopLagMeasurer } from '../../../tracing/metrics/measurers/EventLoopLagMeasurer'
import { ServiceContext } from '../typings'
Expand Down Expand Up @@ -32,7 +31,7 @@ export const prometheusLoggerMiddleware = () => {
return next()
}

const routeId = ctx.get(COLOSSUS_ROUTE_ID_HEADER)
const routeId = ctx.get(HeaderKeys.COLOSSUS_ROUTE_ID)
if (routeId) {
return next()
}
Expand Down
13 changes: 5 additions & 8 deletions src/service/worker/runtime/events/middlewares/context.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { IOClients } from '../../../../../clients/IOClients'
import {
EVENT_HANDLER_ID_HEADER,
EVENT_KEY_HEADER,
EVENT_SENDER_HEADER,
EVENT_SUBJECT_HEADER,
HeaderKeys,
} from '../../../../../constants'
import { ParamsContext, RecorderState, ServiceContext } from '../../typings'
import { prepareHandlerCtx } from '../../utils/context'
Expand All @@ -13,12 +10,12 @@ export async function eventContextMiddleware <T extends IOClients, U extends Rec
ctx.vtex = {
...prepareHandlerCtx(header, ctx.tracing),
eventInfo: {
key: header[EVENT_KEY_HEADER],
sender: header[EVENT_SENDER_HEADER],
subject: header[EVENT_SUBJECT_HEADER],
key: header[HeaderKeys.EVENT_KEY],
sender: header[HeaderKeys.EVENT_SENDER],
subject: header[HeaderKeys.EVENT_SUBJECT],
},
route: {
id: header[EVENT_HANDLER_ID_HEADER],
id: header[HeaderKeys.EVENT_HANDLER_ID],
params: {},
type: 'event',
},
Expand Down
4 changes: 2 additions & 2 deletions src/service/worker/runtime/events/router.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EVENT_HANDLER_ID_HEADER } from '../../../../constants'
import { HeaderKeys } from '../../../../constants'
import { LogLevel } from '../../../logger'
import { RouteHandler, ServiceContext } from '../typings'
import { logOnceToDevConsole } from './../../../logger/console'

export const routerFromEventHandlers = (events: Record<string, RouteHandler> | null) => {
return async (ctx: ServiceContext, next: () => Promise<void>) => {
const handlerId = ctx.get(EVENT_HANDLER_ID_HEADER)
const handlerId = ctx.get(HeaderKeys.EVENT_HANDLER_ID)

if (!handlerId || !events) {
return next()
Expand Down
Loading
Loading