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

chore: add open telemetry to api #380

Draft
wants to merge 49 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5012247
chore: add open telemetry to api
icazevedo Apr 17, 2023
f5368d0
up
icazevedo Apr 17, 2023
2eb5857
fix: resolver spans not working because of graphql-jit package outdat…
icazevedo Apr 17, 2023
8440f0a
chore: add service and platform attributes
icazevedo Apr 18, 2023
0a5666f
chore: test changes in instrumentation
icazevedo Apr 18, 2023
ce5b1df
remove instrumentations
icazevedo Apr 18, 2023
38b5963
remove unused imports
icazevedo Apr 18, 2023
9f18230
chore: add custom open telemetry plugin
icazevedo Apr 18, 2023
0bdf87d
add back parentType to resolver span name
icazevedo Apr 18, 2023
255d716
test
icazevedo Apr 18, 2023
a55b581
teste louco
icazevedo Apr 18, 2023
52dd535
teste louco pt 2
icazevedo Apr 18, 2023
231d1ce
fix parenting I guess
icazevedo Apr 18, 2023
290aa3d
fix context switching
icazevedo Apr 18, 2023
3165584
fix early span end call
icazevedo Apr 18, 2023
3dad4be
remove all the fieldType stuff
icazevedo Apr 19, 2023
9d15d85
Test moving the ctx around
icazevedo Apr 19, 2023
27cd55c
AGORA VAI HEIN TO CONFIANTE
icazevedo Apr 19, 2023
8f9f2f3
add root span prefix to map keys
icazevedo Apr 19, 2023
265d706
add assurance to ctx type
icazevedo Apr 19, 2023
55f1a22
add debug path to attribute list
icazevedo Apr 19, 2023
b8a9ed7
fix list spans
icazevedo Apr 19, 2023
0fe7ea5
improve readability of list items in spans
icazevedo Apr 19, 2023
0e1478c
fix indexInList
icazevedo Apr 19, 2023
46519fb
clear key map after every query (hopefully)
icazevedo Apr 19, 2023
55962f6
change code mecanichs and clean up
icazevedo Apr 19, 2023
29f91c4
trigger cicd
icazevedo Apr 19, 2023
71a20c8
fix: initialize resolverContextsByRootSpans[rootContextSpanId] object
icazevedo Apr 19, 2023
1be2879
remove logging attributes from trace
icazevedo Apr 20, 2023
6549ced
add custom resolver to test custom resolvers and errors
icazevedo Apr 20, 2023
126f47b
add resolver to fail
icazevedo Apr 20, 2023
285f32d
now it's breaking!
icazevedo Apr 20, 2023
bb1a1c5
end resolver span even after errors
icazevedo Apr 20, 2023
78b76a3
record span error apropriately
icazevedo Apr 20, 2023
62f69d1
change masked errors order
icazevedo Apr 24, 2023
30c66ba
add node auto instrumentations
icazevedo Apr 24, 2023
5ec002f
use NodeTracerProvider
icazevedo Apr 24, 2023
29c1d0e
adding node instrumentations on lambda
icazevedo Apr 24, 2023
24da4bd
resource merge
icazevedo Apr 24, 2023
41aff4d
architectural changes - next integration. Testing node_options differ…
icazevedo May 4, 2023
029edc9
fix setupTracerProvider
icazevedo May 4, 2023
39e33c3
Revert "add node auto instrumentations"
icazevedo May 11, 2023
68e778b
test logs v0
icazevedo May 15, 2023
2445015
fix error attributes
icazevedo May 23, 2023
c8305bd
remove unused import
icazevedo May 23, 2023
11dea14
add more strategies
icazevedo May 23, 2023
89481aa
testing more things
icazevedo May 23, 2023
6193a2b
use https protocol
icazevedo May 23, 2023
f475a6b
trigget ci
icazevedo May 23, 2023
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
74 changes: 15 additions & 59 deletions @generated/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,69 +16,10 @@ export type Scalars = {
Boolean: boolean
Int: number
Float: number
/**
* Example:
*
* ```json
* {
* Color: 'Red', Size: '42'
* }
* ```
*/
ActiveVariations: any
/**
* Example:
*
* ```json
* {
* Color: [
* {
* src: "https://storecomponents.vtexassets.com/...",
* alt: "...",
* label: "...",
* value: "..."
* },
* {
* src: "https://storecomponents.vtexassets.com/...",
* alt: "...",
* label: "...",
* value: "..."
* }
* ],
* Size: [
* {
* src: "https://storecomponents.vtexassets.com/...",
* alt: "...",
* label: "...",
* value: "..."
* }
* ]
* }
* ```
*/
FormattedVariants: any
/** A string or the string representation of an object (a stringified object). */
ObjectOrString: any
/**
* Example:
*
* ```json
* {
* 'Color-Red-Size-40': 'classic-shoes-37'
* }
* ```
*/
SlugsMap: any
/**
* Example:
*
* ```json
* {
* Color: [ "Red", "Blue", "Green" ],
* Size: [ "40", "41" ]
* }
* ```
*/
VariantsByName: any
}

