@outputai/http 0.4.1-dev.c0b98d8.0 → 0.4.1-next.43c9293.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.
- package/dist/cost.d.ts +2 -9
- package/dist/cost.js +5 -4
- package/dist/cost.spec.js +42 -48
- package/dist/fetch/index.d.ts +0 -1
- package/dist/fetch/index.js +3 -9
- package/dist/fetch/index.spec.js +2 -7
- package/dist/fetch/logger.d.ts +3 -24
- package/dist/fetch/logger.js +16 -58
- package/dist/fetch/logger.spec.js +38 -90
- package/package.json +2 -2
package/dist/cost.d.ts
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { KyResponse } from 'ky';
|
|
2
|
-
export type RequestCost = {
|
|
3
|
-
total: number;
|
|
4
|
-
components?: Array<{
|
|
5
|
-
name: string;
|
|
6
|
-
value: number;
|
|
7
|
-
}>;
|
|
8
|
-
};
|
|
9
2
|
/**
|
|
10
3
|
* Attach cost information to the trace of an HTTP Request using the response
|
|
11
4
|
*
|
|
12
5
|
* @param response - The response of the HTTP Request to attach the information
|
|
13
|
-
* @param
|
|
6
|
+
* @param value - The price of the HTTP request
|
|
14
7
|
* @returns
|
|
15
8
|
*/
|
|
16
|
-
export declare const addRequestCost: (response: KyResponse | Response,
|
|
9
|
+
export declare const addRequestCost: (response: KyResponse | Response, value: number) => void;
|
package/dist/cost.js
CHANGED
|
@@ -4,15 +4,16 @@ import { requestIdSymbol } from './consts.js';
|
|
|
4
4
|
* Attach cost information to the trace of an HTTP Request using the response
|
|
5
5
|
*
|
|
6
6
|
* @param response - The response of the HTTP Request to attach the information
|
|
7
|
-
* @param
|
|
7
|
+
* @param value - The price of the HTTP request
|
|
8
8
|
* @returns
|
|
9
9
|
*/
|
|
10
|
-
export const addRequestCost = (response,
|
|
10
|
+
export const addRequestCost = (response, value) => {
|
|
11
11
|
const eventId = Reflect.get(response, requestIdSymbol);
|
|
12
12
|
if (!eventId) {
|
|
13
13
|
console.warn('addRequestCost(): The "response" argument did not originate from @outputai/http, no costs were added.');
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const attribute = new Tracing.Attribute.HTTPRequestCost(response.url, eventId, value);
|
|
17
|
+
Tracing.addEventAttribute({ eventId, attribute });
|
|
18
|
+
emitEvent('cost:http:request', attribute);
|
|
18
19
|
};
|
package/dist/cost.spec.js
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { requestIdSymbol } from './consts.js';
|
|
3
|
-
vi.mock('@outputai/core/sdk_activity_integration', () =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
4
|
+
class HTTPRequestCost {
|
|
5
|
+
static TYPE = 'http:request:cost';
|
|
6
|
+
type = HTTPRequestCost.TYPE;
|
|
7
|
+
url;
|
|
8
|
+
requestId;
|
|
9
|
+
total;
|
|
10
|
+
constructor(url, requestId, total) {
|
|
11
|
+
this.url = url;
|
|
12
|
+
this.requestId = requestId;
|
|
13
|
+
this.total = total;
|
|
8
14
|
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
Tracing: {
|
|
18
|
+
addEventAttribute: vi.fn(),
|
|
19
|
+
Attribute: {
|
|
20
|
+
HTTPRequestCost
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
emitEvent: vi.fn()
|
|
24
|
+
};
|
|
25
|
+
});
|
|
12
26
|
import { Tracing, emitEvent } from '@outputai/core/sdk_activity_integration';
|
|
13
27
|
import { addRequestCost } from './cost.js';
|
|
14
28
|
const tracing = vi.mocked(Tracing, true);
|
|
@@ -24,7 +38,7 @@ describe('addRequestCost', () => {
|
|
|
24
38
|
});
|
|
25
39
|
it('shortcircuits when the response has no http request id', () => {
|
|
26
40
|
const response = new Response();
|
|
27
|
-
const cost =
|
|
41
|
+
const cost = 1;
|
|
28
42
|
addRequestCost(response, cost);
|
|
29
43
|
expect(console.warn).toHaveBeenCalledWith('addRequestCost(): The "response" argument did not originate from @outputai/http, no costs were added.');
|
|
30
44
|
expect(tracing.addEventAttribute).not.toHaveBeenCalled();
|
|
@@ -33,56 +47,36 @@ describe('addRequestCost', () => {
|
|
|
33
47
|
it('records cost on the trace event when the response carries the request id', () => {
|
|
34
48
|
const response = new Response(undefined, { status: 200 });
|
|
35
49
|
Reflect.set(response, requestIdSymbol, 'evt-cost-1');
|
|
36
|
-
const cost =
|
|
50
|
+
const cost = 2.5;
|
|
37
51
|
addRequestCost(response, cost);
|
|
38
52
|
expect(console.warn).not.toHaveBeenCalled();
|
|
39
53
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
40
54
|
eventId: 'evt-cost-1',
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
cost
|
|
55
|
+
attribute: expect.objectContaining({
|
|
56
|
+
type: Tracing.Attribute.HTTPRequestCost.TYPE,
|
|
57
|
+
url: response.url,
|
|
58
|
+
requestId: 'evt-cost-1',
|
|
59
|
+
total: cost
|
|
60
|
+
})
|
|
48
61
|
});
|
|
62
|
+
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
63
|
+
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
49
64
|
});
|
|
50
|
-
it('
|
|
65
|
+
it('records zero cost on the trace event', () => {
|
|
51
66
|
const response = new Response();
|
|
52
67
|
Reflect.set(response, requestIdSymbol, 'evt-cost-2');
|
|
53
|
-
const cost =
|
|
54
|
-
total: 10,
|
|
55
|
-
components: [
|
|
56
|
-
{ name: 'input', value: 3 },
|
|
57
|
-
{ name: 'output', value: 7 }
|
|
58
|
-
]
|
|
59
|
-
};
|
|
68
|
+
const cost = 0;
|
|
60
69
|
addRequestCost(response, cost);
|
|
61
70
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
62
71
|
eventId: 'evt-cost-2',
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cost
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it('forwards an empty components array to tracing', () => {
|
|
73
|
-
const response = new Response();
|
|
74
|
-
Reflect.set(response, requestIdSymbol, 'evt-cost-3');
|
|
75
|
-
const cost = { total: 1, components: [] };
|
|
76
|
-
addRequestCost(response, cost);
|
|
77
|
-
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
78
|
-
eventId: 'evt-cost-3',
|
|
79
|
-
name: Tracing.Attribute.COST,
|
|
80
|
-
value: cost
|
|
81
|
-
});
|
|
82
|
-
expect(emit).toHaveBeenCalledWith('cost:http:request', {
|
|
83
|
-
requestId: 'evt-cost-3',
|
|
84
|
-
url: response.url,
|
|
85
|
-
cost
|
|
72
|
+
attribute: expect.objectContaining({
|
|
73
|
+
type: Tracing.Attribute.HTTPRequestCost.TYPE,
|
|
74
|
+
url: response.url,
|
|
75
|
+
requestId: 'evt-cost-2',
|
|
76
|
+
total: cost
|
|
77
|
+
})
|
|
86
78
|
});
|
|
79
|
+
const attribute = tracing.addEventAttribute.mock.calls[0][0].attribute;
|
|
80
|
+
expect(emit).toHaveBeenCalledWith('cost:http:request', attribute);
|
|
87
81
|
});
|
|
88
82
|
});
|
package/dist/fetch/index.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ RequestInit };
|
|
|
13
13
|
* Behaves the same as any fetch function except:
|
|
14
14
|
* - Sets a request header called `x-request--trace-id` with a random UUID;
|
|
15
15
|
* - Sends the request, response, error and/or failure to the Trace system;
|
|
16
|
-
* - Emits a `http:request` event on every call (success, http_error, network_error).
|
|
17
16
|
*
|
|
18
17
|
* @see {@link https://fetch.spec.whatwg.org/}
|
|
19
18
|
* @param input - URL string, URL object or Request object (undici's or Node's)
|
package/dist/fetch/index.js
CHANGED
|
@@ -15,7 +15,6 @@ export * as undici from 'undici';
|
|
|
15
15
|
* Behaves the same as any fetch function except:
|
|
16
16
|
* - Sets a request header called `x-request--trace-id` with a random UUID;
|
|
17
17
|
* - Sends the request, response, error and/or failure to the Trace system;
|
|
18
|
-
* - Emits a `http:request` event on every call (success, http_error, network_error).
|
|
19
18
|
*
|
|
20
19
|
* @see {@link https://fetch.spec.whatwg.org/}
|
|
21
20
|
* @param input - URL string, URL object or Request object (undici's or Node's)
|
|
@@ -30,25 +29,20 @@ export const fetch = async (input, init) => {
|
|
|
30
29
|
const requestId = randomUUID();
|
|
31
30
|
headers.set('x-request-trace-id', requestId);
|
|
32
31
|
const request = new undici.Request(base, { headers });
|
|
33
|
-
const method = request.method;
|
|
34
|
-
const url = request.url;
|
|
35
|
-
const startedAt = performance.now();
|
|
36
32
|
await logRequest({ requestId, request });
|
|
37
33
|
try {
|
|
38
34
|
const response = await undici.fetch(request);
|
|
39
|
-
const durationMs = Math.round(performance.now() - startedAt);
|
|
40
35
|
// This enriches the response of the request id, so it is identifiable later.
|
|
41
36
|
addRequestIdToResponse(response, requestId);
|
|
42
37
|
if (response.status > 399) {
|
|
43
|
-
await logError({ requestId, response
|
|
38
|
+
await logError({ requestId, response });
|
|
44
39
|
return response;
|
|
45
40
|
}
|
|
46
|
-
await logResponse({ requestId, response
|
|
41
|
+
await logResponse({ requestId, response });
|
|
47
42
|
return response;
|
|
48
43
|
}
|
|
49
44
|
catch (error) {
|
|
50
|
-
|
|
51
|
-
logFailure({ requestId, error: error, method, url, durationMs });
|
|
45
|
+
logFailure({ requestId, error: error });
|
|
52
46
|
throw error;
|
|
53
47
|
}
|
|
54
48
|
};
|
package/dist/fetch/index.spec.js
CHANGED
|
@@ -62,13 +62,8 @@ describe('fetch/index', () => {
|
|
|
62
62
|
expect(loggerMock.logRequest.mock.calls[0][0].request.method).toBe('GET');
|
|
63
63
|
expect(loggerMock.logRequest.mock.calls[0][0].request.url).toBe(`${MOCK_ORIGIN}/ok`);
|
|
64
64
|
expect(loggerMock.logResponse).toHaveBeenCalledTimes(1);
|
|
65
|
-
|
|
66
|
-
expect(
|
|
67
|
-
expect(responseCall.response).toBe(response);
|
|
68
|
-
expect(responseCall.method).toBe('GET');
|
|
69
|
-
expect(responseCall.url).toBe(`${MOCK_ORIGIN}/ok`);
|
|
70
|
-
expect(typeof responseCall.durationMs).toBe('number');
|
|
71
|
-
expect(responseCall.durationMs).toBeGreaterThanOrEqual(0);
|
|
65
|
+
expect(loggerMock.logResponse.mock.calls[0][0].requestId).toBe(FIXED_REQUEST_ID);
|
|
66
|
+
expect(loggerMock.logResponse.mock.calls[0][0].response).toBe(response);
|
|
72
67
|
expect(utilsMock.addRequestIdToResponse).toHaveBeenCalledTimes(1);
|
|
73
68
|
expect(utilsMock.addRequestIdToResponse).toHaveBeenCalledWith(response, FIXED_REQUEST_ID);
|
|
74
69
|
expect(loggerMock.logError).not.toHaveBeenCalled();
|
package/dist/fetch/logger.d.ts
CHANGED
|
@@ -12,55 +12,34 @@ export declare const logRequest: ({ requestId, request }: {
|
|
|
12
12
|
}) => Promise<void>;
|
|
13
13
|
/**
|
|
14
14
|
* Sends the trace error event for an http response with error status
|
|
15
|
-
* and emits a `http:request` event with `outcome: 'http_error'`.
|
|
16
15
|
*
|
|
17
16
|
* @param options
|
|
18
17
|
* @param options.requestId - id of the request
|
|
19
18
|
* @param options.response - The HTTP Response object
|
|
20
|
-
* @param options.method - HTTP method of the request
|
|
21
|
-
* @param options.url - URL of the request
|
|
22
|
-
* @param options.durationMs - elapsed time from request issuance to response, in milliseconds
|
|
23
19
|
*/
|
|
24
|
-
export declare const logError: ({ requestId, response
|
|
20
|
+
export declare const logError: ({ requestId, response }: {
|
|
25
21
|
requestId: string;
|
|
26
22
|
response: Response;
|
|
27
|
-
method: string;
|
|
28
|
-
url: string;
|
|
29
|
-
durationMs: number;
|
|
30
23
|
}) => Promise<void>;
|
|
31
24
|
/**
|
|
32
25
|
* Sends the trace end event for an http response
|
|
33
|
-
* and emits a `http:request` event with `outcome: 'success'`.
|
|
34
26
|
*
|
|
35
27
|
* @param {object} options
|
|
36
28
|
* @param options.requestId - id of the request
|
|
37
29
|
* @param {Response} options.response - The HTTP Response object
|
|
38
|
-
* @param options.method - HTTP method of the request
|
|
39
|
-
* @param options.url - URL of the request
|
|
40
|
-
* @param options.durationMs - elapsed time from request issuance to response, in milliseconds
|
|
41
30
|
*/
|
|
42
|
-
export declare const logResponse: ({ requestId, response
|
|
31
|
+
export declare const logResponse: ({ requestId, response }: {
|
|
43
32
|
requestId: string;
|
|
44
33
|
response: Response;
|
|
45
|
-
method: string;
|
|
46
|
-
url: string;
|
|
47
|
-
durationMs: number;
|
|
48
34
|
}) => Promise<void>;
|
|
49
35
|
/**
|
|
50
36
|
* Creates the trace error event for a network/connection failure
|
|
51
|
-
* and emits a `http:request` event with `outcome: 'network_error'`.
|
|
52
37
|
*
|
|
53
38
|
* @param options
|
|
54
39
|
* @param options.requestId - id of the request
|
|
55
40
|
* @param options.error - The error thrown
|
|
56
|
-
* @param options.method - HTTP method of the request
|
|
57
|
-
* @param options.url - URL of the request
|
|
58
|
-
* @param options.durationMs - elapsed time from request issuance to failure, in milliseconds
|
|
59
41
|
*/
|
|
60
|
-
export declare const logFailure: ({ requestId, error
|
|
42
|
+
export declare const logFailure: ({ requestId, error }: {
|
|
61
43
|
requestId: string;
|
|
62
44
|
error: Error;
|
|
63
|
-
method: string;
|
|
64
|
-
url: string;
|
|
65
|
-
durationMs: number;
|
|
66
45
|
}) => void;
|
package/dist/fetch/logger.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tracing
|
|
1
|
+
import { Tracing } from '@outputai/core/sdk_activity_integration';
|
|
2
2
|
import { config } from '../config.js';
|
|
3
3
|
import { parseBody, redactHeaders, serializeError } from './utils.js';
|
|
4
4
|
/**
|
|
@@ -16,84 +16,42 @@ export const logRequest = async ({ requestId, request }) => {
|
|
|
16
16
|
...(config.logVerbose && { headers: redactHeaders(request.headers), body: await parseBody(request) })
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
|
-
Tracing.addEventAttribute({ eventId: requestId,
|
|
19
|
+
Tracing.addEventAttribute({ eventId: requestId, attribute: new Tracing.Attribute.HTTPRequestCount(request.url, requestId) });
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
22
|
* Sends the trace error event for an http response with error status
|
|
23
|
-
* and emits a `http:request` event with `outcome: 'http_error'`.
|
|
24
23
|
*
|
|
25
24
|
* @param options
|
|
26
25
|
* @param options.requestId - id of the request
|
|
27
26
|
* @param options.response - The HTTP Response object
|
|
28
|
-
* @param options.method - HTTP method of the request
|
|
29
|
-
* @param options.url - URL of the request
|
|
30
|
-
* @param options.durationMs - elapsed time from request issuance to response, in milliseconds
|
|
31
27
|
*/
|
|
32
|
-
export const logError = async ({ requestId, response
|
|
33
|
-
|
|
34
|
-
id: requestId, details: {
|
|
35
|
-
status: response.status,
|
|
36
|
-
statusText: response.statusText,
|
|
37
|
-
headers: redactHeaders(response.headers),
|
|
38
|
-
body: await parseBody(response)
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
emitEvent('http:request', {
|
|
42
|
-
requestId,
|
|
43
|
-
method,
|
|
44
|
-
url,
|
|
28
|
+
export const logError = async ({ requestId, response }) => Tracing.addEventError({
|
|
29
|
+
id: requestId, details: {
|
|
45
30
|
status: response.status,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
31
|
+
statusText: response.statusText,
|
|
32
|
+
headers: redactHeaders(response.headers),
|
|
33
|
+
body: await parseBody(response)
|
|
34
|
+
}
|
|
35
|
+
});
|
|
50
36
|
/**
|
|
51
37
|
* Sends the trace end event for an http response
|
|
52
|
-
* and emits a `http:request` event with `outcome: 'success'`.
|
|
53
38
|
*
|
|
54
39
|
* @param {object} options
|
|
55
40
|
* @param options.requestId - id of the request
|
|
56
41
|
* @param {Response} options.response - The HTTP Response object
|
|
57
|
-
* @param options.method - HTTP method of the request
|
|
58
|
-
* @param options.url - URL of the request
|
|
59
|
-
* @param options.durationMs - elapsed time from request issuance to response, in milliseconds
|
|
60
42
|
*/
|
|
61
|
-
export const logResponse = async ({ requestId, response
|
|
62
|
-
|
|
63
|
-
id: requestId, details: {
|
|
64
|
-
status: response.status,
|
|
65
|
-
statusText: response.statusText,
|
|
66
|
-
...(config.logVerbose && { headers: redactHeaders(response.headers), body: await parseBody(response) })
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
emitEvent('http:request', {
|
|
70
|
-
requestId,
|
|
71
|
-
method,
|
|
72
|
-
url,
|
|
43
|
+
export const logResponse = async ({ requestId, response }) => Tracing.addEventEnd({
|
|
44
|
+
id: requestId, details: {
|
|
73
45
|
status: response.status,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
};
|
|
46
|
+
statusText: response.statusText,
|
|
47
|
+
...(config.logVerbose && { headers: redactHeaders(response.headers), body: await parseBody(response) })
|
|
48
|
+
}
|
|
49
|
+
});
|
|
78
50
|
/**
|
|
79
51
|
* Creates the trace error event for a network/connection failure
|
|
80
|
-
* and emits a `http:request` event with `outcome: 'network_error'`.
|
|
81
52
|
*
|
|
82
53
|
* @param options
|
|
83
54
|
* @param options.requestId - id of the request
|
|
84
55
|
* @param options.error - The error thrown
|
|
85
|
-
* @param options.method - HTTP method of the request
|
|
86
|
-
* @param options.url - URL of the request
|
|
87
|
-
* @param options.durationMs - elapsed time from request issuance to failure, in milliseconds
|
|
88
56
|
*/
|
|
89
|
-
export const logFailure = ({ requestId, error
|
|
90
|
-
Tracing.addEventError({ id: requestId, details: serializeError(error) });
|
|
91
|
-
emitEvent('http:request', {
|
|
92
|
-
requestId,
|
|
93
|
-
method,
|
|
94
|
-
url,
|
|
95
|
-
status: undefined,
|
|
96
|
-
durationMs,
|
|
97
|
-
outcome: 'network_error'
|
|
98
|
-
});
|
|
99
|
-
};
|
|
57
|
+
export const logFailure = ({ requestId, error }) => Tracing.addEventError({ id: requestId, details: serializeError(error) });
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
2
|
import { Response, Request } from 'undici';
|
|
3
|
-
vi.mock('@outputai/core/sdk_activity_integration', () =>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
vi.mock('@outputai/core/sdk_activity_integration', () => {
|
|
4
|
+
class HTTPRequestCount {
|
|
5
|
+
static TYPE = 'http:request:count';
|
|
6
|
+
type = HTTPRequestCount.TYPE;
|
|
7
|
+
url;
|
|
8
|
+
requestId;
|
|
9
|
+
constructor(url, requestId) {
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.requestId = requestId;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
Tracing: {
|
|
16
|
+
addEventStart: vi.fn(),
|
|
17
|
+
addEventEnd: vi.fn(),
|
|
18
|
+
addEventError: vi.fn(),
|
|
19
|
+
addEventAttribute: vi.fn(),
|
|
20
|
+
Attribute: {
|
|
21
|
+
HTTPRequestCount
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
import { Tracing } from '@outputai/core/sdk_activity_integration';
|
|
13
27
|
const tracing = vi.mocked(Tracing, true);
|
|
14
|
-
const emit = vi.mocked(emitEvent, true);
|
|
15
28
|
/** Loads logger with optional verbose tracing env so `config.js` is evaluated fresh. */
|
|
16
29
|
async function logLogger(verbose) {
|
|
17
30
|
vi.resetModules();
|
|
@@ -28,15 +41,17 @@ beforeEach(() => {
|
|
|
28
41
|
tracing.addEventEnd.mockClear();
|
|
29
42
|
tracing.addEventError.mockClear();
|
|
30
43
|
tracing.addEventAttribute.mockClear();
|
|
31
|
-
emit.mockClear();
|
|
32
44
|
});
|
|
33
45
|
describe('fetch/logger', () => {
|
|
34
46
|
describe('logRequest', () => {
|
|
35
|
-
const
|
|
47
|
+
const expectRequestCountAttribute = (requestId, url) => {
|
|
36
48
|
expect(tracing.addEventAttribute).toHaveBeenCalledWith({
|
|
37
49
|
eventId: requestId,
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
attribute: expect.objectContaining({
|
|
51
|
+
type: Tracing.Attribute.HTTPRequestCount.TYPE,
|
|
52
|
+
url,
|
|
53
|
+
requestId
|
|
54
|
+
})
|
|
40
55
|
});
|
|
41
56
|
};
|
|
42
57
|
it('records minimal details when verbose is off', async () => {
|
|
@@ -52,14 +67,14 @@ describe('fetch/logger', () => {
|
|
|
52
67
|
url: 'https://api.example.com/r'
|
|
53
68
|
}
|
|
54
69
|
});
|
|
55
|
-
|
|
70
|
+
expectRequestCountAttribute('req-1', 'https://api.example.com/r');
|
|
56
71
|
});
|
|
57
72
|
it('defaults method to GET', async () => {
|
|
58
73
|
const { logRequest } = await logLogger(false);
|
|
59
74
|
const request = new Request('https://x.test');
|
|
60
75
|
await logRequest({ requestId: 'r2', request });
|
|
61
76
|
expect(tracing.addEventStart.mock.calls[0][0].details.method).toBe('GET');
|
|
62
|
-
|
|
77
|
+
expectRequestCountAttribute('r2', 'https://x.test/');
|
|
63
78
|
});
|
|
64
79
|
it('includes redacted headers and parsed body when verbose is on', async () => {
|
|
65
80
|
const { logRequest } = await logLogger(true);
|
|
@@ -84,7 +99,7 @@ describe('fetch/logger', () => {
|
|
|
84
99
|
body: { x: 1 }
|
|
85
100
|
}
|
|
86
101
|
});
|
|
87
|
-
|
|
102
|
+
expectRequestCountAttribute('req-v', 'https://api.example.com/p');
|
|
88
103
|
});
|
|
89
104
|
});
|
|
90
105
|
describe('logError', () => {
|
|
@@ -100,9 +115,7 @@ describe('fetch/logger', () => {
|
|
|
100
115
|
'content-type': 'application/json'
|
|
101
116
|
}
|
|
102
117
|
});
|
|
103
|
-
await logError({
|
|
104
|
-
requestId: 'e1', response, method: 'GET', url: 'https://upstream.test/x', durationMs: 1
|
|
105
|
-
});
|
|
118
|
+
await logError({ requestId: 'e1', response });
|
|
106
119
|
expect(tracing.addEventError).toHaveBeenCalledWith({
|
|
107
120
|
id: 'e1',
|
|
108
121
|
details: {
|
|
@@ -125,9 +138,7 @@ describe('fetch/logger', () => {
|
|
|
125
138
|
statusText: 'Bad Gateway',
|
|
126
139
|
headers: { 'content-type': 'text/plain' }
|
|
127
140
|
});
|
|
128
|
-
await logError({
|
|
129
|
-
requestId: 'e2', response, method: 'GET', url: 'https://upstream.test/y', durationMs: 1
|
|
130
|
-
});
|
|
141
|
+
await logError({ requestId: 'e2', response });
|
|
131
142
|
expect(tracing.addEventError).toHaveBeenCalledWith({
|
|
132
143
|
id: 'e2',
|
|
133
144
|
details: {
|
|
@@ -147,9 +158,7 @@ describe('fetch/logger', () => {
|
|
|
147
158
|
statusText: 'OK',
|
|
148
159
|
headers: { 'content-type': 'application/json', Authorization: 'x' }
|
|
149
160
|
});
|
|
150
|
-
await logResponse({
|
|
151
|
-
requestId: 'lr1', response, method: 'GET', url: 'https://x.test/a', durationMs: 1
|
|
152
|
-
});
|
|
161
|
+
await logResponse({ requestId: 'lr1', response });
|
|
153
162
|
expect(tracing.addEventEnd).toHaveBeenCalledWith({
|
|
154
163
|
id: 'lr1',
|
|
155
164
|
details: {
|
|
@@ -168,9 +177,7 @@ describe('fetch/logger', () => {
|
|
|
168
177
|
'Set-Cookie': 'a=b'
|
|
169
178
|
}
|
|
170
179
|
});
|
|
171
|
-
await logResponse({
|
|
172
|
-
requestId: 'lr-v', response, method: 'POST', url: 'https://x.test/b', durationMs: 1
|
|
173
|
-
});
|
|
180
|
+
await logResponse({ requestId: 'lr-v', response });
|
|
174
181
|
expect(tracing.addEventEnd).toHaveBeenCalledWith({
|
|
175
182
|
id: 'lr-v',
|
|
176
183
|
details: {
|
|
@@ -189,7 +196,7 @@ describe('fetch/logger', () => {
|
|
|
189
196
|
it('forwards serialized error details (including stack) to Tracing.addEventError', async () => {
|
|
190
197
|
const { logFailure } = await logLogger(false);
|
|
191
198
|
const err = new TypeError('network');
|
|
192
|
-
logFailure({ requestId: 'f1', error: err
|
|
199
|
+
logFailure({ requestId: 'f1', error: err });
|
|
193
200
|
expect(tracing.addEventError).toHaveBeenCalledWith({
|
|
194
201
|
id: 'f1',
|
|
195
202
|
details: {
|
|
@@ -202,63 +209,4 @@ describe('fetch/logger', () => {
|
|
|
202
209
|
});
|
|
203
210
|
});
|
|
204
211
|
});
|
|
205
|
-
describe('http:request event emission', () => {
|
|
206
|
-
it('emits http:request with outcome=success on logResponse', async () => {
|
|
207
|
-
const { logResponse } = await logLogger(false);
|
|
208
|
-
const response = new Response('', { status: 200 });
|
|
209
|
-
await logResponse({
|
|
210
|
-
requestId: 'r-ok',
|
|
211
|
-
response,
|
|
212
|
-
method: 'GET',
|
|
213
|
-
url: 'https://api.example.com/ok',
|
|
214
|
-
durationMs: 42
|
|
215
|
-
});
|
|
216
|
-
expect(emit).toHaveBeenCalledWith('http:request', {
|
|
217
|
-
requestId: 'r-ok',
|
|
218
|
-
method: 'GET',
|
|
219
|
-
url: 'https://api.example.com/ok',
|
|
220
|
-
status: 200,
|
|
221
|
-
durationMs: 42,
|
|
222
|
-
outcome: 'success'
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
it('emits http:request with outcome=http_error on logError', async () => {
|
|
226
|
-
const { logError } = await logLogger(false);
|
|
227
|
-
const response = new Response('boom', { status: 500 });
|
|
228
|
-
await logError({
|
|
229
|
-
requestId: 'r-err',
|
|
230
|
-
response,
|
|
231
|
-
method: 'POST',
|
|
232
|
-
url: 'https://api.example.com/err',
|
|
233
|
-
durationMs: 15
|
|
234
|
-
});
|
|
235
|
-
expect(emit).toHaveBeenCalledWith('http:request', {
|
|
236
|
-
requestId: 'r-err',
|
|
237
|
-
method: 'POST',
|
|
238
|
-
url: 'https://api.example.com/err',
|
|
239
|
-
status: 500,
|
|
240
|
-
durationMs: 15,
|
|
241
|
-
outcome: 'http_error'
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
it('emits http:request with outcome=network_error on logFailure (status undefined)', async () => {
|
|
245
|
-
const { logFailure } = await logLogger(false);
|
|
246
|
-
const err = new TypeError('network');
|
|
247
|
-
logFailure({
|
|
248
|
-
requestId: 'r-net',
|
|
249
|
-
error: err,
|
|
250
|
-
method: 'GET',
|
|
251
|
-
url: 'https://api.example.com/net',
|
|
252
|
-
durationMs: 9
|
|
253
|
-
});
|
|
254
|
-
expect(emit).toHaveBeenCalledWith('http:request', {
|
|
255
|
-
requestId: 'r-net',
|
|
256
|
-
method: 'GET',
|
|
257
|
-
url: 'https://api.example.com/net',
|
|
258
|
-
status: undefined,
|
|
259
|
-
durationMs: 9,
|
|
260
|
-
outcome: 'network_error'
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
212
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outputai/http",
|
|
3
|
-
"version": "0.4.1-
|
|
3
|
+
"version": "0.4.1-next.43c9293.0",
|
|
4
4
|
"description": "Framework abstraction to make HTTP calls with tracing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"ky": "1.14.3",
|
|
13
13
|
"undici": "8.1.0",
|
|
14
|
-
"@outputai/core": "0.4.1-
|
|
14
|
+
"@outputai/core": "0.4.1-next.43c9293.0"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"publishConfig": {
|