@vtex/api 6.46.0-beta.0 → 6.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +67 -5
  2. package/lib/HttpClient/HttpClient.d.ts +3 -1
  3. package/lib/HttpClient/HttpClient.js +5 -3
  4. package/lib/HttpClient/middlewares/cache.d.ts +1 -1
  5. package/lib/HttpClient/middlewares/cache.js +66 -35
  6. package/lib/HttpClient/middlewares/memoization.js +7 -6
  7. package/lib/HttpClient/middlewares/request/index.js +5 -4
  8. package/lib/HttpClient/middlewares/request/setupAxios/interceptors/exponentialBackoff.js +6 -1
  9. package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/index.js +8 -6
  10. package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/spanSetup.d.ts +2 -2
  11. package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/spanSetup.js +6 -6
  12. package/lib/HttpClient/middlewares/tracing.js +13 -11
  13. package/lib/HttpClient/typings.d.ts +14 -1
  14. package/lib/clients/infra/VBase.js +1 -1
  15. package/lib/service/logger/logger.d.ts +0 -6
  16. package/lib/service/logger/logger.js +0 -14
  17. package/lib/service/metrics/requestMetricsMiddleware.d.ts +2 -0
  18. package/lib/service/metrics/requestMetricsMiddleware.js +44 -0
  19. package/lib/service/tracing/TracerSingleton.js +0 -6
  20. package/lib/service/tracing/tracingMiddlewares.js +23 -35
  21. package/lib/service/worker/index.js +2 -0
  22. package/lib/service/worker/runtime/builtIn/handlers.js +6 -6
  23. package/lib/service/worker/runtime/builtIn/middlewares.js +1 -1
  24. package/lib/service/worker/runtime/http/middlewares/settings.js +2 -1
  25. package/lib/service/worker/runtime/statusTrack.js +3 -3
  26. package/lib/service/worker/runtime/typings.d.ts +2 -1
  27. package/lib/service/worker/runtime/utils/context.d.ts +1 -1
  28. package/lib/service/worker/runtime/utils/context.js +1 -1
  29. package/lib/tracing/LogFields.d.ts +5 -0
  30. package/lib/tracing/Tags.d.ts +3 -1
  31. package/lib/tracing/UserLandTracer.d.ts +7 -7
  32. package/lib/tracing/UserLandTracer.js +2 -1
  33. package/lib/tracing/errorReporting/ErrorReport.d.ts +1 -1
  34. package/lib/tracing/errorReporting/ErrorReport.js +13 -4
  35. package/lib/tracing/utils.d.ts +2 -2
  36. package/lib/tracing/utils.js +3 -3
  37. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -7,9 +7,64 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [6.46.0-beta.0] - 2023-03-07
10
+ ## [6.46.0] - 2023-10-25
11
+ ### Added
12
+ - Add disk cache steps and retry count to tracing
13
+
14
+ ### Changed
15
+ - Stop updating client cache when revalidated and still expired
16
+
17
+ ## [6.45.24] - 2023-10-05
18
+ ### Added
19
+
20
+ - Allow disabling memoization for all requests of a client
21
+
22
+ ## [6.45.23] - 2023-10-04
23
+
24
+ ### Fixed
25
+ - Fix metrics being restricted by tracing restrictions
26
+
27
+ ## [6.45.22] - 2023-09-20
28
+
29
+ ### Removed
11
30
 
12
- ## [6.46.0-beta] - 2023-03-07
31
+ - Remove reference of how to search for the app's errors on now-defunct Splunk.
32
+
33
+ ### Fixed
34
+
35
+ - Error tracing spans not logging the details.
36
+
37
+ ## [6.45.21] - 2023-09-19
38
+ ### Changed
39
+ - Fix tracingMiddleware shouldTrace decision
40
+ - Fix HttpClient tracing based on sampling decision
41
+
42
+ ## [6.45.21-beta.2] - 2023-09-19
43
+
44
+ ## [6.45.21-beta.1] - 2023-09-07
45
+
46
+ ## [6.45.21-beta.0] - 2023-09-04
47
+
48
+ ## [6.45.21-beta] - 2023-09-04
49
+
50
+ ## [6.45.20] - 2023-08-30
51
+ ### Changed
52
+ - Remove sampling decision from runtime
53
+ - Only create tracing spans when they come from router
54
+
55
+ ## [6.45.20-beta] - 2023-08-30
56
+
57
+ ## [6.45.19] - 2023-07-12
58
+ ### Changed
59
+ - Bump `graphql-upload` version
60
+
61
+ ## [6.45.18] - 2023-07-07
62
+ ### Fixed
63
+ - Metrics to prometheus
64
+
65
+ ## [6.45.16] - 2023-07-03
66
+ ### Changed
67
+ - Bump `prom-client` version
13
68
 
14
69
  ## [6.45.15] - 2023-02-01
15
70
  ### Changed
@@ -1761,9 +1816,16 @@ instead
1761
1816
  - `HttpClient` now adds `'Accept-Encoding': 'gzip'` header by default.
1762
1817
 
1763
1818
 
1764
- [Unreleased]: https://github.com/vtex/node-vtex-api/compare/v6.46.0-beta.0...HEAD
1765
- [6.46.0-beta.0]: https://github.com/vtex/node-vtex-api/compare/v6.46.0-beta...v6.46.0-beta.0
1766
- [6.46.0-beta]: https://github.com/vtex/node-vtex-api/compare/v6.45.15...v6.46.0-beta
1819
+ [Unreleased]: https://github.com/vtex/node-vtex-api/compare/v6.45.22...HEAD
1767
1820
  [6.45.15]: https://github.com/vtex/node-vtex-api/compare/v6.45.14...v6.45.15
1768
1821
  [6.45.14]: https://github.com/vtex/node-vtex-api/compare/v6.45.13...v6.45.14
1769
1822
  [6.45.13]: https://github.com/vtex/node-vtex-api/compare/v6.45.12...v6.45.13