Expand Down Expand Up @@ -376,6 +317,11 @@ export type PickupStoreInfo = {
isPickupStore: Maybe<Scalars['Boolean']>
}

export type ProductCluster = {
id: Maybe<Scalars['String']>
name: Maybe<Scalars['String']>
}

export type Query = {
/** Returns information about all collections. */
allCollections: StoreCollectionConnection
Expand Down Expand Up @@ -773,6 +719,7 @@ export type StoreProduct = {
brand: StoreBrand
/** List of items consisting of chain linked web pages, ending with the current page. */
breadcrumbList: StoreBreadcrumbList
clusterHighlights: Maybe<Array<ProductCluster>>
/** Product description. */
description: Scalars['String']
/** Global Trade Item Number. */
Expand Down Expand Up @@ -947,6 +894,7 @@ export type ProductSummary_ProductFragment = {
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
clusterHighlights: Array<{ name: string | null; id: string | null }> | null
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand Down Expand Up @@ -1269,6 +1217,10 @@ export type ProductsQueryQuery = {
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
clusterHighlights: Array<{
name: string | null
id: string | null
}> | null
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand Down Expand Up @@ -1303,6 +1255,10 @@ export type SearchSuggestionsQueryQuery = {
id: string
brand: { name: string; brandName: string }
isVariantOf: { productGroupID: string; name: string }
clusterHighlights: Array<{
name: string | null
id: string | null
}> | null
image: Array<{ url: string; alternateName: string }>
offers: {
lowPrice: number
Expand Down
4 changes: 2 additions & 2 deletions @generated/graphql/persisted.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"ValidateCartMutation": "mutation ValidateCartMutation($cart: IStoreCart!, $session: IStoreSession!) {\n validateCart(cart: $cart, session: $session) {\n order {\n orderNumber\n acceptedOffer {\n seller {\n identifier\n }\n quantity\n price\n listPrice\n itemOffered {\n sku\n name\n image {\n url\n alternateName\n }\n brand {\n name\n }\n isVariantOf {\n productGroupID\n name\n }\n gtin\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n }\n }\n messages {\n text\n status\n }\n }\n}\n",
"SubscribeToNewsletter": "mutation SubscribeToNewsletter($data: IPersonNewsletter!) {\n subscribeToNewsletter(data: $data) {\n id\n }\n}\n",
"BrowserProductQuery": "query BrowserProductQuery($locator: [IStoreSelectedFacet!]!) {\n product(locator: $locator) {\n id: productID\n sku\n name\n gtin\n description\n isVariantOf {\n name\n productGroupID\n skuVariants {\n activeVariations\n slugsMap(dominantVariantName: \"Color\")\n availableVariations(dominantVariantName: \"Color\")\n }\n }\n image {\n url\n alternateName\n }\n brand {\n name\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n seller {\n identifier\n }\n }\n }\n breadcrumbList {\n itemListElement {\n item\n name\n position\n }\n }\n additionalProperty {\n propertyID\n name\n value\n valueReference\n }\n }\n}\n",
"ProductsQuery": "query ProductsQuery($first: Int!, $after: String, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n }\n}\n",
"SearchSuggestionsQuery": "query SearchSuggestionsQuery($term: String!, $selectedFacets: [IStoreSelectedFacet!]) {\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n isTermMisspelled\n logicalOperator\n }\n }\n}\n",
"ProductsQuery": "query ProductsQuery($first: Int!, $after: String, $sort: StoreSort!, $term: String!, $selectedFacets: [IStoreSelectedFacet!]!) {\n search(\n first: $first\n after: $after\n sort: $sort\n term: $term\n selectedFacets: $selectedFacets\n ) {\n products {\n pageInfo {\n totalCount\n }\n edges {\n node {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n clusterHighlights {\n name\n id\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n }\n }\n}\n",
"SearchSuggestionsQuery": "query SearchSuggestionsQuery($term: String!, $selectedFacets: [IStoreSelectedFacet!]) {\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n products {\n id: productID\n slug\n sku\n brand {\n brandName: name\n name\n }\n name\n gtin\n isVariantOf {\n productGroupID\n name\n }\n clusterHighlights {\n name\n id\n }\n image {\n url\n alternateName\n }\n offers {\n lowPrice\n offers {\n availability\n price\n listPrice\n quantity\n seller {\n identifier\n }\n }\n }\n }\n }\n products {\n pageInfo {\n totalCount\n }\n }\n metadata {\n isTermMisspelled\n logicalOperator\n }\n }\n}\n",
"TopSearchSuggestionsQuery": "query TopSearchSuggestionsQuery($term: String!, $selectedFacets: [IStoreSelectedFacet!]) {\n search(first: 5, term: $term, selectedFacets: $selectedFacets) {\n suggestions {\n terms {\n value\n }\n }\n }\n}\n",
"ValidateSession": "mutation ValidateSession($session: IStoreSession!, $search: String!) {\n validateSession(session: $session, search: $search) {\n locale\n channel\n country\n postalCode\n currency {\n code\n symbol\n }\n person {\n id\n email\n givenName\n familyName\n }\n }\n}\n",
"ShippingSimulationQuery": "query ShippingSimulationQuery($postalCode: String!, $country: String!, $items: [IShippingItem!]!) {\n shipping(items: $items, postalCode: $postalCode, country: $country) {\n logisticsInfo {\n slas {\n carrier\n price\n shippingEstimate\n localizedEstimates\n }\n }\n address {\n city\n neighborhood\n }\n }\n}\n"
Expand Down
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,24 @@
},
"dependencies": {
"@builder.io/partytown": "^0.6.1",
"@envelop/core": "^1.2.0",
"@envelop/graphql-jit": "^1.1.1",
"@envelop/parser-cache": "^2.2.0",
"@envelop/validation-cache": "^2.2.0",
"@envelop/core": "^2",
"@envelop/graphql-jit": "^4",
"@envelop/opentelemetry": "^4.0.6",
"@envelop/parser-cache": "^4",
"@envelop/validation-cache": "^4",
"@faststore/api": "^1.12.38",
"@faststore/graphql-utils": "^1.11.8",
"@faststore/sdk": "^1.11.8",
"@faststore/ui": "^1.12.23",
"@opentelemetry/api-logs": "^0.39.0",
"@opentelemetry/exporter-logs-otlp-grpc": "^0.39.1",
"@opentelemetry/exporter-trace-otlp-grpc": "^0.38.0",
"@opentelemetry/instrumentation": "^0.38.0",
"@opentelemetry/instrumentation-graphql": "^0.34.0",
"@opentelemetry/instrumentation-http": "^0.38.0",
"@opentelemetry/resources": "^1.12.0",
"@opentelemetry/sdk-logs": "^0.39.0",
"@opentelemetry/sdk-trace-base": "^1.12.0",
"@types/react": "^18.0.14",
"@vtex/client-cms": "^0.2.12",
"@vtex/tsconfig": "0.6.0",
Expand Down
5 changes: 4 additions & 1 deletion src/components/product/ProductCard/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ export const fragment = gql`
productGroupID
name
}

clusterHighlights {
name
id
}
image {
url
alternateName
Expand Down
140 changes: 138 additions & 2 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,31 @@ import {
import { useGraphQlJit } from '@envelop/graphql-jit'
import { useParserCache } from '@envelop/parser-cache'
import { useValidationCache } from '@envelop/validation-cache'
import { getContextFactory, getSchema, isFastStoreError } from '@faststore/api'
import {
getContextFactory,
getSchema,
getTypeDefs,
isFastStoreError,
} from '@faststore/api'
import { GraphQLError } from 'graphql'
import { makeExecutableSchema, mergeSchemas } from '@graphql-tools/schema'
import { mergeTypeDefs } from '@graphql-tools/merge'
import type { Maybe, Options as APIOptions, CacheControl } from '@faststore/api'
// Import the required OpenTelemetry libraries and plugins
import {
BasicTracerProvider,
SimpleSpanProcessor,
} from '@opentelemetry/sdk-trace-base'
import { Resource } from '@opentelemetry/resources'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'
import {
LoggerProvider,
SimpleLogRecordProcessor,
ConsoleLogRecordExporter,
} from '@opentelemetry/sdk-logs'
import { OTLPLogsExporter } from '@opentelemetry/exporter-logs-otlp-grpc'

import { useOpenTelemetry } from './openTelemetry'
import persisted from '../../@generated/graphql/persisted.json'
import storeConfig from '../../store.config'

Expand All @@ -22,8 +43,93 @@ interface ExecuteOptions<V = Record<string, unknown>> {
query?: string | null
}

const honeycombCollectorOptions = {
// url is optional and can be omitted - default is http://localhost:4317
url: 'opentelemetry-collector-beta.vtex.systems',
}

const openSearchCollectorOptions = {
// url is optional and can be omitted - default is http://localhost:4317
url: 'https://developer-logs.opentelemetry-collector.vtex.systems',
}

// Create a new tracer provider
const tracerProvider = new BasicTracerProvider({
resource: new Resource({
'service.name': 'faststore-api',
'service.version': '1.12.38',
'service.name_and_version': '[email protected]',
platform: storeConfig.platform,
[`${storeConfig.platform}.account`]: storeConfig.api.storeId,
[`${storeConfig.platform}.workspace`]: storeConfig.api.workspace,
[`${storeConfig.platform}.environment`]: storeConfig.api.environment,
}),
})

const loggerProvider = new LoggerProvider({
resource: new Resource({
'service.name': 'faststore-api',
'service.version': '1.12.38',
'service.name_and_version': '[email protected]',
platform: storeConfig.platform,
[`${storeConfig.platform}.account`]: storeConfig.api.storeId,
[`${storeConfig.platform}.workspace`]: storeConfig.api.workspace,
[`${storeConfig.platform}.environment`]: storeConfig.api.environment,
index: 'faststore_beta_api',
search_index: 'faststore_beta_api',
'vtex.search_index': 'faststore_beta_api',
}),
})

// Create a new exporter
const honeycombExporter = new OTLPTraceExporter(honeycombCollectorOptions)
const openSearchExporter = new OTLPLogsExporter(openSearchCollectorOptions)

// Set up a span processor to export spans to Honeycomb
const honeyCombSpanProcessor = new SimpleSpanProcessor(honeycombExporter)
const openSearchLogProcessor = new SimpleLogRecordProcessor(openSearchExporter)

// Set up a span processor to export spans to Honeycomb
const consoleLogExporter = new ConsoleLogRecordExporter()
const consoleLogProcessor = new SimpleLogRecordProcessor(consoleLogExporter)

// Set up a console exporter for debugging purposes
// const consoleExporter = new ConsoleSpanExporter()
// const debugProcessor = new SimpleSpanProcessor(consoleExporter)

// Register the span processors with the tracer provider
tracerProvider.addSpanProcessor(honeyCombSpanProcessor)
// tracerProvider.addSpanProcessor(debugProcessor)

loggerProvider.addLogRecordProcessor(openSearchLogProcessor)
loggerProvider.addLogRecordProcessor(consoleLogProcessor)

// Register the tracer provider with the OpenTelemetry API
tracerProvider.register()

const persistedQueries = new Map(Object.entries(persisted))

// Creating type definitions
const typeDefs = `
type ProductCluster {
id: String
name: String
}

extend type StoreProduct {
clusterHighlights: [ProductCluster!]
}
`

// Creating resolvers
const resolvers = {
StoreProduct: {
clusterHighlights: () => {
throw new Error('This is my test error from OpenTelemetry')
},
},
}

const apiOptions: APIOptions = {
platform: storeConfig.platform as APIOptions['platform'],
account: storeConfig.api.storeId,
Expand All @@ -38,6 +144,19 @@ const apiOptions: APIOptions = {

export const apiSchema = getSchema(apiOptions)

const mergedTypeDefs = mergeTypeDefs([getTypeDefs(), typeDefs])

const getMergedSchemas = async () =>
mergeSchemas({
schemas: [
await apiSchema,
makeExecutableSchema({
resolvers,
typeDefs: mergedTypeDefs,
}),
],
})

const apiContextFactory = getContextFactory(apiOptions)

const formatError: FormatErrorHandler = (err) => {
Expand All @@ -53,8 +172,25 @@ const formatError: FormatErrorHandler = (err) => {
const getEnvelop = async () =>
envelop({
plugins: [
useAsyncSchema(apiSchema),
useAsyncSchema(getMergedSchemas()),
useExtendContext(apiContextFactory),
useOpenTelemetry(
{
resolvers: true, // Tracks resolvers calls, and tracks resolvers thrown errors
variables: false, // Includes the operation variables values as part of the metadata collected
result: false, // Includes execution result object as part of the metadata collected
},

// The @opentelemetry/sdk-trace-base was renamed from @opentelemetry/tracing but the
// envelop plugin doesn't support this change yet. This causes the class type to be incompatible,
// even if they are the same. https://github.com/n1ru4l/envelop/issues/1610
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tracerProvider as any,
loggerProvider,
undefined,
undefined,
'faststore-api'
),
useMaskedErrors({ formatError }),
useGraphQlJit(),
useValidationCache(),
Expand Down
Loading