@autofleet/outbreak 0.1.0-beta-1 → 0.2.0-beta-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/package.json +3 -3
- package/src/{http-wrapper/config.ts → config.ts} +1 -1
- package/src/example.ts +39 -0
- package/src/{http-wrapper/wrapper.ts → http_wrapper.ts} +8 -8
- package/src/index.ts +45 -6
- package/src/tracer.ts +67 -0
- package/tests/http-wrapper.test.ts +21 -22
- package/src/http-wrapper/index.ts +0 -41
- package/src/tracer/index.ts +0 -60
package/package.json
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
{
|
2
2
|
"name": "@autofleet/outbreak",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.0-beta-1",
|
4
4
|
"description": "",
|
5
|
-
"main": "dist/index.js",
|
6
|
-
"types": "dist/index.d.ts",
|
5
|
+
"main": "dist/src/index.js",
|
6
|
+
"types": "dist/src/index.d.ts",
|
7
7
|
"scripts": {
|
8
8
|
"build": "rm -rf ./dist && tsc",
|
9
9
|
"prepublish": "npm run build",
|
package/src/example.ts
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
import express from 'express';
|
2
|
+
import axios from 'axios';
|
3
|
+
import winston from 'winston';
|
4
|
+
|
5
|
+
import headersTracer from './index';
|
6
|
+
|
7
|
+
axios.defaults.adapter = require('axios/lib/adapters/http');
|
8
|
+
|
9
|
+
const logger = winston.createLogger({
|
10
|
+
transports: [
|
11
|
+
new winston.transports.Console(),
|
12
|
+
],
|
13
|
+
});
|
14
|
+
|
15
|
+
headersTracer({
|
16
|
+
setAndPropagateCorrelationId: true,
|
17
|
+
headersToPropagate: [
|
18
|
+
'x-text',
|
19
|
+
],
|
20
|
+
headersPrefix: 'x-af-',
|
21
|
+
winstonLogger: logger,
|
22
|
+
});
|
23
|
+
|
24
|
+
const app = express();
|
25
|
+
|
26
|
+
app.all('/', async (req, res) => {
|
27
|
+
const network = axios.create({ baseURL: 'http://localhost:8080/' });
|
28
|
+
const { data } = await network.get('/header-parrot');
|
29
|
+
res.json(data);
|
30
|
+
});
|
31
|
+
|
32
|
+
app.all('/header-parrot', (req, res) => {
|
33
|
+
logger.info('Test message', { t: 1 });
|
34
|
+
res.json(req.headers);
|
35
|
+
});
|
36
|
+
|
37
|
+
app.listen(8080, () => {
|
38
|
+
logger.info('App listen');
|
39
|
+
});
|
@@ -3,7 +3,7 @@ import http from 'http';
|
|
3
3
|
import shortid from 'shortid';
|
4
4
|
import { URL } from 'url';
|
5
5
|
import express from 'express';
|
6
|
-
import { newTrace
|
6
|
+
import tracer, { newTrace } from './tracer';
|
7
7
|
|
8
8
|
const originalHttpCreateServer = http.createServer;
|
9
9
|
const originalRequest = http.request;
|
@@ -14,7 +14,7 @@ function setAndCollectCorrelationId(config, req, res): void {
|
|
14
14
|
if (typeof correlationId === 'undefined') {
|
15
15
|
correlationId = shortid.generate();
|
16
16
|
}
|
17
|
-
|
17
|
+
tracer.currentTrace.context.set(config.correlationIdHeader, correlationId);
|
18
18
|
res.setHeader(config.correlationIdHeader, correlationId);
|
19
19
|
}
|
20
20
|
|
@@ -25,15 +25,15 @@ function collect(req, headers, headersPrefix): void {
|
|
25
25
|
|
26
26
|
[...headers, ...prefixHeaders].forEach((header) => {
|
27
27
|
if (typeof req.headers[header] !== 'undefined') {
|
28
|
-
|
28
|
+
tracer.currentTrace.context.set(header, req.headers[header]);
|
29
29
|
}
|
30
30
|
});
|
31
31
|
}
|
32
32
|
|
33
33
|
function injectInResponse(response, headers): void {
|
34
|
-
if (
|
34
|
+
if (tracer.currentTrace) {
|
35
35
|
headers.forEach((header) => {
|
36
|
-
response.setHeader(header,
|
36
|
+
response.setHeader(header, tracer.currentTrace.context.get(header));
|
37
37
|
});
|
38
38
|
}
|
39
39
|
}
|
@@ -64,14 +64,14 @@ function wrapHttpCreateServer(config) {
|
|
64
64
|
}
|
65
65
|
|
66
66
|
function inject(options): void {
|
67
|
-
if (
|
67
|
+
if (tracer.currentTrace) {
|
68
68
|
if (!options.headers) {
|
69
69
|
// eslint-disable-next-line no-param-reassign
|
70
70
|
options.headers = {};
|
71
71
|
}
|
72
|
-
[...(
|
72
|
+
[...(tracer.currentTrace.context.keys())].forEach((header) => {
|
73
73
|
// eslint-disable-next-line no-param-reassign
|
74
|
-
options.headers[header] =
|
74
|
+
options.headers[header] = tracer.currentTrace.context.get(header);
|
75
75
|
});
|
76
76
|
}
|
77
77
|
}
|
package/src/index.ts
CHANGED
@@ -1,8 +1,47 @@
|
|
1
|
-
|
2
|
-
import { newTrace, getCurrentTrace } from './tracer';
|
1
|
+
import winston, { format } from 'winston';
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
import httpWrapper from './http_wrapper';
|
4
|
+
import config from './config';
|
5
|
+
import * as tracer from './tracer';
|
6
|
+
|
7
|
+
interface Options {
|
8
|
+
winstonLogger?: winston.Logger;
|
9
|
+
loggerTraceKey? : string;
|
10
|
+
headersPrefix?: string;
|
11
|
+
setAndPropagateCorrelationId?: boolean;
|
12
|
+
headersToPropagate?: string[];
|
13
|
+
}
|
14
|
+
|
15
|
+
const addMetadataToLog = (loggerTraceKey) => format((info) => {
|
16
|
+
const currentTrace = tracer.getCurrentTrace();
|
17
|
+
if (currentTrace && currentTrace.context && currentTrace.context.get) {
|
18
|
+
// eslint-disable-next-line no-param-reassign
|
19
|
+
info[loggerTraceKey] = currentTrace.context.get(config.correlationIdHeader);
|
20
|
+
}
|
21
|
+
|
22
|
+
return info;
|
23
|
+
});
|
24
|
+
|
25
|
+
export default function (options: Options): void {
|
26
|
+
httpWrapper(config.load(options));
|
27
|
+
tracer.enable();
|
28
|
+
|
29
|
+
if (options.winstonLogger) {
|
30
|
+
const traceKey = options.loggerTraceKey || 'traceId';
|
31
|
+
// eslint-disable-next-line no-param-reassign
|
32
|
+
options.winstonLogger.format = format.combine(
|
33
|
+
addMetadataToLog(traceKey)(),
|
34
|
+
options.winstonLogger.format,
|
35
|
+
);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
export const getCurrentContext = (): any => tracer.getCurrentTrace();
|
40
|
+
|
41
|
+
export const { newTrace } = tracer;
|
42
|
+
|
43
|
+
export const traceTypes = {
|
44
|
+
HTTP_REQUEST: 'httpRequest',
|
45
|
+
WEB_SOCKET: 'webSocket',
|
46
|
+
RABBIT: 'rabbit',
|
8
47
|
};
|
package/src/tracer.ts
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
import asyncHooks from 'async_hooks';
|
2
|
+
import uuid from 'uuid';
|
3
|
+
|
4
|
+
const prevStates = {};
|
5
|
+
|
6
|
+
const tracer = {
|
7
|
+
currentTrace: null,
|
8
|
+
traces: {},
|
9
|
+
};
|
10
|
+
|
11
|
+
function init(asyncId, type, triggerAsyncId): void {
|
12
|
+
if (tracer.traces[triggerAsyncId]) {
|
13
|
+
tracer.traces[asyncId] = tracer.traces[triggerAsyncId];
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
function before(asyncId): void {
|
18
|
+
if (!tracer.traces[asyncId]) {
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
prevStates[asyncId] = tracer.currentTrace;
|
22
|
+
tracer.currentTrace = tracer.traces[asyncId];
|
23
|
+
}
|
24
|
+
|
25
|
+
function after(asyncId): void {
|
26
|
+
if (!tracer.traces[asyncId]) {
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
tracer.currentTrace = prevStates[asyncId];
|
30
|
+
}
|
31
|
+
|
32
|
+
function destroy(asyncId): void {
|
33
|
+
if (tracer.traces[asyncId]) {
|
34
|
+
delete tracer.traces[asyncId];
|
35
|
+
delete prevStates[asyncId];
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
const hook = asyncHooks.createHook({
|
40
|
+
init, before, after, destroy,
|
41
|
+
});
|
42
|
+
|
43
|
+
class Trace {
|
44
|
+
id: string;
|
45
|
+
|
46
|
+
type: string;
|
47
|
+
|
48
|
+
context: Map<string, string>;
|
49
|
+
|
50
|
+
constructor(type) {
|
51
|
+
this.id = uuid.v1();
|
52
|
+
this.type = type;
|
53
|
+
this.context = new Map();
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
export const newTrace = (type): void => {
|
58
|
+
tracer.currentTrace = new Trace(type);
|
59
|
+
tracer.traces[asyncHooks.executionAsyncId()] = tracer.currentTrace;
|
60
|
+
return tracer.currentTrace;
|
61
|
+
};
|
62
|
+
|
63
|
+
export const enable = (): asyncHooks.AsyncHook => hook.enable();
|
64
|
+
|
65
|
+
export const getCurrentTrace = (): any => tracer.currentTrace || {};
|
66
|
+
|
67
|
+
export default tracer;
|
@@ -3,8 +3,7 @@ import http from 'http';
|
|
3
3
|
import axios from 'axios';
|
4
4
|
import winston from 'winston';
|
5
5
|
|
6
|
-
import headersTracer from '../src/
|
7
|
-
import { headersTracer as a } from '../src/index';
|
6
|
+
import headersTracer from '../src/index';
|
8
7
|
|
9
8
|
|
10
9
|
const logger = winston.createLogger({
|
@@ -18,15 +17,15 @@ const generateApp = async (addEndpoints, port) => {
|
|
18
17
|
|
19
18
|
addEndpoints(app);
|
20
19
|
|
21
|
-
const server: http.Server
|
20
|
+
const server: http.Server = await new Promise((resolve) => {
|
22
21
|
const s = app.listen(port, () => {
|
23
|
-
console.log('Listen on port', port)
|
22
|
+
console.log('Listen on port', port);
|
24
23
|
resolve(s);
|
25
24
|
});
|
26
|
-
})
|
25
|
+
});
|
27
26
|
|
28
27
|
return () => server.close();
|
29
|
-
}
|
28
|
+
};
|
30
29
|
|
31
30
|
describe('E2E', () => {
|
32
31
|
it('Basic functionality', async () => {
|
@@ -42,33 +41,33 @@ describe('E2E', () => {
|
|
42
41
|
|
43
42
|
const closeServer1 = await generateApp((app) => {
|
44
43
|
app.get('/', async (req, res) => {
|
45
|
-
const { data: res1 } = await axios.post(
|
46
|
-
res.json(res1)
|
47
|
-
})
|
48
|
-
}, 8081)
|
44
|
+
const { data: res1 } = await axios.post('http://localhost:8082');
|
45
|
+
res.json(res1);
|
46
|
+
});
|
47
|
+
}, 8081);
|
49
48
|
|
50
49
|
const closeServer2 = await generateApp((app) => {
|
51
50
|
app.post('/', (req, res) => {
|
52
|
-
server2TraceId = req.headers['x-trace-id']
|
51
|
+
server2TraceId = req.headers['x-trace-id'];
|
53
52
|
res.json({
|
54
53
|
value: req.headers['x-test-header'],
|
55
|
-
wkanda: req.headers['x-wakanda-id']
|
56
|
-
})
|
57
|
-
})
|
58
|
-
}, 8082)
|
54
|
+
wkanda: req.headers['x-wakanda-id'],
|
55
|
+
});
|
56
|
+
});
|
57
|
+
}, 8082);
|
59
58
|
|
60
|
-
const { data: res1, headers } = await axios.get(
|
59
|
+
const { data: res1, headers } = await axios.get('http://localhost:8081', {
|
61
60
|
headers: {
|
62
61
|
'x-test-header': 'testHeader',
|
63
|
-
'x-wakanda-id': 'my-wakanda-id'
|
64
|
-
}
|
62
|
+
'x-wakanda-id': 'my-wakanda-id',
|
63
|
+
},
|
65
64
|
});
|
66
65
|
closeServer1();
|
67
66
|
closeServer2();
|
68
67
|
|
69
68
|
|
70
|
-
expect(headers['x-trace-id']).toEqual(server2TraceId)
|
71
|
-
expect(res1.value).toEqual('testHeader')
|
72
|
-
expect(res1.wkanda).toEqual('my-wakanda-id')
|
69
|
+
expect(headers['x-trace-id']).toEqual(server2TraceId);
|
70
|
+
expect(res1.value).toEqual('testHeader');
|
71
|
+
expect(res1.wkanda).toEqual('my-wakanda-id');
|
73
72
|
});
|
74
|
-
})
|
73
|
+
});
|
@@ -1,41 +0,0 @@
|
|
1
|
-
import winston, { format } from 'winston';
|
2
|
-
|
3
|
-
import httpWrapper from './wrapper';
|
4
|
-
import config from './config';
|
5
|
-
import * as tracer from '../tracer';
|
6
|
-
|
7
|
-
interface Options {
|
8
|
-
winstonLogger?: winston.Logger;
|
9
|
-
headersPrefix?: string;
|
10
|
-
setAndPropagateCorrelationId?: boolean;
|
11
|
-
headersToPropagate?: string[];
|
12
|
-
}
|
13
|
-
|
14
|
-
const addMetadataToLog = format((info) => {
|
15
|
-
const currentTrace = tracer.getCurrentTrace();
|
16
|
-
if (currentTrace && currentTrace.context && currentTrace.context.get) {
|
17
|
-
// eslint-disable-next-line no-param-reassign
|
18
|
-
info.traceId = currentTrace.context.get(config.correlationIdHeader);
|
19
|
-
}
|
20
|
-
|
21
|
-
return info;
|
22
|
-
});
|
23
|
-
|
24
|
-
export default function (options: Options): void {
|
25
|
-
httpWrapper(config.load(options));
|
26
|
-
tracer.enable();
|
27
|
-
|
28
|
-
if (options.winstonLogger) {
|
29
|
-
// eslint-disable-next-line no-param-reassign
|
30
|
-
options.winstonLogger.format = format.combine(
|
31
|
-
addMetadataToLog(),
|
32
|
-
options.winstonLogger.format,
|
33
|
-
);
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
export const traceTypes = {
|
38
|
-
HTTP_REQUEST: 'httpRequest',
|
39
|
-
WEB_SOCKET: 'webSocket',
|
40
|
-
RABBIT: 'rabbit',
|
41
|
-
};
|
package/src/tracer/index.ts
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
import asyncHooks from 'async_hooks';
|
2
|
-
import uuid from 'uuid';
|
3
|
-
|
4
|
-
class Trace {
|
5
|
-
id: string;
|
6
|
-
|
7
|
-
type: string;
|
8
|
-
|
9
|
-
context: Map<string, any>;
|
10
|
-
|
11
|
-
hooks: { [key: string]: Function[] }
|
12
|
-
|
13
|
-
constructor(type) {
|
14
|
-
this.id = uuid.v1();
|
15
|
-
this.type = type;
|
16
|
-
this.context = new Map();
|
17
|
-
this.hooks = {
|
18
|
-
onDestroy: [],
|
19
|
-
}
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
const executionContextMap: Map<number, Trace> = new Map();
|
24
|
-
|
25
|
-
function init(asyncId, type, triggerAsyncId): void {
|
26
|
-
if (executionContextMap.has(triggerAsyncId)) {
|
27
|
-
executionContextMap.set(asyncId, executionContextMap.get(triggerAsyncId));
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
function destroy(asyncId): void {
|
32
|
-
const ecValue = executionContextMap.get(asyncId);
|
33
|
-
if (ecValue) {
|
34
|
-
ecValue.hooks.onDestroy.map((cb) => cb(asyncId, ecValue));
|
35
|
-
}
|
36
|
-
executionContextMap.delete(asyncId);
|
37
|
-
}
|
38
|
-
|
39
|
-
const hook = asyncHooks.createHook({
|
40
|
-
init, destroy,
|
41
|
-
});
|
42
|
-
|
43
|
-
export const newTrace = (type): Trace => {
|
44
|
-
const trace = new Trace(type);
|
45
|
-
executionContextMap.set(asyncHooks.executionAsyncId(), trace);
|
46
|
-
return trace;
|
47
|
-
};
|
48
|
-
|
49
|
-
export const enable = (): asyncHooks.AsyncHook => hook.enable();
|
50
|
-
|
51
|
-
export const getCurrentTrace = (): Trace => {
|
52
|
-
const currentContext = executionContextMap.get(asyncHooks.executionAsyncId());
|
53
|
-
if (!currentContext) {
|
54
|
-
return newTrace('initialContext');
|
55
|
-
}
|
56
|
-
|
57
|
-
return currentContext;
|
58
|
-
};
|
59
|
-
|
60
|
-
export default executionContextMap;
|