Send metrics for map instantiations (named, anonymous and static) with the new format.
This commit is contained in:
@@ -57,7 +57,6 @@ const user = require('./middlewares/user');
|
||||
const sendResponse = require('./middlewares/send-response');
|
||||
const syntaxError = require('./middlewares/syntax-error');
|
||||
const errorMiddleware = require('./middlewares/error-middleware');
|
||||
const pubSubMetrics = require('./middlewares/pubsub-metrics');
|
||||
|
||||
const MapRouter = require('./map/map-router');
|
||||
const TemplateRouter = require('./template/template-router');
|
||||
@@ -161,7 +160,10 @@ module.exports = class ApiRouter {
|
||||
});
|
||||
namedMapProviderCacheReporter.start();
|
||||
|
||||
const metricsBackend = new PubSubMetricsBackend(serverOptions.pubSubMetrics);
|
||||
|
||||
const collaborators = {
|
||||
config: serverOptions,
|
||||
analysisStatusBackend,
|
||||
attributesBackend,
|
||||
dataviewBackend,
|
||||
@@ -181,13 +183,13 @@ module.exports = class ApiRouter {
|
||||
layergroupMetadata,
|
||||
namedMapProviderCache,
|
||||
tablesExtentBackend,
|
||||
clusterBackend
|
||||
clusterBackend,
|
||||
metricsBackend
|
||||
};
|
||||
|
||||
this.metadataBackend = metadataBackend;
|
||||
this.mapRouter = new MapRouter({ collaborators });
|
||||
this.templateRouter = new TemplateRouter({ collaborators });
|
||||
this.metadataBackend = metadataBackend;
|
||||
this.pubSubMetricsBackend = new PubSubMetricsBackend(this.serverOptions.pubSubMetrics);
|
||||
}
|
||||
|
||||
route (app, routes) {
|
||||
@@ -220,11 +222,6 @@ module.exports = class ApiRouter {
|
||||
apiRouter.use(sendResponse());
|
||||
apiRouter.use(syntaxError());
|
||||
apiRouter.use(errorMiddleware());
|
||||
apiRouter.use(pubSubMetrics({
|
||||
enabled: this.serverOptions.pubSubMetrics.enabled,
|
||||
metricsBackend: this.pubSubMetricsBackend,
|
||||
logger: global.logger
|
||||
}));
|
||||
|
||||
paths.forEach(path => app.use(path, apiRouter));
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ const mapError = require('../middlewares/map-error');
|
||||
const CreateLayergroupMapConfigProvider = require('../../models/mapconfig/provider/create-layergroup-provider');
|
||||
const rateLimit = require('../middlewares/rate-limit');
|
||||
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||
const metrics = require('../middlewares/pubsub-metrics');
|
||||
|
||||
module.exports = class AnonymousMapController {
|
||||
/**
|
||||
@@ -39,6 +40,7 @@ module.exports = class AnonymousMapController {
|
||||
* @constructor
|
||||
*/
|
||||
constructor (
|
||||
config,
|
||||
pgConnection,
|
||||
templateMaps,
|
||||
mapBackend,
|
||||
@@ -49,8 +51,10 @@ module.exports = class AnonymousMapController {
|
||||
mapConfigAdapter,
|
||||
statsBackend,
|
||||
authBackend,
|
||||
layergroupMetadata
|
||||
layergroupMetadata,
|
||||
metricsBackend
|
||||
) {
|
||||
this.config = config;
|
||||
this.pgConnection = pgConnection;
|
||||
this.templateMaps = templateMaps;
|
||||
this.mapBackend = mapBackend;
|
||||
@@ -62,6 +66,7 @@ module.exports = class AnonymousMapController {
|
||||
this.statsBackend = statsBackend;
|
||||
this.authBackend = authBackend;
|
||||
this.layergroupMetadata = layergroupMetadata;
|
||||
this.metricsBackend = metricsBackend;
|
||||
}
|
||||
|
||||
route (mapRouter) {
|
||||
@@ -76,8 +81,26 @@ module.exports = class AnonymousMapController {
|
||||
const includeQuery = true;
|
||||
const label = 'ANONYMOUS LAYERGROUP';
|
||||
const addContext = true;
|
||||
const metricsTags = {
|
||||
event: 'map_view',
|
||||
attributes: { map_type: 'anonymous' },
|
||||
from: {
|
||||
req: {
|
||||
query: { client: 'client' }
|
||||
},
|
||||
res: {
|
||||
body: { layergroupid: 'map_id' }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
metrics({
|
||||
enabled: this.config.pubSubMetrics.enabled,
|
||||
metricsBackend: this.metricsBackend,
|
||||
logger: global.logger,
|
||||
tags: metricsTags
|
||||
}),
|
||||
credentials(),
|
||||
authorize(this.authBackend),
|
||||
dbConnSetup(this.pgConnection),
|
||||
|
||||
@@ -15,6 +15,7 @@ const ClusteredFeaturesLayergroupController = require('./clustered-features-laye
|
||||
module.exports = class MapRouter {
|
||||
constructor ({ collaborators }) {
|
||||
const {
|
||||
config,
|
||||
analysisStatusBackend,
|
||||
attributesBackend,
|
||||
dataviewBackend,
|
||||
@@ -34,7 +35,8 @@ module.exports = class MapRouter {
|
||||
layergroupMetadata,
|
||||
namedMapProviderCache,
|
||||
tablesExtentBackend,
|
||||
clusterBackend
|
||||
clusterBackend,
|
||||
metricsBackend
|
||||
} = collaborators;
|
||||
|
||||
this.analysisLayergroupController = new AnalysisLayergroupController(
|
||||
@@ -85,6 +87,7 @@ module.exports = class MapRouter {
|
||||
);
|
||||
|
||||
this.anonymousMapController = new AnonymousMapController(
|
||||
config,
|
||||
pgConnection,
|
||||
templateMaps,
|
||||
mapBackend,
|
||||
@@ -95,10 +98,12 @@ module.exports = class MapRouter {
|
||||
mapConfigAdapter,
|
||||
statsBackend,
|
||||
authBackend,
|
||||
layergroupMetadata
|
||||
layergroupMetadata,
|
||||
metricsBackend
|
||||
);
|
||||
|
||||
this.previewTemplateController = new PreviewTemplateController(
|
||||
config,
|
||||
namedMapProviderCache,
|
||||
previewBackend,
|
||||
surrogateKeysCache,
|
||||
@@ -106,7 +111,8 @@ module.exports = class MapRouter {
|
||||
metadataBackend,
|
||||
pgConnection,
|
||||
authBackend,
|
||||
userLimitsBackend
|
||||
userLimitsBackend,
|
||||
metricsBackend
|
||||
);
|
||||
|
||||
this.analysesController = new AnalysesCatalogController(
|
||||
|
||||
@@ -12,6 +12,7 @@ const lastModifiedHeader = require('../middlewares/last-modified-header');
|
||||
const checkStaticImageFormat = require('../middlewares/check-static-image-format');
|
||||
const rateLimit = require('../middlewares/rate-limit');
|
||||
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||
const metrics = require('../middlewares/pubsub-metrics');
|
||||
|
||||
const DEFAULT_ZOOM_CENTER = {
|
||||
zoom: 1,
|
||||
@@ -27,6 +28,7 @@ function numMapper (n) {
|
||||
|
||||
module.exports = class PreviewTemplateController {
|
||||
constructor (
|
||||
config,
|
||||
namedMapProviderCache,
|
||||
previewBackend,
|
||||
surrogateKeysCache,
|
||||
@@ -34,8 +36,10 @@ module.exports = class PreviewTemplateController {
|
||||
metadataBackend,
|
||||
pgConnection,
|
||||
authBackend,
|
||||
userLimitsBackend
|
||||
userLimitsBackend,
|
||||
metricsBackend
|
||||
) {
|
||||
this.config = config;
|
||||
this.namedMapProviderCache = namedMapProviderCache;
|
||||
this.previewBackend = previewBackend;
|
||||
this.surrogateKeysCache = surrogateKeysCache;
|
||||
@@ -44,6 +48,7 @@ module.exports = class PreviewTemplateController {
|
||||
this.pgConnection = pgConnection;
|
||||
this.authBackend = authBackend;
|
||||
this.userLimitsBackend = userLimitsBackend;
|
||||
this.metricsBackend = metricsBackend;
|
||||
}
|
||||
|
||||
route (mapRouter) {
|
||||
@@ -51,7 +56,23 @@ module.exports = class PreviewTemplateController {
|
||||
}
|
||||
|
||||
middlewares () {
|
||||
const metricsTags = {
|
||||
event: 'map_view',
|
||||
attributes: { map_type: 'static' },
|
||||
from: {
|
||||
req: {
|
||||
query: { client: 'client' }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
metrics({
|
||||
enabled: this.config.pubSubMetrics.enabled,
|
||||
metricsBackend: this.metricsBackend,
|
||||
logger: global.logger,
|
||||
tags: metricsTags
|
||||
}),
|
||||
credentials(),
|
||||
authorize(this.authBackend),
|
||||
dbConnSetup(this.pgConnection),
|
||||
|
||||
@@ -3,55 +3,58 @@
|
||||
const EVENT_VERSION = '1';
|
||||
const MAX_LENGTH = 100;
|
||||
|
||||
function pubSubMetrics ({ enabled, metricsBackend, logger }) {
|
||||
module.exports = function pubSubMetrics ({ enabled, metricsBackend, logger, tags }) {
|
||||
if (!enabled) {
|
||||
return function pubSubMetricsDisabledMiddleware (req, res, next) {
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
if (!tags || !tags.event) {
|
||||
throw new Error('Missing required "event" parameter to report metrics');
|
||||
}
|
||||
|
||||
return function pubSubMetricsMiddleware (req, res, next) {
|
||||
res.on('finish', () => {
|
||||
const { event, attributes } = getEventData(req, res);
|
||||
const { event, attributes } = getEventData(req, res, tags);
|
||||
|
||||
if (event) {
|
||||
metricsBackend.send(event, attributes)
|
||||
.then(() => logger.debug(`PubSubTracker: event '${event}' published succesfully`))
|
||||
.catch((error) => logger.error(`ERROR: pubsub middleware failed to publish event '${event}': ${error.message}`));
|
||||
}
|
||||
metricsBackend.send(event, attributes)
|
||||
.then(() => logger.debug(`PubSubTracker: event '${event}' published succesfully`))
|
||||
.catch((error) => logger.error(`ERROR: pubsub middleware failed to publish event '${event}': ${error.message}`));
|
||||
});
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function getEventData (req, res) {
|
||||
const event = normalizedField(req.get('Carto-Event'));
|
||||
const eventSource = normalizedField(req.get('Carto-Event-Source'));
|
||||
const eventGroupId = normalizedField(req.get('Carto-Event-Group-Id'));
|
||||
function getEventData (req, res, tags) {
|
||||
const event = tags.event;
|
||||
const extra = {};
|
||||
if (tags.from) {
|
||||
if (tags.from.req) {
|
||||
Object.assign(extra, getFromReq(req, tags.from.req));
|
||||
}
|
||||
|
||||
if (!event || !eventSource) {
|
||||
return { event: undefined, attributes: undefined };
|
||||
if (tags.from.res) {
|
||||
Object.assign(extra, getFromRes(res, tags.from.res));
|
||||
}
|
||||
}
|
||||
|
||||
const attributes = {
|
||||
event_source: eventSource,
|
||||
user_id: res.locals.userId,
|
||||
response_code: res.statusCode.toString(),
|
||||
source_domain: req.hostname,
|
||||
const attributes = Object.assign({}, {
|
||||
metrics_event: normalizedField(req.get('Carto-Event')),
|
||||
event_source: normalizedField(req.get('Carto-Event-Source')),
|
||||
event_group_id: normalizedField(req.get('Carto-Event-Group-Id')),
|
||||
event_time: new Date().toISOString(),
|
||||
user_id: res.locals.userId,
|
||||
user_agent: req.get('User-Agent'),
|
||||
response_code: res.statusCode.toString(),
|
||||
response_time: getResponseTime(res),
|
||||
source_domain: req.hostname,
|
||||
event_version: EVENT_VERSION
|
||||
};
|
||||
}, tags.attributes, extra);
|
||||
|
||||
if (eventGroupId) {
|
||||
attributes.event_group_id = eventGroupId;
|
||||
}
|
||||
|
||||
const responseTime = getResponseTime(res);
|
||||
|
||||
if (responseTime) {
|
||||
attributes.response_time = responseTime.toString();
|
||||
}
|
||||
// remove undefined properties
|
||||
Object.keys(attributes).forEach(key => attributes[key] === undefined && delete attributes[key]);
|
||||
|
||||
return { event, attributes };
|
||||
}
|
||||
@@ -75,7 +78,47 @@ function getResponseTime (res) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return stats.total;
|
||||
return stats.total.toString();
|
||||
}
|
||||
|
||||
module.exports = pubSubMetrics;
|
||||
function getFromReq (req, { query = {}, body = {}, params = {}, headers = {} } = {}) {
|
||||
const extra = {};
|
||||
|
||||
for (const [queryParam, eventName] of Object.entries(query)) {
|
||||
extra[eventName] = req.query[queryParam];
|
||||
}
|
||||
|
||||
for (const [bodyParam, eventName] of Object.entries(body)) {
|
||||
extra[eventName] = req.body[bodyParam];
|
||||
}
|
||||
|
||||
for (const [pathParam, eventName] of Object.entries(params)) {
|
||||
extra[eventName] = req.params[pathParam];
|
||||
}
|
||||
|
||||
for (const [header, eventName] of Object.entries(headers)) {
|
||||
extra[eventName] = req.get(header);
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
function getFromRes (res, { body = {}, headers = {}, locals = {} } = {}) {
|
||||
const extra = {};
|
||||
|
||||
if (res.body) {
|
||||
for (const [bodyParam, eventName] of Object.entries(body)) {
|
||||
extra[eventName] = res.body[bodyParam];
|
||||
}
|
||||
}
|
||||
|
||||
for (const [header, eventName] of Object.entries(headers)) {
|
||||
extra[eventName] = res.get(header);
|
||||
}
|
||||
|
||||
for (const [localParam, eventName] of Object.entries(locals)) {
|
||||
extra[eventName] = res.locals[localParam];
|
||||
}
|
||||
|
||||
return extra;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ const NamedMapMapConfigProvider = require('../../models/mapconfig/provider/named
|
||||
const CreateLayergroupMapConfigProvider = require('../../models/mapconfig/provider/create-layergroup-provider');
|
||||
const rateLimit = require('../middlewares/rate-limit');
|
||||
const { RATE_LIMIT_ENDPOINTS_GROUPS } = rateLimit;
|
||||
const metrics = require('../middlewares/pubsub-metrics');
|
||||
|
||||
module.exports = class NamedMapController {
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ module.exports = class NamedMapController {
|
||||
* @constructor
|
||||
*/
|
||||
constructor (
|
||||
config,
|
||||
pgConnection,
|
||||
templateMaps,
|
||||
mapBackend,
|
||||
@@ -48,8 +50,10 @@ module.exports = class NamedMapController {
|
||||
mapConfigAdapter,
|
||||
statsBackend,
|
||||
authBackend,
|
||||
layergroupMetadata
|
||||
layergroupMetadata,
|
||||
metricsBackend
|
||||
) {
|
||||
this.config = config;
|
||||
this.pgConnection = pgConnection;
|
||||
this.templateMaps = templateMaps;
|
||||
this.mapBackend = mapBackend;
|
||||
@@ -61,6 +65,7 @@ module.exports = class NamedMapController {
|
||||
this.statsBackend = statsBackend;
|
||||
this.authBackend = authBackend;
|
||||
this.layergroupMetadata = layergroupMetadata;
|
||||
this.metricsBackend = metricsBackend;
|
||||
}
|
||||
|
||||
route (templateRouter) {
|
||||
@@ -74,8 +79,26 @@ module.exports = class NamedMapController {
|
||||
const includeQuery = false;
|
||||
const label = 'NAMED MAP LAYERGROUP';
|
||||
const addContext = false;
|
||||
const metricsTags = {
|
||||
event: 'map_view',
|
||||
attributes: { map_type: 'named' },
|
||||
from: {
|
||||
req: {
|
||||
query: { client: 'client' }
|
||||
},
|
||||
res: {
|
||||
body: { layergroupid: 'map_id' }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
metrics({
|
||||
enabled: this.config.pubSubMetrics.enabled,
|
||||
metricsBackend: this.metricsBackend,
|
||||
logger: global.logger,
|
||||
tags: metricsTags
|
||||
}),
|
||||
credentials(),
|
||||
authorize(this.authBackend),
|
||||
dbConnSetup(this.pgConnection),
|
||||
|
||||
@@ -9,6 +9,7 @@ const TileTemplateController = require('./tile-template-controller');
|
||||
module.exports = class TemplateRouter {
|
||||
constructor ({ collaborators }) {
|
||||
const {
|
||||
config,
|
||||
pgConnection,
|
||||
templateMaps,
|
||||
mapBackend,
|
||||
@@ -21,10 +22,12 @@ module.exports = class TemplateRouter {
|
||||
authBackend,
|
||||
layergroupMetadata,
|
||||
namedMapProviderCache,
|
||||
tileBackend
|
||||
tileBackend,
|
||||
metricsBackend
|
||||
} = collaborators;
|
||||
|
||||
this.namedMapController = new NamedMapController(
|
||||
config,
|
||||
pgConnection,
|
||||
templateMaps,
|
||||
mapBackend,
|
||||
@@ -35,7 +38,8 @@ module.exports = class TemplateRouter {
|
||||
mapConfigAdapter,
|
||||
statsBackend,
|
||||
authBackend,
|
||||
layergroupMetadata
|
||||
layergroupMetadata,
|
||||
metricsBackend
|
||||
);
|
||||
|
||||
this.tileTemplateController = new TileTemplateController(
|
||||
|
||||
Reference in New Issue
Block a user