@vtex/api 6.46.0-beta.0 → 6.46.1
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.
- package/CHANGELOG.md +75 -5
- package/lib/HttpClient/HttpClient.d.ts +3 -1
- package/lib/HttpClient/HttpClient.js +5 -3
- package/lib/HttpClient/middlewares/cache.d.ts +1 -1
- package/lib/HttpClient/middlewares/cache.js +66 -35
- package/lib/HttpClient/middlewares/memoization.js +7 -6
- package/lib/HttpClient/middlewares/request/index.js +5 -4
- package/lib/HttpClient/middlewares/request/setupAxios/interceptors/exponentialBackoff.js +6 -1
- package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/index.js +8 -6
- package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/spanSetup.d.ts +2 -2
- package/lib/HttpClient/middlewares/request/setupAxios/interceptors/tracing/spanSetup.js +6 -6
- package/lib/HttpClient/middlewares/tracing.js +13 -11
- package/lib/HttpClient/typings.d.ts +14 -1
- package/lib/clients/infra/VBase.d.ts +2 -2
- package/lib/clients/infra/VBase.js +1 -1
- package/lib/responses.d.ts +1 -0
- package/lib/service/loaders.js +2 -2
- package/lib/service/logger/logger.d.ts +0 -6
- package/lib/service/logger/logger.js +0 -14
- package/lib/service/metrics/requestMetricsMiddleware.d.ts +2 -0
- package/lib/service/metrics/requestMetricsMiddleware.js +44 -0
- package/lib/service/tracing/TracerSingleton.js +0 -6
- package/lib/service/tracing/tracingMiddlewares.js +23 -35
- package/lib/service/worker/index.js +2 -0
- package/lib/service/worker/runtime/builtIn/handlers.js +6 -6
- package/lib/service/worker/runtime/builtIn/middlewares.js +1 -1
- package/lib/service/worker/runtime/http/middlewares/settings.js +2 -1
- package/lib/service/worker/runtime/statusTrack.js +3 -3
- package/lib/service/worker/runtime/typings.d.ts +2 -1
- package/lib/service/worker/runtime/utils/context.d.ts +1 -1
- package/lib/service/worker/runtime/utils/context.js +1 -1
- package/lib/tracing/LogFields.d.ts +5 -0
- package/lib/tracing/Tags.d.ts +3 -1
- package/lib/tracing/UserLandTracer.d.ts +7 -7
- package/lib/tracing/UserLandTracer.js +2 -1
- package/lib/tracing/errorReporting/ErrorReport.d.ts +1 -1
- package/lib/tracing/errorReporting/ErrorReport.js +13 -4
- package/lib/tracing/utils.d.ts +2 -2
- package/lib/tracing/utils.js +3 -3
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,9 +7,72 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [6.46.
|
|
10
|
+
## [6.46.1] - 2024-01-31
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
### Fixed
|
|
13
|
+
- Return type for the saveJSON method of VBase client
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Increase timeout for messagesGraphQL client
|
|
17
|
+
|
|
18
|
+
## [6.46.0] - 2023-10-25
|
|
19
|
+
### Added
|
|
20
|
+
- Add disk cache steps and retry count to tracing
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Stop updating client cache when revalidated and still expired
|
|
24
|
+
|
|
25
|
+
## [6.45.24] - 2023-10-05
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Allow disabling memoization for all requests of a client
|
|
29
|
+
|
|
30
|
+
## [6.45.23] - 2023-10-04
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- Fix metrics being restricted by tracing restrictions
|
|
34
|
+
|
|
35
|
+
## [6.45.22] - 2023-09-20
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- Remove reference of how to search for the app's errors on now-defunct Splunk.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Error tracing spans not logging the details.
|
|
44
|
+
|
|
45
|
+
## [6.45.21] - 2023-09-19
|
|
46
|
+
### Changed
|
|
47
|
+
- Fix tracingMiddleware shouldTrace decision
|
|
48
|
+
- Fix HttpClient tracing based on sampling decision
|
|
49
|
+
|
|
50
|
+
## [6.45.21-beta.2] - 2023-09-19
|
|
51
|
+
|
|
52
|
+
## [6.45.21-beta.1] - 2023-09-07
|
|
53
|
+
|
|
54
|
+
## [6.45.21-beta.0] - 2023-09-04
|
|
55
|
+
|
|
56
|
+
## [6.45.21-beta] - 2023-09-04
|
|
57
|
+
|
|
58
|
+
## [6.45.20] - 2023-08-30
|
|
59
|
+
### Changed
|
|
60
|
+
- Remove sampling decision from runtime
|
|
61
|
+
- Only create tracing spans when they come from router
|
|
62
|
+
|
|
63
|
+
## [6.45.20-beta] - 2023-08-30
|
|
64
|
+
|
|
65
|
+
## [6.45.19] - 2023-07-12
|
|
66
|
+
### Changed
|
|
67
|
+
- Bump `graphql-upload` version
|
|
68
|
+
|
|
69
|
+
## [6.45.18] - 2023-07-07
|
|
70
|
+
### Fixed
|
|
71
|
+
- Metrics to prometheus
|
|
72
|
+
|
|
73
|
+
## [6.45.16] - 2023-07-03
|
|
74
|
+
### Changed
|
|
75
|
+
- Bump `prom-client` version
|
|
13
76
|
|
|
14
77
|
## [6.45.15] - 2023-02-01
|
|
15
78
|
### Changed
|
|
@@ -1761,9 +1824,16 @@ instead
|
|
|
1761
1824
|
- `HttpClient` now adds `'Accept-Encoding': 'gzip'` header by default.
|
|
1762
1825
|
|
|
1763
1826
|
|
|
1764
|
-
[Unreleased]: https://github.com/vtex/node-vtex-api/compare/v6.
|
|
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
|
|
1827
|
+
[Unreleased]: https://github.com/vtex/node-vtex-api/compare/v6.45.22...HEAD
|
|
1767
1828
|
[6.45.15]: https://github.com/vtex/node-vtex-api/compare/v6.45.14...v6.45.15
|
|
1768
1829
|
[6.45.14]: https://github.com/vtex/node-vtex-api/compare/v6.45.13...v6.45.14
|
|
1769
1830
|
[6.45.13]: https://github.com/vtex/node-vtex-api/compare/v6.45.12...v6.45.13
|
|
1831
|
+
|
|
1832
|
+
[6.45.22]: https://github.com/vtex/node-vtex-api/compare/v6.45.21...v6.45.22
|
|
1833
|
+
[6.45.21]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.2...v6.45.21
|
|
1834
|
+
[6.45.21-beta.2]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.1...v6.45.21-beta.2
|
|
1835
|
+
[6.45.21-beta.1]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta.0...v6.45.21-beta.1
|
|
1836
|
+
[6.45.21-beta.0]: https://github.com/vtex/node-vtex-api/compare/v6.45.21-beta...v6.45.21-beta.0
|
|
1837
|
+
[6.45.21-beta]: https://github.com/vtex/node-vtex-api/compare/v6.45.20...v6.45.21-beta
|
|
1838
|
+
[6.45.20]: https://github.com/vtex/node-vtex-api/compare/v6.45.20-beta...v6.45.20
|
|
1839
|
+
[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
|
|
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:
|
|
100
|
-
memoizable:
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
73
|
-
|
|
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 =
|
|
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
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
span
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
export declare const injectResponseInfoOnSpan: (span: Span, response: AxiosResponse<any
|
|
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
|
-
|
|
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
|
|
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;
|
|
@@ -3,7 +3,7 @@ import { IncomingMessage } from 'http';
|
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
4
|
import { Logger } from './../../service/logger/logger';
|
|
5
5
|
import { InstanceOptions, IOResponse, RequestTracingConfig } from '../../HttpClient';
|
|
6
|
-
import { BucketMetadata, FileListItem } from '../../responses';
|
|
6
|
+
import { BucketMetadata, FileListItem, VBaseSaveResponse } from '../../responses';
|
|
7
7
|
import { IOContext } from '../../service/worker/runtime/typings';
|
|
8
8
|
import { InfraClient } from './InfraClient';
|
|
9
9
|
export declare class VBase extends InfraClient {
|
|
@@ -20,7 +20,7 @@ export declare class VBase extends InfraClient {
|
|
|
20
20
|
getFileStream: (bucket: string, path: string, tracingConfig?: RequestTracingConfig | undefined) => Promise<IncomingMessage>;
|
|
21
21
|
saveFile: (bucket: string, path: string, stream: Readable, gzip?: boolean, ttl?: number | undefined, tracingConfig?: RequestTracingConfig | undefined, ifMatch?: string | undefined) => Promise<void>;
|
|
22
22
|
getFileMetadata: (bucket: string, path: string, tracingConfig?: RequestTracingConfig | undefined) => Promise<IOResponse<void>>;
|
|
23
|
-
saveJSON: <T>(bucket: string, path: string, data: T, tracingConfig?: RequestTracingConfig | undefined, ifMatch?: string | undefined) => Promise<
|
|
23
|
+
saveJSON: <T>(bucket: string, path: string, data: T, tracingConfig?: RequestTracingConfig | undefined, ifMatch?: string | undefined) => Promise<VBaseSaveResponse>;
|
|
24
24
|
saveZippedContent: (bucket: string, path: string, stream: Readable, tracingConfig?: RequestTracingConfig | undefined, ifMatch?: string | undefined) => Promise<void>;
|
|
25
25
|
deleteFile: (bucket: string, path: string, tracingConfig?: RequestTracingConfig | undefined, ifMatch?: string | undefined) => Promise<IOResponse<void>>;
|
|
26
26
|
getConflicts: <T>(bucket: string, tracingConfig?: RequestTracingConfig | undefined) => Promise<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,
|
package/lib/responses.d.ts
CHANGED
package/lib/service/loaders.js
CHANGED
|
@@ -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
|
|
21
|
-
|
|
22
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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
|
|
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
|
package/lib/tracing/Tags.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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;
|
package/lib/tracing/utils.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Span } from 'opentracing';
|
|
2
2
|
export interface TraceInfo {
|
|
3
|
-
traceId
|
|
3
|
+
traceId?: string;
|
|
4
4
|
isSampled: boolean;
|
|
5
5
|
}
|
|
6
|
-
export declare function getTraceInfo(span
|
|
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
|
*
|
package/lib/tracing/utils.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "6.46.1",
|
|
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.
|
|
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": "^
|
|
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": "^
|
|
79
|
+
"prom-client": "^14.2.0",
|
|
80
80
|
"qs": "^6.5.1",
|
|
81
81
|
"querystring": "^0.2.0",
|
|
82
82
|
"ramda": "^0.26.0",
|