1823
+
1824
+ [6.45.22]: https://github.com/vtex/node-vtex-api/compare/v6.45.21...v6.45.22
1825
+ [6.45.21]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.2...v6.45.21
1826
+ [6.45.21-beta.2]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.1...v6.45.21-beta.2
1827
+ [6.45.21-beta.1]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.0...v6.45.21-beta.1
1828
+ [6.45.21-beta.0]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta...v6.45.21-beta.0
1829
+ [6.45.21-beta]: https://github.com/vtex/node-vtex-api/compare/v6.45.20...v6.45.21-beta
1830
+ [6.45.20]: https://github.com/vtex/node-vtex-api/compare/v6.45.20-beta...v6.45.20
1831
+ [6.45.20-beta]: https://github.com/vtex/node-vtex-api/compare/v6.45.19...v6.45.20-beta
@@ -7,6 +7,8 @@ declare type ClientOptions = IOContext & Partial<InstanceOptions>;
7
7
  export declare class HttpClient {
8
8
  name: string;
9
9
  private logger;
10
+ private cacheableType;
11
+ private memoizable;
10
12
  private runMiddlewares;
11
13
  constructor(opts: ClientOptions);
12
14
  get: <T = any>(url: string, config?: RequestConfig) => Promise<T>;
@@ -24,7 +26,7 @@ export declare class HttpClient {
24
26
  patch: <T = void>(url: string, data?: any, config?: RequestConfig) => Promise<T>;
25
27
  head: (url: string, config?: RequestConfig) => Promise<IOResponse<void>>;
26
28
  delete: <T = void>(url: string, config?: RequestConfig | undefined) => Promise<IOResponse<T>>;
27
- protected request: (config: RequestConfig) => Promise<AxiosResponse<any, any>>;
29
+ protected request: (config: RequestConfig) => Promise<AxiosResponse<any>>;
28
30
  private getConfig;
29
31
  }
30
32
  export {};
@@ -96,14 +96,16 @@ class HttpClient {
96
96
  return context.response;
97
97
  };
98
98
  this.getConfig = (url, config = {}) => ({
99
- cacheable: cache_1.CacheType.Memory,
100
- memoizable: true,
99
+ cacheable: this.cacheableType,
100
+ memoizable: this.memoizable,
101
101
  ...config,
102
102
  url,
103
103
  });
104
- const { baseURL, authToken, authType, memoryCache, diskCache, locale, name, metrics, product, serverTiming, recorder, userAgent, timeout = DEFAULT_TIMEOUT_MS, segmentToken, sessionToken, retries, concurrency, headers: defaultHeaders, host, params, operationId, tenant, binding, verbose, cancellation, exponentialTimeoutCoefficient, initialBackoffDelay, exponentialBackoffCoefficient, httpsAgent, tracer, logger, } = opts;
104
+ const { baseURL, authToken, authType, memoryCache, diskCache, memoizable = true, locale, name, metrics, product, serverTiming, recorder, userAgent, timeout = DEFAULT_TIMEOUT_MS, segmentToken, sessionToken, retries, concurrency, headers: defaultHeaders, host, params, operationId, tenant, binding, verbose, cancellation, exponentialTimeoutCoefficient, initialBackoffDelay, exponentialBackoffCoefficient, httpsAgent, tracer, logger, cacheableType = cache_1.CacheType.Memory, } = opts;
105
105
  this.name = name || baseURL || 'unknown';
106
106
  this.logger = logger;
107
+ this.cacheableType = cacheableType;
108
+ this.memoizable = memoizable;
107
109
  const limit = concurrency && concurrency > 0 && p_limit_1.default(concurrency) || undefined;
108
110
  const headers = {
109
111
  ...defaultHeaders,
@@ -2,7 +2,7 @@
2
2
  import { AxiosRequestConfig, AxiosResponse } from 'axios';
3
3
  import { CacheLayer } from '../../caches/CacheLayer';
4
4
  import { MiddlewareContext, RequestConfig } from '../typings';
5
- export declare const cacheKey: (config: AxiosRequestConfig<any>) => string;
5
+ export declare const cacheKey: (config: AxiosRequestConfig) => string;
6
6
  export declare function isLocallyCacheable(arg: RequestConfig, type: CacheType): arg is CacheableRequestConfig;
7
7
  export declare enum CacheType {
8
8
  None = 0,
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const constants_1 = require("../../constants");
4
+ const tracing_1 = require("../../tracing");
4
5
  const RANGE_HEADER_QS_KEY = '__range_header';
5
6
  const cacheableStatusCodes = [200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501]; // https://tools.ietf.org/html/rfc7231#section-6.1
6
7
  exports.cacheKey = (config) => {
7
8
  const { baseURL = '', url = '', params, headers } = config;
8
- const locale = headers === null || headers === void 0 ? void 0 : headers[constants_1.LOCALE_HEADER];
9
+ const locale = headers[constants_1.LOCALE_HEADER];
9
10
  const encodedBaseURL = baseURL.replace(/\//g, '\\');
10
11
  const encodedURL = url.replace(/\//g, '\\');
11
12
  let key = `${locale}--${encodedBaseURL}--${encodedURL}?`;
@@ -59,25 +60,36 @@ exports.cacheMiddleware = ({ type, storage }) => {
59
60
  if (!isLocallyCacheable(ctx.config, type)) {
60
61
  return await next();
61
62
  }
62
- const span = ctx.tracing.rootSpan;
63
+ const { rootSpan: span, tracer, logger } = (_a = ctx.tracing) !== null && _a !== void 0 ? _a : {};
63
64
  const key = exports.cacheKey(ctx.config);
64
- const segmentToken = (_a = ctx.config.headers) === null || _a === void 0 ? void 0 : _a[constants_1.SEGMENT_HEADER];
65
+ const segmentToken = ctx.config.headers[constants_1.SEGMENT_HEADER];
65
66
  const keyWithSegment = key + segmentToken;
66
- span.log({
67
+ span === null || span === void 0 ? void 0 : span.log({
67
68
  event: "cache-key-created" /* CACHE_KEY_CREATE */,
68
69
  ["cache-type" /* CACHE_TYPE */]: cacheType,
69
70
  ["key" /* KEY */]: key,
70
71
  ["key-with-segment" /* KEY_WITH_SEGMENT */]: keyWithSegment,
71
72
  });
72
- const cacheHasWithSegment = await storage.has(keyWithSegment);
73
- const cached = cacheHasWithSegment ? await storage.get(keyWithSegment) : await storage.get(key);
73
+ const cacheReadSpan = createCacheSpan(cacheType, 'read', tracer, span);
74
+ let cached;
75
+ try {
76
+ const cacheHasWithSegment = await storage.has(keyWithSegment);
77
+ cached = cacheHasWithSegment ? await storage.get(keyWithSegment) : await storage.get(key);
78
+ }
79
+ catch (error) {
80
+ tracing_1.ErrorReport.create({ originalError: error }).injectOnSpan(cacheReadSpan);
81
+ logger === null || logger === void 0 ? void 0 : logger.warn({ message: 'Error reading from the HttpClient cache', error });
82
+ }
83
+ finally {
84
+ cacheReadSpan === null || cacheReadSpan === void 0 ? void 0 : cacheReadSpan.finish();
85
+ }
74
86
  if (cached && cached.response) {
75
87
  const { etag: cachedEtag, response, expiration, responseType, responseEncoding } = cached;
76
88
  if (type === CacheType.Disk && responseType === 'arraybuffer') {
77
89
  response.data = Buffer.from(response.data, responseEncoding);
78
90
  }
79
91
  const now = Date.now();
80
- span.log({
92
+ span === null || span === void 0 ? void 0 : span.log({
81
93
  event: "local-cache-hit-info" /* LOCAL_CACHE_HIT_INFO */,
82
94
  ["cache-type" /* CACHE_TYPE */]: cacheType,
83
95
  ["etag" /* ETAG */]: cachedEtag,
@@ -92,21 +104,18 @@ exports.cacheMiddleware = ({ type, storage }) => {
92
104
  revalidated: 0,
93
105
  router: 0,
94
106
  };
95
- span.setTag(CACHE_RESULT_TAG, "HIT" /* HIT */);
107
+ span === null || span === void 0 ? void 0 : span.setTag(CACHE_RESULT_TAG, "HIT" /* HIT */);
96
108
  return;
97
109
  }
98
- span.setTag(CACHE_RESULT_TAG, "STALE" /* STALE */);
110
+ span === null || span === void 0 ? void 0 : span.setTag(CACHE_RESULT_TAG, "STALE" /* STALE */);
99
111
  const validateStatus = addNotModified(ctx.config.validateStatus);
100
112
  if (cachedEtag && validateStatus(response.status)) {
101
- if (!ctx.config.headers) {
102
- ctx.config.headers = {};
103
- }
104
113
  ctx.config.headers['if-none-match'] = cachedEtag;
105
114
  ctx.config.validateStatus = validateStatus;
106
115
  }
107
116
  }
108
117
  else {
109
- span.setTag(CACHE_RESULT_TAG, "MISS" /* MISS */);
118
+ span === null || span === void 0 ? void 0 : span.setTag(CACHE_RESULT_TAG, "MISS" /* MISS */);
110
119
  }
111
120
  await next();
112
121
  if (!ctx.response) {
@@ -125,7 +134,7 @@ exports.cacheMiddleware = ({ type, storage }) => {
125
134
  const { age, etag, maxAge: headerMaxAge, noStore, noCache } = parseCacheHeaders(headers);
126
135
  const { forceMaxAge } = ctx.config;
127
136
  const maxAge = forceMaxAge && cacheableStatusCodes.includes(status) ? Math.max(forceMaxAge, headerMaxAge) : headerMaxAge;
128
- span.log({
137
+ span === null || span === void 0 ? void 0 : span.log({
129
138
  event: "cache-config" /* CACHE_CONFIG */,
130
139
  ["cache-type" /* CACHE_TYPE */]: cacheType,
131
140
  ["age" /* AGE */]: age,
@@ -138,7 +147,7 @@ exports.cacheMiddleware = ({ type, storage }) => {
138
147
  });
139
148
  // Indicates this should NOT be cached and this request will not be considered a miss.
140
149
  if (!forceMaxAge && (noStore || (noCache && !etag))) {
141
- span.log({ event: "no-local-cache-save" /* NO_LOCAL_CACHE_SAVE */, ["cache-type" /* CACHE_TYPE */]: cacheType });
150
+ span === null || span === void 0 ? void 0 : span.log({ event: "no-local-cache-save" /* NO_LOCAL_CACHE_SAVE */, ["cache-type" /* CACHE_TYPE */]: cacheType });
142
151
  return;
143
152
  }
144
153
  const shouldCache = maxAge || etag;
@@ -148,30 +157,52 @@ exports.cacheMiddleware = ({ type, storage }) => {
148
157
  const currentAge = revalidated ? 0 : age;
149
158
  const varySegment = ctx.response.headers.vary && ctx.response.headers.vary.includes(constants_1.SEGMENT_HEADER);
150
159
  const setKey = varySegment ? keyWithSegment : key;
151
- const responseEncoding = (configResponseEncoding || (responseType === 'arraybuffer' ? 'base64' : undefined));
160
+ const responseEncoding = configResponseEncoding || (responseType === 'arraybuffer' ? 'base64' : undefined);
152
161
  const cacheableData = type === CacheType.Disk && responseType === 'arraybuffer'
153
162
  ? data.toString(responseEncoding)
154
163
  : data;
155
- const expiration = Date.now() + (maxAge - currentAge) * 1000;
156
- await storage.set(setKey, {
157
- etag,
158
- expiration,
159
- response: { data: cacheableData, headers, status },
160
- responseEncoding,
161
- responseType,
162
- });
163
- span.log({
164
- event: "local-cache-saved" /* LOCAL_CACHE_SAVED */,
165
- ["cache-type" /* CACHE_TYPE */]: cacheType,
166
- ["key-set" /* KEY_SET */]: setKey,
167
- ["age" /* AGE */]: currentAge,
168
- ["etag" /* ETAG */]: etag,
169
- ["expiration-time" /* EXPIRATION_TIME */]: (expiration - Date.now()) / 1000,
170
- ["response-encoding" /* RESPONSE_ENCONDING */]: responseEncoding,
171
- ["response-type" /* RESPONSE_TYPE */]: responseType,
172
- });
164
+ const now = Date.now();
165
+ const expiration = now + (maxAge - currentAge) * 1000;
166
+ const alreadyExpired = expiration <= now;
167
+ const reusingRevalidatedCache = cached && (ctx.response === cached.response);
168
+ const shouldSkipCacheUpdate = alreadyExpired && reusingRevalidatedCache;
169
+ if (shouldSkipCacheUpdate) {
170
+ return;
171
+ }
172
+ const cacheWriteSpan = createCacheSpan(cacheType, 'write', tracer, span);
173
+ try {
174
+ await storage.set(setKey, {
175
+ etag,
176
+ expiration,
177
+ response: { data: cacheableData, headers, status },
178
+ responseEncoding,
179
+ responseType,
180
+ });
181
+ span === null || span === void 0 ? void 0 : span.log({
182
+ event: "local-cache-saved" /* LOCAL_CACHE_SAVED */,
183
+ ["cache-type" /* CACHE_TYPE */]: cacheType,
184
+ ["key-set" /* KEY_SET */]: setKey,
185
+ ["age" /* AGE */]: currentAge,
186
+ ["etag" /* ETAG */]: etag,
187
+ ["expiration-time" /* EXPIRATION_TIME */]: (expiration - Date.now()) / 1000,
188
+ ["response-encoding" /* RESPONSE_ENCONDING */]: responseEncoding,
189
+ ["response-type" /* RESPONSE_TYPE */]: responseType,
190
+ });
191
+ }
192
+ catch (error) {
193
+ tracing_1.ErrorReport.create({ originalError: error }).injectOnSpan(cacheWriteSpan);
194
+ logger === null || logger === void 0 ? void 0 : logger.warn({ message: 'Error writing to the HttpClient cache', error });
195
+ }
196
+ finally {
197
+ cacheWriteSpan === null || cacheWriteSpan === void 0 ? void 0 : cacheWriteSpan.finish();
198
+ }
173
199
  return;
174
200
  }
175
- span.log({ event: "no-local-cache-save" /* NO_LOCAL_CACHE_SAVE */, ["cache-type" /* CACHE_TYPE */]: cacheType });
201
+ span === null || span === void 0 ? void 0 : span.log({ event: "no-local-cache-save" /* NO_LOCAL_CACHE_SAVE */, ["cache-type" /* CACHE_TYPE */]: cacheType });
176
202
  };
177
203
  };
204
+ const createCacheSpan = (cacheType, operation, tracer, parentSpan) => {
205
+ if (tracer && tracer.isTraceSampled && cacheType === 'disk') {
206
+ return tracer.startSpan(`${operation}-disk-cache`, { childOf: parentSpan });
207
+ }
208
+ };
@@ -3,22 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const cache_1 = require("./cache");
4
4
  exports.memoizationMiddleware = ({ memoizedCache }) => {
5
5
  return async (ctx, next) => {
6
+ var _a;
6
7
  if (!cache_1.isLocallyCacheable(ctx.config, cache_1.CacheType.Any) || !ctx.config.memoizable) {
7
8
  return next();
8
9
  }
9
- const span = ctx.tracing.rootSpan;
10
+ const span = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.rootSpan;
10
11
  const key = cache_1.cacheKey(ctx.config);
11
12
  const isMemoized = !!memoizedCache.has(key);
12
- span.log({ event: "cache-key-created" /* CACHE_KEY_CREATE */, ["cache-type" /* CACHE_TYPE */]: 'memoization', ["key" /* KEY */]: key });
13
+ span === null || span === void 0 ? void 0 : span.log({ event: "cache-key-created" /* CACHE_KEY_CREATE */, ["cache-type" /* CACHE_TYPE */]: 'memoization', ["key" /* KEY */]: key });
13
14
  if (isMemoized) {
14
- span.setTag("http.cache.memoization" /* HTTP_MEMOIZATION_CACHE_RESULT */, "HIT" /* HIT */);
15
+ span === null || span === void 0 ? void 0 : span.setTag("http.cache.memoization" /* HTTP_MEMOIZATION_CACHE_RESULT */, "HIT" /* HIT */);
15
16
  const memoized = await memoizedCache.get(key);
16
17
  ctx.memoizedHit = isMemoized;
17
18
  ctx.response = memoized.response;
18
19
  return;
19
20
  }
20
21
  else {
21
- span.setTag("http.cache.memoization" /* HTTP_MEMOIZATION_CACHE_RESULT */, "MISS" /* MISS */);
22
+ span === null || span === void 0 ? void 0 : span.setTag("http.cache.memoization" /* HTTP_MEMOIZATION_CACHE_RESULT */, "MISS" /* MISS */);
22
23
  const promise = new Promise(async (resolve, reject) => {
23
24
  try {
24
25
  await next();
@@ -26,11 +27,11 @@ exports.memoizationMiddleware = ({ memoizedCache }) => {
26
27
  cacheHit: ctx.cacheHit,
27
28
  response: ctx.response,
28
29
  });
29
- span.log({ event: "memoization-cache-saved" /* MEMOIZATION_CACHE_SAVED */, ["key-set" /* KEY_SET */]: key });
30
+ span === null || span === void 0 ? void 0 : span.log({ event: "memoization-cache-saved" /* MEMOIZATION_CACHE_SAVED */, ["key-set" /* KEY_SET */]: key });
30
31
  }
31
32
  catch (err) {
32
33
  reject(err);
33
- span.log({ event: "memoization-cache-saved-error" /* MEMOIZATION_CACHE_SAVED_ERROR */, ["key-set" /* KEY_SET */]: key });
34
+ span === null || span === void 0 ? void 0 : span.log({ event: "memoization-cache-saved-error" /* MEMOIZATION_CACHE_SAVED_ERROR */, ["key-set" /* KEY_SET */]: key });
34
35
  }
35
36
  });
36
37
  memoizedCache.set(key, promise);
@@ -17,6 +17,7 @@ exports.defaultsMiddleware = ({ baseURL, rawHeaders, params, timeout, retries, v
17
17
  const countByMetric = {};
18
18
  const headers = renameBy_1.renameBy(ramda_1.toLower, rawHeaders);
19
19
  return (ctx, next) => {
20
+ var _a, _b, _c;
20
21
  ctx.config = {
21
22
  baseURL,
22
23
  exponentialBackoffCoefficient,
@@ -46,10 +47,10 @@ exports.defaultsMiddleware = ({ baseURL, rawHeaders, params, timeout, retries, v
46
47
  ctx.config.count = countByMetric[ctx.config.metric];
47
48
  ctx.config.label = `${ctx.config.metric}#${ctx.config.count}`;
48
49
  }
49
- if (ctx.tracing.isSampled) {
50
+ if ((_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.isSampled) {
50
51
  const { config } = ctx;
51
52
  const fullUrl = buildFullPath_1.default(config.baseURL, http.getUri(config));
52
- ctx.tracing.rootSpan.addTags({
53
+ (_c = (_b = ctx.tracing) === null || _b === void 0 ? void 0 : _b.rootSpan) === null || _c === void 0 ? void 0 : _c.addTags({
53
54
  [Tags_1.OpentracingTags.HTTP_METHOD]: config.method || 'get',
54
55
  [Tags_1.OpentracingTags.HTTP_URL]: fullUrl,
55
56
  });
@@ -61,12 +62,12 @@ const ROUTER_CACHE_KEY = 'x-router-cache';
61
62
  const ROUTER_CACHE_HIT = 'HIT';
62
63
  const ROUTER_CACHE_REVALIDATED = 'REVALIDATED';
63
64
  exports.routerCacheMiddleware = async (ctx, next) => {
64
- var _a, _b, _c;
65
+ var _a, _b, _c, _d, _e;
65
66
  await next();
66
67
  const routerCacheHit = (_b = (_a = ctx.response) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b[ROUTER_CACHE_KEY];
67
68
  const status = (_c = ctx.response) === null || _c === void 0 ? void 0 : _c.status;
68
69
  if (routerCacheHit) {
69
- ctx.tracing.rootSpan.setTag("http.cache.router" /* HTTP_ROUTER_CACHE_RESULT */, routerCacheHit);
70
+ (_e = (_d = ctx.tracing) === null || _d === void 0 ? void 0 : _d.rootSpan) === null || _e === void 0 ? void 0 : _e.setTag("http.cache.router" /* HTTP_ROUTER_CACHE_RESULT */, routerCacheHit);
70
71
  }
71
72
  if (routerCacheHit === ROUTER_CACHE_HIT || (routerCacheHit === ROUTER_CACHE_REVALIDATED && status !== 304)) {
72
73
  ctx.cacheHit = {
@@ -18,6 +18,7 @@ const exponentialDelay = (initialBackoffDelay, exponentialBackoffCoefficient, re
18
18
  // A lot of this code is based on:
19
19
  // https://github.com/softonic/axios-retry/blob/ffd4327f31d063522e58c525d28d4c5053d0ea7b/es/index.js#L109
20
20
  const onResponseError = (http) => (error) => {
21
+ var _a, _b, _c, _d;
21
22
  if (error.config == null) {
22
23
  return Promise.reject(error);
23
24
  }
@@ -38,7 +39,11 @@ const onResponseError = (http) => (error) => {
38
39
  ? config.timeout * exponentialTimeoutCoefficient
39
40
  : config.timeout;
40
41
  config.transformRequest = [data => data];
41
- config.tracing.rootSpan.log({ event: "setup-request-retry" /* SETUP_REQUEST_RETRY */, ["retry-number" /* RETRY_NUMBER */]: config.retryCount, ["retry-in" /* RETRY_IN */]: delay });
42
+ (_b = (_a = config.tracing) === null || _a === void 0 ? void 0 : _a.rootSpan) === null || _b === void 0 ? void 0 : _b.log({ event: "setup-request-retry" /* SETUP_REQUEST_RETRY */, ["retry-number" /* RETRY_NUMBER */]: config.retryCount, ["retry-in" /* RETRY_IN */]: delay });
43
+ (_d = (_c = config.tracing) === null || _c === void 0 ? void 0 : _c.rootSpan) === null || _d === void 0 ? void 0 : _d.addTags({
44
+ ["http.retry.count" /* HTTP_RETRY_COUNT */]: config.retryCount,
45
+ ["http.retry.error.code" /* HTTP_RETRY_ERROR_CODE */]: error.code,
46
+ });
42
47
  return new Promise(resolve => setTimeout(() => resolve(http(config)), delay));
43
48
  }
44
49
  return Promise.reject(error);
@@ -6,7 +6,8 @@ const SpanReferenceTypes_1 = require("../../../../../../tracing/spanReference/Sp
6
6
  const spanSetup_1 = require("./spanSetup");
7
7
  exports.requestSpanPrefix = 'http-request';
8
8
  const preRequestInterceptor = (http) => (config) => {
9
- if (!config.tracing || !config.tracing.isSampled) {
9
+ var _a;
10
+ if (!config.tracing || !((_a = config.tracing) === null || _a === void 0 ? void 0 : _a.isSampled)) {
10
11
  return config;
11
12
  }
12
13
  const { tracer, rootSpan, requestSpanNameSuffix } = config.tracing;
@@ -22,17 +23,18 @@ const preRequestInterceptor = (http) => (config) => {
22
23
  return config;
23
24
  };
24
25
  const onResponseSuccess = (response) => {
25
- if (!response.config.tracing || !response.config.tracing.isSampled) {
26
+ var _a, _b;
27
+ if (!response.config.tracing || !((_a = response.config.tracing) === null || _a === void 0 ? void 0 : _a.isSampled)) {
26
28
  return response;
27
29
  }
28
- const requestSpan = response.config.tracing.requestSpan;
30
+ const requestSpan = (_b = response.config.tracing) === null || _b === void 0 ? void 0 : _b.requestSpan;
29
31
  spanSetup_1.injectResponseInfoOnSpan(requestSpan, response);
30
- requestSpan.finish();
32
+ requestSpan === null || requestSpan === void 0 ? void 0 : requestSpan.finish();
31
33
  return response;
32
34
  };
33
35
  const onResponseError = (err) => {
34
- var _a, _b;
35
- if (!((_b = (_a = err === null || err === void 0 ? void 0 : err.config) === null || _a === void 0 ? void 0 : _a.tracing) === null || _b === void 0 ? void 0 : _b.requestSpan) || !err.config.tracing.isSampled) {
36
+ var _a, _b, _c;
37
+ if (!((_b = (_a = err === null || err === void 0 ? void 0 : err.config) === null || _a === void 0 ? void 0 : _a.tracing) === null || _b === void 0 ? void 0 : _b.requestSpan) || !((_c = err.config.tracing) === null || _c === void 0 ? void 0 : _c.isSampled)) {
36
38
  return Promise.reject(err);
37
39
  }
38
40
  const { requestSpan } = err.config.tracing;
@@ -1,4 +1,4 @@
1
1
  import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
2
2
  import { Span } from 'opentracing';
3
- export declare const injectRequestInfoOnSpan: (span: Span, http: AxiosInstance, config: AxiosRequestConfig<any>) => void;
4
- export declare const injectResponseInfoOnSpan: (span: Span, response: AxiosResponse<any, any> | undefined) => void;
3
+ export declare const injectRequestInfoOnSpan: (span: Span | undefined, http: AxiosInstance, config: AxiosRequestConfig) => void;
4
+ export declare const injectResponseInfoOnSpan: (span: Span | undefined, response: AxiosResponse<any> | undefined) => void;
@@ -8,22 +8,22 @@ const constants_1 = require("../../../../../../constants");
8
8
  const Tags_1 = require("../../../../../../tracing/Tags");
9
9
  const utils_1 = require("../../../../../../tracing/utils");
10
10
  exports.injectRequestInfoOnSpan = (span, http, config) => {
11
- span.addTags({
11
+ span === null || span === void 0 ? void 0 : span.addTags({
12
12
  [Tags_1.OpentracingTags.SPAN_KIND]: Tags_1.OpentracingTags.SPAN_KIND_RPC_CLIENT,
13
13
  [Tags_1.OpentracingTags.HTTP_METHOD]: config.method,
14
14
  [Tags_1.OpentracingTags.HTTP_URL]: buildFullPath_1.default(config.baseURL, http.getUri(config)),
15
15
  });
16
- span.log({ 'request-headers': utils_1.cloneAndSanitizeHeaders(config.headers) });
16
+ span === null || span === void 0 ? void 0 : span.log({ 'request-headers': utils_1.cloneAndSanitizeHeaders(config.headers) });
17
17
  };
18
18
  // Response may be undefined in case of client timeout, invalid URL, ...
19
19
  exports.injectResponseInfoOnSpan = (span, response) => {
20
20
  if (!response) {
21
- span.setTag("http.no_response" /* HTTP_NO_RESPONSE */, 'true');
21
+ span === null || span === void 0 ? void 0 : span.setTag("http.no_response" /* HTTP_NO_RESPONSE */, 'true');
22
22
  return;
23
23
  }
24
- span.log({ 'response-headers': utils_1.cloneAndSanitizeHeaders(response.headers) });
25
- span.setTag(Tags_1.OpentracingTags.HTTP_STATUS_CODE, response.status);
24
+ span === null || span === void 0 ? void 0 : span.log({ 'response-headers': utils_1.cloneAndSanitizeHeaders(response.headers) });
25
+ span === null || span === void 0 ? void 0 : span.setTag(Tags_1.OpentracingTags.HTTP_STATUS_CODE, response.status);
26
26
  if (response.headers[constants_1.ROUTER_CACHE_HEADER]) {
27
- span.setTag("http.cache.router" /* HTTP_ROUTER_CACHE_RESULT */, response.headers[constants_1.ROUTER_CACHE_HEADER]);
27
+ span === null || span === void 0 ? void 0 : span.setTag("http.cache.router" /* HTTP_ROUTER_CACHE_RESULT */, response.headers[constants_1.ROUTER_CACHE_HEADER]);
28
28
  }
29
29
  };
@@ -5,13 +5,15 @@ const Tags_1 = require("../../tracing/Tags");
5
5
  const cache_1 = require("./cache");
6
6
  exports.createHttpClientTracingMiddleware = ({ tracer, logger, clientName, hasMemoryCacheMiddleware, hasDiskCacheMiddleware, }) => {
7
7
  return async function tracingMiddleware(ctx, next) {
8
- const { rootSpan, requestSpanNameSuffix, referenceType = tracing_1.SpanReferenceTypes.CHILD_OF } = ctx.config.tracing || {};
8
+ var _a;
9
+ if (!tracer.isTraceSampled) {
10
+ await next();
11
+ return;
12
+ }
13
+ const rootSpan = tracer.fallbackSpanContext();
14
+ const { requestSpanNameSuffix } = ctx.config.tracing || {};
9
15
  const spanName = requestSpanNameSuffix ? `request:${requestSpanNameSuffix}` : 'request';
10
- const span = rootSpan
11
- ? tracer.startSpan(spanName, {
12
- references: [tracing_1.createSpanReference(rootSpan, referenceType)],
13
- })
14
- : tracer.startSpan(spanName);
16
+ const span = tracer.startSpan(spanName, { childOf: rootSpan });
15
17
  ctx.tracing = {
16
18
  ...ctx.config.tracing,
17
19
  isSampled: tracing_1.getTraceInfo(span).isSampled,
@@ -23,7 +25,7 @@ exports.createHttpClientTracingMiddleware = ({ tracer, logger, clientName, hasMe
23
25
  const hasMemoCache = !(!cache_1.isLocallyCacheable(ctx.config, cache_1.CacheType.Any) || !ctx.config.memoizable);
24
26
  const hasMemoryCache = hasMemoryCacheMiddleware && !!cache_1.isLocallyCacheable(ctx.config, cache_1.CacheType.Memory);
25
27
  const hasDiskCache = hasDiskCacheMiddleware && !!cache_1.isLocallyCacheable(ctx.config, cache_1.CacheType.Disk);
26
- span.addTags({
28
+ span === null || span === void 0 ? void 0 : span.addTags({
27
29
  ["http.cache.memoization.enabled" /* HTTP_MEMOIZATION_CACHE_ENABLED */]: hasMemoCache,
28
30
  ["http.cache.memory.enabled" /* HTTP_MEMORY_CACHE_ENABLED */]: hasMemoryCache,
29
31
  ["http.cache.disk.enabled" /* HTTP_DISK_CACHE_ENABLED */]: hasDiskCache,
@@ -36,19 +38,19 @@ exports.createHttpClientTracingMiddleware = ({ tracer, logger, clientName, hasMe
36
38
  }
37
39
  catch (err) {
38
40
  response = err.response;
39
- if (ctx.tracing.isSampled) {
41
+ if ((_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.isSampled) {
40
42
  tracing_1.ErrorReport.create({ originalError: err }).injectOnSpan(span, logger);
41
43
  }
42
44
  throw err;
43
45
  }
44
46
  finally {
45
47
  if (response) {
46
- span.setTag(Tags_1.OpentracingTags.HTTP_STATUS_CODE, response.status);
48
+ span === null || span === void 0 ? void 0 : span.setTag(Tags_1.OpentracingTags.HTTP_STATUS_CODE, response.status);
47
49
  }
48
50
  else {
49
- span.setTag("http.no_response" /* HTTP_NO_RESPONSE */, true);
51
+ span === null || span === void 0 ? void 0 : span.setTag("http.no_response" /* HTTP_NO_RESPONSE */, true);
50
52
  }
51
- span.finish();
53
+ span === null || span === void 0 ? void 0 : span.finish();
52
54
  }
53
55
  };
54
56
  };
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { AxiosRequestConfig, AxiosResponse } from 'axios';
2
3
  import { Middleware } from 'koa-compose';
3
4
  import { Span } from 'opentracing';
@@ -43,6 +44,7 @@ export interface RequestConfig extends AxiosRequestConfig, RequestTracingConfig
43
44
  memoizeable?: boolean;
44
45
  inflightKey?: InflightKeyGenerator;
45
46
  forceMaxAge?: number;
47
+ responseEncoding?: BufferEncoding;
46
48
  nullIfNotFound?: boolean;
47
49
  ignoreRecorder?: boolean;
48
50
  }
@@ -55,7 +57,7 @@ export interface CacheHit {
55
57
  export interface MiddlewaresTracingContext extends Omit<RequestTracingUserConfig, 'rootSpan'> {
56
58
  tracer: IUserLandTracer;
57
59
  logger: IOContext['logger'];
58
- rootSpan: Span;
60
+ rootSpan?: Span;
59
61
  isSampled: boolean;
60
62
  }
61
63
  export interface MiddlewareContext {
@@ -72,6 +74,16 @@ export interface InstanceOptions {
72
74
  timeout?: number;
73
75
  memoryCache?: CacheLayer<string, Cached>;
74
76
  diskCache?: CacheLayer<string, Cached>;
77
+ /**
78
+ * Enables memoization, ephemeral within each request, for all requests of this client.
79
+ * Useful for services that makes recursive requests, like graphql resolvers, which
80
+ * might fetch the same endpoint more than once.
81
+ * If that's not the case for your service, disabling it might improve the CPU and
82
+ * memory usage.
83
+ *
84
+ * Default value: true
85
+ */
86
+ memoizable?: boolean;
75
87
  baseURL?: string;
76
88
  retries?: number;
77
89
  exponentialTimeoutCoefficient?: number;
@@ -104,6 +116,7 @@ export interface InstanceOptions {
104
116
  name?: string;
105
117
  serverTimings?: Record<string, string>;
106
118
  httpsAgent?: AxiosRequestConfig['httpsAgent'];
119
+ cacheableType?: CacheType;
107
120
  }
108
121
  export interface IOResponse<T> {
109
122
  data: T;
@@ -138,7 +138,7 @@ class VBase extends InfraClient_1.InfraClient {
138
138
  return this.saveContent(bucket, path, stream, { unzip: true }, tracingConfig, ifMatch);
139
139
  };
140
140
  this.deleteFile = (bucket, path, tracingConfig, ifMatch) => {
141
- const headers = ifMatch ? { 'If-Match': ifMatch } : {};
141
+ const headers = ifMatch ? { 'If-Match': ifMatch } : null;
142
142
  const metric = 'vbase-delete-file';
143
143
  return this.http.delete(routes.File(bucket, path), { headers, metric, tracing: {
144
144
  requestSpanNameSuffix: metric,
@@ -25,11 +25,5 @@ export declare class Logger {
25
25
  warn: (warning: any) => void;
26
26
  error: (error: any) => void;
27
27
  log: (message: any, level: LogLevel) => void;
28
- /**
29
- * Logs splunk query so the developer can search for the errors in splunk.
30
- * This function runs only once in the lifetime of the Logger class so we
31
- * don't mess up with the developer's terminal
32
- */
33
- private logSplunkQuery;
34
28
  }
35
29
  export {};
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const constants_1 = require("../../constants");
4
4
  const error_1 = require("../../utils/error");
5
5
  const log_1 = require("../../utils/log");
6
- const console_1 = require("./console");
7
6
  const linked = !!process.env.VTEX_APP_LINK;
8
7
  const app = constants_1.APP.ID;
9
8
  const EMPTY_MESSAGE = 'Logger.log was called with null or undefined message';
@@ -45,19 +44,6 @@ class Logger {
45
44
  });
46
45
  }
47
46
  console.log(JSON.stringify(inflatedLog));
48
- // Warn the developer how to retrieve the error in splunk
49
- this.logSplunkQuery();
50
- };
51
- /**
52
- * Logs splunk query so the developer can search for the errors in splunk.
53
- * This function runs only once in the lifetime of the Logger class so we
54
- * don't mess up with the developer's terminal
55
- */
56
- this.logSplunkQuery = () => {
57
- if (linked) {
58
- const message = `Try this query at Splunk to retrieve error log: 'index=io_vtex_logs app="${app}" account=${this.account} workspace=${this.workspace} level=error OR level=warn'`;
59
- console_1.logOnceToDevConsole(message, LogLevel.Info);
60
- }
61
47
  };
62
48
  this.account = ctx.account;
63
49
  this.workspace = ctx.workspace;
@@ -0,0 +1,2 @@
1
+ import { ServiceContext } from '../worker/runtime/typings';
2
+ export declare const addRequestMetricsMiddleware: () => (ctx: ServiceContext<import("../..").IOClients, import("../worker/runtime/typings").RecorderState, import("../worker/runtime/typings").ParamsContext>, next: () => Promise<void>) => Promise<void>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const stream_1 = require("stream");
4
+ const utils_1 = require("../../utils");
5
+ const instruments_1 = require("../tracing/metrics/instruments");
6
+ exports.addRequestMetricsMiddleware = () => {
7
+ const concurrentRequests = instruments_1.createConcurrentRequestsInstrument();
8
+ const requestTimings = instruments_1.createRequestsTimingsInstrument();
9
+ const totalRequests = instruments_1.createTotalRequestsInstrument();
10
+ const responseSizes = instruments_1.createRequestsResponseSizesInstrument();
11
+ const abortedRequests = instruments_1.createTotalAbortedRequestsInstrument();
12
+ return async function addRequestMetrics(ctx, next) {
13
+ const start = process.hrtime();
14
+ concurrentRequests.inc(1);
15
+ ctx.req.once('aborted', () => abortedRequests.inc({ ["handler" /* REQUEST_HANDLER */]: ctx.requestHandlerName }, 1));
16
+ let responseClosed = false;
17
+ ctx.res.once('close', () => (responseClosed = true));
18
+ try {
19
+ await next();
20
+ }
21
+ finally {
22
+ const responseLength = ctx.response.length;
23
+ if (responseLength) {
24
+ responseSizes.observe({ ["handler" /* REQUEST_HANDLER */]: ctx.requestHandlerName }, responseLength);
25
+ }
26
+ totalRequests.inc({
27
+ ["handler" /* REQUEST_HANDLER */]: ctx.requestHandlerName,
28
+ ["status_code" /* STATUS_CODE */]: ctx.response.status,
29
+ }, 1);
30
+ const onResFinished = () => {
31
+ requestTimings.observe({
32
+ ["handler" /* REQUEST_HANDLER */]: ctx.requestHandlerName,
33
+ }, utils_1.hrToMillisFloat(process.hrtime(start)));
34
+ concurrentRequests.dec(1);
35
+ };
36
+ if (responseClosed) {
37
+ onResFinished();
38
+ }
39
+ else {
40
+ stream_1.finished(ctx.res, onResFinished);
41
+ }
42
+ }
43
+ };
44
+ };
@@ -30,12 +30,6 @@ class TracerSingleton {
30
30
  reporter: {
31
31
  agentHost: process.env.VTEX_OWN_NODE_IP,
32
32
  },
33
- sampler: {
34
- host: process.env.VTEX_OWN_NODE_IP,
35
- param: 0.05,
36
- refreshIntervalMs: 60 * 1000,
37
- type: 'remote',
38
- },
39
33
  serviceName,
40
34
  };
41
35
  const options = {
@@ -7,31 +7,22 @@ const tracing_1 = require("../../tracing");
7
7
  const Tags_1 = require("../../tracing/Tags");
8
8
  const utils_1 = require("../../tracing/utils");
9
9
  const utils_2 = require("../../utils");
10
- const instruments_1 = require("./metrics/instruments");
11
10
  const PATHS_BLACKLISTED_FOR_TRACING = ['/_status', '/healthcheck'];
12
11
  exports.addTracingMiddleware = (tracer) => {
13
- const concurrentRequests = instruments_1.createConcurrentRequestsInstrument();
14
- const requestTimings = instruments_1.createRequestsTimingsInstrument();
15
- const totalRequests = instruments_1.createTotalRequestsInstrument();
16
- const responseSizes = instruments_1.createRequestsResponseSizesInstrument();
17
- const abortedRequests = instruments_1.createTotalAbortedRequestsInstrument();
18
12
  return async function addTracing(ctx, next) {
19
13
  var _a;
20
- const start = process.hrtime();
21
- concurrentRequests.inc(1);
22
- if (PATHS_BLACKLISTED_FOR_TRACING.includes(ctx.request.path)) {
14
+ const rootSpan = tracer.extract(opentracing_1.FORMAT_HTTP_HEADERS, ctx.request.headers);
15
+ ctx.tracing = { tracer, currentSpan: undefined };
16
+ if (!shouldTrace(ctx, rootSpan)) {
23
17
  await next();
24
- concurrentRequests.dec(1);
25
18
  return;
26
19
  }
27
- const rootSpan = tracer.extract(opentracing_1.FORMAT_HTTP_HEADERS, ctx.request.headers);
28
20
  const currentSpan = tracer.startSpan('unknown-operation', {
29
21
  childOf: rootSpan,
30
22
  tags: { [Tags_1.OpentracingTags.SPAN_KIND]: Tags_1.OpentracingTags.SPAN_KIND_RPC_SERVER },
31
23
  });
32
24
  const initialSamplingDecision = tracing_1.getTraceInfo(currentSpan).isSampled;
33
25
  ctx.tracing = { currentSpan, tracer };
34
- ctx.req.once('aborted', () => abortedRequests.inc({ ["handler" /* REQUEST_HANDLER */]: currentSpan.operationName }, 1));
35
26
  let responseClosed = false;
36
27
  ctx.res.once('close', () => (responseClosed = true));
37
28
  try {
@@ -42,20 +33,12 @@ exports.addTracingMiddleware = (tracer) => {
42
33
  throw err;
43
34
  }
44
35
  finally {
45
- const responseLength = ctx.response.length;
46
- if (responseLength) {
47
- responseSizes.observe({ ["handler" /* REQUEST_HANDLER */]: currentSpan.operationName }, responseLength);
48
- }
49
- totalRequests.inc({
50
- ["handler" /* REQUEST_HANDLER */]: currentSpan.operationName,
51
- ["status_code" /* STATUS_CODE */]: ctx.response.status,
52
- }, 1);
53
36
  const traceInfo = tracing_1.getTraceInfo(currentSpan);
54
- if (traceInfo.isSampled) {
37
+ if (traceInfo === null || traceInfo === void 0 ? void 0 : traceInfo.isSampled) {
55
38
  if (!initialSamplingDecision) {
56
- currentSpan.setTag(Tags_1.OpentracingTags.SPAN_KIND, Tags_1.OpentracingTags.SPAN_KIND_RPC_SERVER);
39
+ currentSpan === null || currentSpan === void 0 ? void 0 : currentSpan.setTag(Tags_1.OpentracingTags.SPAN_KIND, Tags_1.OpentracingTags.SPAN_KIND_RPC_SERVER);
57
40
  }
58
- currentSpan.addTags({
41
+ currentSpan === null || currentSpan === void 0 ? void 0 : currentSpan.addTags({
59
42
  [Tags_1.OpentracingTags.HTTP_URL]: ctx.request.href,
60
43
  [Tags_1.OpentracingTags.HTTP_METHOD]: ctx.request.method,
61
44
  [Tags_1.OpentracingTags.HTTP_STATUS_CODE]: ctx.response.status,
@@ -64,16 +47,12 @@ exports.addTracingMiddleware = (tracer) => {
64
47
  ["vtex.incoming.workspace" /* VTEX_WORKSPACE */]: ctx.get(constants_1.WORKSPACE_HEADER),
65
48
  ["vtex.incoming.account" /* VTEX_ACCOUNT */]: ctx.get(constants_1.ACCOUNT_HEADER),
66
49
  });
67
- currentSpan.log(utils_1.cloneAndSanitizeHeaders(ctx.request.headers, 'req.headers.'));
68
- currentSpan.log(utils_1.cloneAndSanitizeHeaders(ctx.response.headers, 'res.headers.'));
50
+ currentSpan === null || currentSpan === void 0 ? void 0 : currentSpan.log(utils_1.cloneAndSanitizeHeaders(ctx.request.headers, 'req.headers.'));
51
+ currentSpan === null || currentSpan === void 0 ? void 0 : currentSpan.log(utils_1.cloneAndSanitizeHeaders(ctx.response.headers, 'res.headers.'));
69
52
  ctx.set(constants_1.TRACE_ID_HEADER, traceInfo.traceId);
70
53
  }
71
54
  const onResFinished = () => {
72
- requestTimings.observe({
73
- ["handler" /* REQUEST_HANDLER */]: currentSpan.operationName,
74
- }, utils_2.hrToMillisFloat(process.hrtime(start)));
75
- concurrentRequests.dec(1);
76
- currentSpan.finish();
55
+ currentSpan === null || currentSpan === void 0 ? void 0 : currentSpan.finish();
77
56
  };
78
57
  if (responseClosed) {
79
58
  onResFinished();
@@ -86,8 +65,9 @@ exports.addTracingMiddleware = (tracer) => {
86
65
  };
87
66
  exports.nameSpanOperationMiddleware = (operationType, operationName) => {
88
67
  return function nameSpanOperation(ctx, next) {
89
- var _a;
90
- (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan.setOperationName(`${operationType}:${operationName}`);
68
+ var _a, _b;
69
+ ctx.requestHandlerName = `${operationType}:${operationName}`;
70
+ (_b = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan) === null || _b === void 0 ? void 0 : _b.setOperationName(ctx.requestHandlerName);
91
71
  return next();
92
72
  };
93
73
  };
@@ -95,13 +75,13 @@ exports.traceUserLandRemainingPipelineMiddleware = () => {
95
75
  return async function traceUserLandRemainingPipeline(ctx, next) {
96
76
  const tracingCtx = ctx.tracing;
97
77
  ctx.tracing = undefined;
98
- const span = tracingCtx.currentSpan;
78
+ const span = tracingCtx === null || tracingCtx === void 0 ? void 0 : tracingCtx.currentSpan;
99
79
  const userLandTracer = ctx.vtex.tracer;
100
80
  userLandTracer.setFallbackSpan(span);
101
81
  userLandTracer.lockFallbackSpan();
102
82
  const startTime = process.hrtime();
103
83
  try {
104
- span.log({ event: "user-middlewares-start" /* USER_MIDDLEWARES_START */ });
84
+ span === null || span === void 0 ? void 0 : span.log({ event: "user-middlewares-start" /* USER_MIDDLEWARES_START */ });
105
85
  await next();
106
86
  }
107
87
  catch (err) {
@@ -109,7 +89,7 @@ exports.traceUserLandRemainingPipelineMiddleware = () => {
109
89
  throw err;
110
90
  }
111
91
  finally {
112
- span.log({
92
+ span === null || span === void 0 ? void 0 : span.log({
113
93
  event: "user-middlewares-finish" /* USER_MIDDLEWARES_FINISH */,
114
94
  ["user-middlewares-duration" /* USER_MIDDLEWARES_DURATION */]: utils_2.hrToMillis(process.hrtime(startTime)),
115
95
  });
@@ -117,3 +97,11 @@ exports.traceUserLandRemainingPipelineMiddleware = () => {
117
97
  }
118
98
  };
119
99
  };
100
+ function shouldTrace(ctx, rootSpan) {
101
+ var _a, _b, _c;
102
+ /** Should trace if path isnt blacklisted and sampling decision came from the edge
103
+ * ((rootSpan as any).isSampled. returns whether or not this span context was sampled
104
+ * There is a cast to bypass opentracing typescript
105
+ */
106
+ return !PATHS_BLACKLISTED_FOR_TRACING.includes(ctx.request.path) && ((_c = (_b = (_a = rootSpan).isSampled) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : false);
107
+ }
@@ -13,6 +13,7 @@ const MetricsAccumulator_1 = require("../../metrics/MetricsAccumulator");
13
13
  const loaders_1 = require("../loaders");
14
14
  const console_1 = require("../logger/console");
15
15
  const logger_1 = require("../logger/logger");
16
+ const requestMetricsMiddleware_1 = require("../metrics/requestMetricsMiddleware");
16
17
  const TracerSingleton_1 = require("../tracing/TracerSingleton");
17
18
  const tracingMiddlewares_1 = require("../tracing/tracingMiddlewares");
18
19
  const listeners_1 = require("./listeners");
@@ -157,6 +158,7 @@ exports.startWorker = (serviceJSON) => {
157
158
  .use(error_1.error)
158
159
  .use(middlewares_1.prometheusLoggerMiddleware())
159
160
  .use(tracingMiddlewares_1.addTracingMiddleware(tracer))
161
+ .use(requestMetricsMiddleware_1.addRequestMetricsMiddleware())
160
162
  .use(middlewares_1.addMetricsLoggerMiddleware())
161
163
  .use(rateLimit_1.concurrentRateLimiter((_a = serviceJSON === null || serviceJSON === void 0 ? void 0 : serviceJSON.rateLimitPerReplica) === null || _a === void 0 ? void 0 : _a.concurrent))
162
164
  .use(koa_compress_1.default())
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.whoAmIHandler = ({ events, routes, }) => (ctx) => {
4
- var _a;
5
- (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan.setOperationName('builtin:whoami');
4
+ var _a, _b;
5
+ (_b = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan) === null || _b === void 0 ? void 0 : _b.setOperationName('builtin:whoami');
6
6
  ctx.status = 200;
7
7
  ctx.body = {
8
8
  events,
@@ -11,8 +11,8 @@ exports.whoAmIHandler = ({ events, routes, }) => (ctx) => {
11
11
  ctx.set('Cache-Control', 'public, max-age=86400'); // cache for 24 hours
12
12
  };
13
13
  exports.healthcheckHandler = ({ events, routes, }) => (ctx) => {
14
- var _a;
15
- (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan.setOperationName('builtin:healthcheck');
14
+ var _a, _b;
15
+ (_b = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan) === null || _b === void 0 ? void 0 : _b.setOperationName('builtin:healthcheck');
16
16
  ctx.status = 200;
17
17
  ctx.body = {
18
18
  events,
@@ -20,8 +20,8 @@ exports.healthcheckHandler = ({ events, routes, }) => (ctx) => {
20
20
  };
21
21
  };
22
22
  exports.metricsLoggerHandler = (ctx) => {
23
- var _a;
24
- (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan.setOperationName('builtin:metrics-logger');
23
+ var _a, _b;
24
+ (_b = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan) === null || _b === void 0 ? void 0 : _b.setOperationName('builtin:metrics-logger');
25
25
  ctx.status = 200;
26
26
  ctx.body = ctx.metricsLogger.getSummaries();
27
27
  };
@@ -34,7 +34,7 @@ exports.prometheusLoggerMiddleware = () => {
34
34
  }
35
35
  await eventLoopLagMeasurer.updateInstrumentsAndReset();
36
36
  ctx.set('Content-Type', prom_client_1.register.contentType);
37
- ctx.body = prom_client_1.register.metrics();
37
+ ctx.body = await prom_client_1.register.metrics();
38
38
  ctx.status = 200;
39
39
  };
40
40
  };
@@ -33,8 +33,9 @@ exports.getDependenciesSettings = async (apps, assets, tracingConfig) => {
33
33
  };
34
34
  exports.getServiceSettings = () => {
35
35
  return async function settingsContext(ctx, next) {
36
+ var _a;
36
37
  const { clients: { apps, assets }, } = ctx;
37
- const rootSpan = ctx.tracing.currentSpan;
38
+ const rootSpan = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan;
38
39
  const dependenciesSettings = await exports.getDependenciesSettings(apps, assets, { tracing: { rootSpan } });
39
40
  // TODO: for now returning all settings, but the ideia is to do merge
40
41
  ctx.vtex.settings = dependenciesSettings;
@@ -10,10 +10,10 @@ const STATUS_TRACK = 'statusTrack';
10
10
  exports.isStatusTrack = (message) => message === STATUS_TRACK;
11
11
  exports.isStatusTrackBroadcast = (message) => message === BROADCAST_STATUS_TRACK;
12
12
  exports.statusTrackHandler = async (ctx) => {
13
- var _a, _b;
14
- (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan.setOperationName('builtin:status-track');
13
+ var _a, _b, _c;
14
+ (_b = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.currentSpan) === null || _b === void 0 ? void 0 : _b.setOperationName('builtin:status-track');
15
15
  if (!constants_1.LINKED) {
16
- (_b = process.send) === null || _b === void 0 ? void 0 : _b.call(process, BROADCAST_STATUS_TRACK);
16
+ (_c = process.send) === null || _c === void 0 ? void 0 : _c.call(process, BROADCAST_STATUS_TRACK);
17
17
  }
18
18
  ctx.body = [];
19
19
  return;
@@ -21,7 +21,7 @@ declare type ServerTiming = Record<string, string>;
21
21
  export declare type Maybe<T> = T | null | undefined;
22
22
  export interface TracingContext {
23
23
  tracer: Tracer;
24
- currentSpan: Span;
24
+ currentSpan: Span | undefined;
25
25
  }
26
26
  export interface Context<T extends IOClients> {
27
27
  clients: T;
@@ -29,6 +29,7 @@ export interface Context<T extends IOClients> {
29
29
  timings: Record<string, [number, number]>;
30
30
  metrics: Record<string, [number, number]>;
31
31
  previousTimerStart: [number, number];
32
+ requestHandlerName?: string;
32
33
  serverTiming?: ServerTiming;
33
34
  tracing?: TracingContext;
34
35
  }
@@ -1,2 +1,2 @@
1
1
  import { IOContext, TracingContext } from '../typings';
2
- export declare const prepareHandlerCtx: (header: any, tracingContext: TracingContext) => Pick<IOContext, "host" | "serverTiming" | "locale" | "account" | "workspace" | "region" | "logger" | "requestId" | "operationId" | "production" | "platform" | "authToken" | "adminUserAuthToken" | "storeUserAuthToken" | "product" | "recorder" | "userAgent" | "segmentToken" | "sessionToken" | "janusEnv" | "eventInfo" | "tenant" | "binding" | "cancellation" | "settings" | "tracer">;
2
+ export declare const prepareHandlerCtx: (header: any, tracingContext?: TracingContext | undefined) => Pick<IOContext, "host" | "serverTiming" | "locale" | "account" | "workspace" | "region" | "logger" | "requestId" | "operationId" | "production" | "platform" | "authToken" | "adminUserAuthToken" | "storeUserAuthToken" | "product" | "recorder" | "userAgent" | "segmentToken" | "sessionToken" | "janusEnv" | "eventInfo" | "tenant" | "binding" | "cancellation" | "settings" | "tracer">;
@@ -29,7 +29,7 @@ exports.prepareHandlerCtx = (header, tracingContext) => {
29
29
  segmentToken: header[constants_1.SEGMENT_HEADER],
30
30
  sessionToken: header[constants_1.SESSION_HEADER],
31
31
  tenant: header[constants_1.TENANT_HEADER] ? tenant_1.parseTenantHeaderValue(header[constants_1.TENANT_HEADER]) : undefined,
32
- tracer: new UserLandTracer_1.UserLandTracer(tracingContext.tracer, tracingContext.currentSpan),
32
+ tracer: new UserLandTracer_1.UserLandTracer(tracingContext === null || tracingContext === void 0 ? void 0 : tracingContext.tracer, tracingContext === null || tracingContext === void 0 ? void 0 : tracingContext.currentSpan),
33
33
  userAgent: process.env.VTEX_APP_ID || '',
34
34
  workspace: header[constants_1.WORKSPACE_HEADER],
35
35
  };
@@ -12,6 +12,11 @@ export declare const enum ErrorReportLogFields {
12
12
  */
13
13
  ERROR_KIND = "error.kind",
14
14
  ERROR_ID = "error.id",
15
+ ERROR_MESSAGE = "error.message",
16
+ ERROR_CODE = "error.code",
17
+ ERROR_STACK = "error.stack",
18
+ ERROR_METADATA_REPORT_COUNT = "error.metadata.reportCount",
19
+ ERROR_METADATA_METRICS_INSTANTIATION_TIME = "error.metadata.metrics.InstantiationTime",
15
20
  /**
16
21
  * VTEX's Infra errors adds error details to the response
17
22
  * The following log fields are supposed to hold these fields
@@ -51,7 +51,9 @@ export declare const enum CustomHttpTags {
51
51
  HTTP_MEMORY_CACHE_RESULT = "http.cache.memory",
52
52
  HTTP_MEMOIZATION_CACHE_RESULT = "http.cache.memoization",
53
53
  HTTP_DISK_CACHE_RESULT = "http.cache.disk",
54
- HTTP_ROUTER_CACHE_RESULT = "http.cache.router"
54
+ HTTP_ROUTER_CACHE_RESULT = "http.cache.router",
55
+ HTTP_RETRY_ERROR_CODE = "http.retry.error.code",
56
+ HTTP_RETRY_COUNT = "http.retry.count"
55
57
  }
56
58
  export declare const UserlandTags: {
57
59
  SPAN_KIND: "span.kind";
@@ -1,10 +1,10 @@
1
1
  import { Span, SpanContext, SpanOptions, Tracer } from 'opentracing';
2
2
  export interface IUserLandTracer {
3
- traceId: string;
3
+ traceId?: string;
4
4
  isTraceSampled: boolean;
5
5
  startSpan: Tracer['startSpan'];
6
6
  inject: Tracer['inject'];
7
- fallbackSpanContext: () => SpanContext;
7
+ fallbackSpanContext: () => SpanContext | undefined;
8
8
  }
9
9
  export declare const createTracingContextFromCarrier: (newSpanName: string, carrier: Record<string, any>) => {
10
10
  span: Span;
@@ -15,13 +15,13 @@ export declare class UserLandTracer implements IUserLandTracer {
15
15
  private fallbackSpan;
16
16
  private fallbackSpanLock;
17
17
  private _isSampled;
18
- private _traceId;
19
- constructor(tracer: Tracer, fallbackSpan: Span);
20
- get traceId(): string;
18
+ private _traceId?;
19
+ constructor(tracer: Tracer, fallbackSpan?: Span);
20
+ get traceId(): string | undefined;
21
21
  get isTraceSampled(): boolean;
22
22
  lockFallbackSpan(): void;
23
- setFallbackSpan(newSpan: Span): void;
23
+ setFallbackSpan(newSpan?: Span): void;
24
24
  startSpan(name: string, options?: SpanOptions): Span;
25
25
  inject(spanContext: SpanContext | Span, format: string, carrier: any): void;
26
- fallbackSpanContext(): SpanContext;
26
+ fallbackSpanContext(): SpanContext | undefined;
27
27
  }
@@ -49,7 +49,8 @@ class UserLandTracer {
49
49
  return this.tracer.inject(spanContext, format, carrier);
50
50
  }
51
51
  fallbackSpanContext() {
52
- return this.fallbackSpan.context();
52
+ var _a;
53
+ return (_a = this.fallbackSpan) === null || _a === void 0 ? void 0 : _a.context();
53
54
  }
54
55
  }
55
56
  exports.UserLandTracer = UserLandTracer;
@@ -26,6 +26,6 @@ export declare class ErrorReport extends ErrorReportBase {
26
26
  * instance on the provided Span. If a logger is provided and the
27
27
  * span is part of a **sampled** trace, then the error will be logged.
28
28
  */
29
- injectOnSpan(span: Span, logger?: IOContext['logger']): this;
29
+ injectOnSpan(span?: Span, logger?: IOContext['logger']): this;
30
30
  private shouldLogToSplunk;
31
31
  }
@@ -34,8 +34,8 @@ class ErrorReport extends node_error_report_1.ErrorReportBase {
34
34
  * span is part of a **sampled** trace, then the error will be logged.
35
35
  */
36
36
  injectOnSpan(span, logger) {
37
- var _a;
38
- span.setTag(__1.TracingTags.ERROR, 'true');
37
+ var _a, _b, _c, _d;
38
+ span === null || span === void 0 ? void 0 : span.setTag(__1.TracingTags.ERROR, 'true');
39
39
  const indexedLogs = {
40
40
  ["error.kind" /* ERROR_KIND */]: this.kind,
41
41
  ["error.id" /* ERROR_ID */]: this.metadata.errorId,
@@ -47,7 +47,15 @@ class ErrorReport extends node_error_report_1.ErrorReportBase {
47
47
  indexedLogs["error.server.request_id" /* ERROR_SERVER_REQUEST_ID */] = this.parsedInfo.response.data.requestId;
48
48
  }
49
49
  const serializableError = this.toObject();
50
- span.log({ event: 'error', ...indexedLogs, error: serializableError });
50
+ span === null || span === void 0 ? void 0 : span.log({
51
+ event: 'error',
52
+ ...indexedLogs,
53
+ ["error.message" /* ERROR_MESSAGE */]: serializableError.message,
54
+ ["error.metadata.metrics.InstantiationTime" /* ERROR_METADATA_METRICS_INSTANTIATION_TIME */]: (_b = serializableError.metadata) === null || _b === void 0 ? void 0 : _b.reportCount,
55
+ ["error.metadata.reportCount" /* ERROR_METADATA_REPORT_COUNT */]: (_d = (_c = serializableError.metadata) === null || _c === void 0 ? void 0 : _c.metrics) === null || _d === void 0 ? void 0 : _d.instantiationTime,
56
+ ["error.stack" /* ERROR_STACK */]: serializableError.stack,
57
+ ["error.code" /* ERROR_CODE */]: serializableError.code,
58
+ });
51
59
  if (logger && this.shouldLogToSplunk(span)) {
52
60
  logger.error(serializableError);
53
61
  this.markErrorAsReported();
@@ -55,7 +63,8 @@ class ErrorReport extends node_error_report_1.ErrorReportBase {
55
63
  return this;
56
64
  }
57
65
  shouldLogToSplunk(span) {
58
- return !this.isErrorReported() && utils_1.getTraceInfo(span).isSampled;
66
+ var _a;
67
+ return !this.isErrorReported() && ((_a = utils_1.getTraceInfo(span)) === null || _a === void 0 ? void 0 : _a.isSampled);
59
68
  }
60
69
  }
61
70
  exports.ErrorReport = ErrorReport;
@@ -1,9 +1,9 @@
1
1
  import { Span } from 'opentracing';
2
2
  export interface TraceInfo {
3
- traceId: string;
3
+ traceId?: string;
4
4
  isSampled: boolean;
5
5
  }
6
- export declare function getTraceInfo(span: Span): TraceInfo;
6
+ export declare function getTraceInfo(span?: Span): TraceInfo;
7
7
  /**
8
8
  * Do a shallow copy of a headers object and redacts sensitive information.
9
9
  *
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const node_error_report_1 = require("@vtex/node-error-report");
4
4
  function getTraceInfo(span) {
5
5
  var _a, _b, _c;
6
- const spanContext = span.context();
6
+ const spanContext = span === null || span === void 0 ? void 0 : span.context();
7
7
  return {
8
- isSampled: (_c = (_b = (_a = spanContext).isSampled) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : false,
9
- traceId: spanContext.toTraceId(),
8
+ isSampled: (_c = (_b = (_a = spanContext) === null || _a === void 0 ? void 0 : _a.isSampled) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : false,
9
+ traceId: spanContext === null || spanContext === void 0 ? void 0 : spanContext.toTraceId(),
10
10
  };
11
11
  }
12
12
  exports.getTraceInfo = getTraceInfo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/api",
3
- "version": "6.46.0-beta.0",
3
+ "version": "6.46.0",
4
4
  "description": "VTEX I/O API client",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -54,7 +54,7 @@
54
54
  "agentkeepalive": "^4.0.2",
55
55
  "apollo-server-errors": "^2.2.1",
56
56
  "archiver": "^3.0.0",
57
- "axios": "0.27.2",
57
+ "axios": "^0.21.1",
58
58
  "axios-retry": "^3.1.2",
59
59
  "bluebird": "^3.5.4",
60
60
  "chalk": "^2.4.2",
@@ -65,7 +65,7 @@
65
65
  "fs-extra": "^7.0.0",
66
66
  "graphql": "^14.5.8",
67
67
  "graphql-tools": "^4.0.6",
68
- "graphql-upload": "^8.1.0",
68
+ "graphql-upload": "^13.0.0",
69
69
  "jaeger-client": "^3.18.0",
70
70
  "js-base64": "^2.5.1",
71
71
  "koa": "^2.11.0",
@@ -76,7 +76,7 @@
76
76
  "mime-types": "^2.1.12",
77
77
  "opentracing": "^0.14.4",
78
78
  "p-limit": "^2.2.0",
79
- "prom-client": "^12.0.0",
79
+ "prom-client": "^14.2.0",
80
80
  "qs": "^6.5.1",
81
81
  "querystring": "^0.2.0",
82
82
  "ramda": "^0.26.0",