From 991ec46a70852fdc34c0de27164be1a1c41e6d59 Mon Sep 17 00:00:00 2001 From: Adrian Mroz Date: Fri, 2 Jun 2023 15:50:25 +0200 Subject: [PATCH 1/2] Basic implementation --- docs/extending-turnilo.md | 11 +++++++++-- src/server/app.ts | 8 ++++++-- src/server/routes/query/query.mocha.ts | 7 +++++++ src/server/routes/query/routes/visualization.ts | 4 ++-- src/server/utils/query/log-query.mocha.ts | 6 +++--- src/server/utils/query/log-query.ts | 3 ++- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/docs/extending-turnilo.md b/docs/extending-turnilo.md index a5fb49e8f..4d453052e 100644 --- a/docs/extending-turnilo.md +++ b/docs/extending-turnilo.md @@ -12,6 +12,14 @@ Turnilo lets you extend its behaviour in three ways: * Query decorator for all Plywood queries sent to Druid cluster * Plugins for backend application +## `turniloMetadata` + +Turnilo adds `turniloMetadata` object to every `Request` object. +It is a namespace for you to keep any values necessary that live with requests. + +`turniloMetadata` has one special property for now: +* `loggerContext`: It is a `Record` of values that will be added by logger for every message. + ## Request decorator In the cluster config add a key `druidRequestDecorator` with property `path` that points to a relative js file. @@ -131,5 +139,4 @@ This function will be called at the start of application with following paramete Worth to look into !(express documentation)[https://expressjs.com/en/api.html#app]. Use `get`, `post` etc. to define new endpoints. Use `use` to define middleware. -Additionally, Turnilo defines empty object on Request object under `turniloMetadata` key. -Here you can pass values between your plugins and not pollute headers. +Use `turniloMetadata` object on `Request` to pass values between your plugins and not pollute headers. diff --git a/src/server/app.ts b/src/server/app.ts index 7845b7721..353ecbc13 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -38,7 +38,9 @@ import { errorLayout } from "./views"; declare module "express" { export interface Request { - turniloMetadata: object; + turniloMetadata: { + loggerContext: Record + }; } } @@ -94,7 +96,9 @@ export default function createApp(serverSettings: ServerSettings, settingsManage } app.use((req: Request, res: Response, next: Function) => { - req.turniloMetadata = {}; + req.turniloMetadata = { + loggerContext: {} + }; next(); }); diff --git a/src/server/routes/query/query.mocha.ts b/src/server/routes/query/query.mocha.ts index 558ad0166..5eaaa9df3 100644 --- a/src/server/routes/query/query.mocha.ts +++ b/src/server/routes/query/query.mocha.ts @@ -47,6 +47,13 @@ const app = express(); app.use(bodyParser.json()); +app.use((req: any, res, next) => { + req.turniloMetadata = { + loggerContext: {} + }; + next(); +}); + app.use("/", queryRouter(settingsManagerFixture)); describe("query router", () => { diff --git a/src/server/routes/query/routes/visualization.ts b/src/server/routes/query/routes/visualization.ts index 5d2808149..5172eb28a 100644 --- a/src/server/routes/query/routes/visualization.ts +++ b/src/server/routes/query/routes/visualization.ts @@ -28,11 +28,11 @@ function getQuery(essence: Essence, timekeeper: Timekeeper): Expression { return essence.visualization.name === "grid" ? makeGridQuery(essence, timekeeper) : makeQuery(essence, timekeeper); } -export default async function visualizationRoute({ context }: QueryRouterRequest, res: Response) { +export default async function visualizationRoute({ turniloMetadata, context }: QueryRouterRequest, res: Response) { const { dataCube, essence, decorator, timekeeper, logger } = context; const query = getQuery(essence, timekeeper); const queryTimeStart = Date.now(); const result = await executeQuery(dataCube, query, essence.timezone, decorator); - logQueryInfo(essence, timekeeper, logger.setLoggerId("turnilo-visualization-query"), Date.now() - queryTimeStart); + logQueryInfo(essence, timekeeper, logger.setLoggerId("turnilo-visualization-query"), Date.now() - queryTimeStart, turniloMetadata.loggerContext); res.json({ result }); } diff --git a/src/server/utils/query/log-query.mocha.ts b/src/server/utils/query/log-query.mocha.ts index 8b9de8b3d..7635da23b 100644 --- a/src/server/utils/query/log-query.mocha.ts +++ b/src/server/utils/query/log-query.mocha.ts @@ -50,7 +50,7 @@ describe("log-query-info", () => { }; it("Should log all basic variables", () => { - logQueryInfo(baseEssence, timekeeper, logger, executionTime); + logQueryInfo(baseEssence, timekeeper, logger, executionTime, {}); expect(logger.log.calledWith( expectedMessage, expectedBasicVariables @@ -59,7 +59,7 @@ describe("log-query-info", () => { it("Should log additional time shift variables", () => { const essence = baseEssence.set("timeShift", TimeShift.fromJS("P1D")); - logQueryInfo(essence, timekeeper, logger, executionTime); + logQueryInfo(essence, timekeeper, logger, executionTime, {}); expect(logger.log.calledWith( expectedMessage, { @@ -73,7 +73,7 @@ describe("log-query-info", () => { it("Should log additional time split variables", () => { const essence = baseEssence.addSplit(timeSplitCombine("time", "P2D"), VisStrategy.FairGame); - logQueryInfo(essence, timekeeper, logger, executionTime); + logQueryInfo(essence, timekeeper, logger, executionTime, {}); expect(logger.log.calledWith( expectedMessage, { diff --git a/src/server/utils/query/log-query.ts b/src/server/utils/query/log-query.ts index 7ffc76ea8..87035259a 100644 --- a/src/server/utils/query/log-query.ts +++ b/src/server/utils/query/log-query.ts @@ -65,10 +65,11 @@ function timeVariables(essence: Essence, timekeeper: Timekeeper): Record) { const nonTimeFilters = essence.filter.removeClause(essence.getTimeDimension().name); logger.log(`Visualization query ${essence.description(timekeeper)}`, { + ...context, executionTime, ...timeVariables(essence, timekeeper), dataCube: essence.dataCube.name, From b064f46494c640a61118d43b50f6a6c931ff24be Mon Sep 17 00:00:00 2001 From: Adrian Mroz Date: Fri, 2 Jun 2023 19:08:49 +0200 Subject: [PATCH 2/2] more tests and docs --- docs/extending-turnilo.md | 22 ++++++++++++++++++---- src/server/utils/query/log-query.mocha.ts | 11 +++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/extending-turnilo.md b/docs/extending-turnilo.md index 4d453052e..465018387 100644 --- a/docs/extending-turnilo.md +++ b/docs/extending-turnilo.md @@ -12,13 +12,27 @@ Turnilo lets you extend its behaviour in three ways: * Query decorator for all Plywood queries sent to Druid cluster * Plugins for backend application -## `turniloMetadata` +## turniloMetadata Turnilo adds `turniloMetadata` object to every `Request` object. It is a namespace for you to keep any values necessary that live with requests. -`turniloMetadata` has one special property for now: -* `loggerContext`: It is a `Record` of values that will be added by logger for every message. +`turniloMetadata` has special properties: + +### `loggerContext` + +It is a `Record` of values that will be added by logger for every message. + +You could use it to log User Agent of browser that requested view with a simple [plugin](#plugins): + +```javascript +app.use(function(req, res, next) { + req.turniloMetadata.loggerContext.userAgent = req.get('User-Agent'); + next(); +}); +``` + + ## Request decorator @@ -139,4 +153,4 @@ This function will be called at the start of application with following paramete Worth to look into !(express documentation)[https://expressjs.com/en/api.html#app]. Use `get`, `post` etc. to define new endpoints. Use `use` to define middleware. -Use `turniloMetadata` object on `Request` to pass values between your plugins and not pollute headers. +Use [`turniloMetadata`](#turniloMetadata) object on `Request` to pass values between your plugins and not pollute headers. diff --git a/src/server/utils/query/log-query.mocha.ts b/src/server/utils/query/log-query.mocha.ts index 7635da23b..69c6a7f79 100644 --- a/src/server/utils/query/log-query.mocha.ts +++ b/src/server/utils/query/log-query.mocha.ts @@ -85,4 +85,15 @@ describe("log-query-info", () => { )).to.be.true; }); + it("Should log additional logger context", () => { + logQueryInfo(baseEssence, timekeeper, logger, executionTime, { contextName: "context-value" }); + expect(logger.log.calledWith( + expectedMessage, + { + ...expectedBasicVariables, + contextName: "context-value" + } + )).to.be.true; + }); + });