์ฌ๊ธฐ์ ๊ทผ๋ณธ์ ์ธ ๋ฌธ์ ๋ ๋ค์ ๋ณต์กํ๋ฏ๋ก ์ฌ๊ธฐ์์ ์ฐ๋ฆฌ๊ฐ ์๊ตฌํ๋ ๊ฒ์ ์์ฝํ๊ฒ ์ต๋๋ค -- ์ฐ๋ฆฌ๋ defaultMetadata
๊ฐ ํ๋๋ก ํจ์๋ฅผ ์ ๊ณตํ์ฌ ๋์ ์ผ๋ก ์์ฑ๋ ๊ฐ์ ํฌํจํ๋ ๊ธฐ๋ฅ์ ๋ณด๋ ๋ฐ ๊ด์ฌ์ด ์์ ๊ฒ์
๋๋ค. ์๋ฅผ ๋ค์ด:
const logger = createLogger({
defaultMeta: {
// Function to be evaluated every time a log is written.
timeWritten: () => { return `Date.now()`; }
},
// ...
});
์ ์ฒด ๊ทผ๊ฑฐ๋ ์๋๋ฅผ ์ฐธ์กฐํ์ญ์์ค.
winston
๋ฒ์ ?_winston@2
winston@3
node -v
์ถ๋ ฅ:_ v10.15.2Winston์ฉ Stackdriver Logging Transport์ ์์ฑ์๋ก์ ์ฐ๋ฆฌ๋ ๋ก๊ทธ ์์ฒญ ์๊ด ๊ด๊ณ๋ฅผ ์ง์ํ๊ณ ์ถ์ต๋๋ค. async_hooks
๋ชจ๋์ ์์ฒญ ID์ ๊ฐ์ ์์ฒญ๋ณ ์ ๋ณด๋ฅผ ์ ์ฅํ ์ ์๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๊ทธ๋ฌ๋ Transport
์ธก์์ ์์ ์ ์ผ๋ก ๊ฒ์ํ ์ ์๋ ์๋จ์ด ์์ต๋๋ค.
๋ฌธ์ ๋ ์๋ฅผ ํตํด ๊ฐ์ฅ ์ ์ค๋ช ๋ฉ๋๋ค. ๋ค์ ์ฝ๋๋ ~100๊ฐ์ ์์ฒญ์ด ์ฐ์์ ์ผ๋ก ๋น ๋ฅด๊ฒ ์ด๋ฃจ์ด์ง ๋ ์ด ์ถ๋ ฅ ์ ์์ฑํฉ๋๋ค.
const ah = require('async_hooks');
const express = require('express');
const { createLogger } = require('winston');
const TransportStream = require('winston-transport');
// Activate async hooks.
let requestIdHighWater = 0;
const requestIdTable = {};
ah.createHook({
init: (child, resource, parent) => {
requestIdTable[child] = requestIdTable[parent];
}
}).enable();
// OUR CODE
class MyTransport extends TransportStream {
log(info, next) {
// From the transport, the request ID is not reliable.
const actualRequestId = requestIdTable[ah.executionAsyncId()];
console.log(`Expected: ${info.expectedRequestId}, Actual: ${actualRequestId}`);
setImmediate(next);
}
}
// OUR USER'S CODE
const logger = createLogger({
transports: [new MyTransport()]
});
const app = express();
app.get('/', async (req, res) => {
// Store a request ID.
const requestId = requestIdHighWater++;
requestIdTable[ah.executionAsyncId()] = requestId;
logger.info('hello', { expectedRequestId: requestId });
res.sendStatus(200);
});
app.listen(3000);
"์์" ๋ฐ "์ค์ " ์์ฒญ ID์ ๋ถ์ผ์น์ ์ ์ํ์ญ์์ค. ์ฐ๋ฆฌ๊ฐ MyTransport
์ ์์ฑ์๋ผ๋ฉด ํ์ฌ ์คํ ID("์ค์ ")๋ฅผ ์ฐธ์กฐํ์ฌ ์ฌ๋ฐ๋ฅธ ์์ฒญ ID๋ฅผ ์ ํํ๊ฒ ์ป์ ์ ์๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ๋์ ๋ฉํ๋ฐ์ดํฐ("์์")๋ก ์ฌ๋ฐ๋ฅธ ์์ฒญ ID๋ฅผ ์ ๋ฌํ๋ ์ฌ์ฉ์์ ์์กดํด์ผ ํฉ๋๋ค. ์ด๋ Transport๊ฐ log
์ฝ๋ฐฑ ํธ์ถ์ ์ฐ๊ธฐํ ๋ Winston 3๊ฐ ๋ก๊ทธ๋ฅผ ์ผ๊ด ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์
๋๋ค( readable-stream
๋ฅผ ํตํด).
๋ฌธ์ ๋ ์ฐ๋ฆฌ๋ฅผ ์ํด ์์ฒญ ID๋ฅผ ์ ๋ฌํ๋ ์ฌ์ฉ์์ ์์กดํ๊ณ ์ถ์ง ์๋ค๋ ๊ฒ์ ๋๋ค. ์ฐ๋ฆฌ๋ ๊ทธ๊ฒ์ด ์๋์ผ๋ก ์ผ์ด๋๊ธฐ๋ฅผ ์ํฉ๋๋ค. ๊ฒฐ๊ตญ ์ฌ์ฉ์๋ ์๋ง๋ ๋ค์๊ณผ ๊ฐ์ด ์ฐ๊ณ ์ถ์ ๊ฒ์ ๋๋ค.
app.get('/', async (req, res) => {
logger.info('hello');
res.sendStatus(200);
});
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ์๊ฒ ๋งก๊ธฐ์ญ์์ค.
์ฐ๊ฒฐ๋ ์ถ๋ ฅ์ ์์ ๋ฐ ์ค์ ์์ฒญ ID๊ฐ ์ผ์นํฉ๋๋ค.
์ค์ ์์ฒญ ID๊ฐ ์ผ์นํ๋๋ก ์ฝ๋๋ฅผ ์์ ํ๋ ๊ฒ์ ๋ถ๊ฐ๋ฅํ๋ฏ๋ก ์ด ๋ฌธ์ ์ ๋ํ ํด๊ฒฐ์ฑ
์ ์ฌ์ฉ์๊ฐ ๋ก๊ฑฐ๊ฐ ํธ์ถ๋ ๋ ํธ์ถ๋๋ defaultMetadata
์ ํ๋๋ก ํจ์๋ฅผ ์ง์ ํ ์ ์๋๋ก ํ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ ๊ฒ ํ๋ฉด ์ฝ๋๊ฐ this ๋ก ๋ณ๊ฒฝ๋์ด ์ฌ์ฉ์๊ฐ ์์ฒญ ID๋ฅผ ์ธ์ํ์ง ์๊ณ ๋ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ defaultMetadata
ํ๋๋ก ์ฝํฌ๋ฅผ ์ ๊ณตํ๋ค๋ ์์ ๊ฒฝ๊ณ ๊ฐ ์์ต๋๋ค(์๋ง๋ ์ฐ๋ฆฌ ๋ชจ๋์ ์ฌ์ฉ).
๋ฐ๋ผ์ ์ด ๋ฌธ์ ์ ๋ํ ํด๊ฒฐ์ฑ ์ ์ฌ์ฉ์๊ฐ defaultMetadata์ ํ๋๋ก ํจ์๋ฅผ ์ง์ ํ ์ ์๋๋ก ํ๋ ๊ฒ์ ๋๋ค.
๋๋ ์ด๊ฒ์ด redux thunks ์ ์ ์ฌํ๊ฒ ๊ตฌํ๋ ์ ์๋ค๊ณ ์๊ฐํ์ต๋๋ค. defaultMetadata
์ ํจ์ ๊ฐ ์์ฑ์ ๊ฐ๋ ๋์ defaultMetadata
์์ฒด๊ฐ ์ ํ์ ์ผ๋ก ํจ์๊ฐ ๋ ์ ์๋ค๊ณ ์๊ฐํ์ต๋๋ค. ์ฝํฌ์ ๋ง์ฐฌ๊ฐ์ง๋ก winston
๋ ์ด๋ฌํ ๊ฒฝ์ฐ defaultMetadata
๋ฅผ ํธ์ถํ์ฌ ๋ฐํ๋ ๊ฐ์ ์ฌ์ฉํฉ๋๋ค. ๋ค์ ๋งํด:
const logger = createLogger({
defaultMeta: () => {
// Function to be evaluated every time a log is written.
return {
timeWritten: Date.now();
// ..
}
}
// ...
});
์ ์๋ ์๋ฃจ์ ์ด ๊ตฌํ๋ ๋๊น์ง ์ฌ์ฉํ ์ ์๋ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค. getter ์ฌ์ฉ:
const logger = createLogger({
defaultMeta: {
get timeWritten () { return `Date.now()`; }
},
// ...
});
UPD: @mooski๊ฐ ์ธ๊ธํ ๋ฒ๊ทธ ์์
~ํ๋ฅญํ ์๋ฃจ์
@Alexsey - ์ด ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์ timeWritten
๋ค์ ์ฝ๋ก ์ด ์์ด์๋ ์ ๋๋ค๋ ์ ์ ์ ์ํ์ธ์.~
์ ๋ฐ์ดํธ: @Alexsey์ ์ํด ์์ ๋จ
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ ์๋ ์๋ฃจ์ ์ด ๊ตฌํ๋ ๋๊น์ง ์ฌ์ฉํ ์ ์๋ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค. getter ์ฌ์ฉ:
UPD: @mooski๊ฐ ์ธ๊ธํ ๋ฒ๊ทธ ์์