mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-29 01:15:48 +02:00
feat: resolve useragent source and add as source label to metrics (#7883)
This commit is contained in:
parent
183a9fc737
commit
cf83043d8a
@ -30,6 +30,7 @@ type MetricEvent =
|
|||||||
type RequestOriginEventPayload = {
|
type RequestOriginEventPayload = {
|
||||||
type: 'UI' | 'API';
|
type: 'UI' | 'API';
|
||||||
method: Request['method'];
|
method: Request['method'];
|
||||||
|
source?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MetricEventPayloads = {
|
type MetricEventPayloads = {
|
||||||
|
@ -350,7 +350,7 @@ export default class MetricsMonitor {
|
|||||||
const requestOriginCounter = createCounter({
|
const requestOriginCounter = createCounter({
|
||||||
name: 'request_origin_counter',
|
name: 'request_origin_counter',
|
||||||
help: 'Number of authenticated requests, including origin information.',
|
help: 'Number of authenticated requests, including origin information.',
|
||||||
labelNames: ['type', 'method'],
|
labelNames: ['type', 'method', 'source'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const resourceLimit = createGauge({
|
const resourceLimit = createGauge({
|
||||||
@ -715,9 +715,9 @@ export default class MetricsMonitor {
|
|||||||
events.onMetricEvent(
|
events.onMetricEvent(
|
||||||
eventBus,
|
eventBus,
|
||||||
events.REQUEST_ORIGIN,
|
events.REQUEST_ORIGIN,
|
||||||
({ type, method }) => {
|
({ type, method, source }) => {
|
||||||
if (flagResolver.isEnabled('originMiddleware')) {
|
if (flagResolver.isEnabled('originMiddleware')) {
|
||||||
requestOriginCounter.increment({ type, method });
|
requestOriginCounter.increment({ type, method, source });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
29
src/lib/middleware/integration-headers.test.ts
Normal file
29
src/lib/middleware/integration-headers.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { determineIntegrationSource } from './integration-headers';
|
||||||
|
|
||||||
|
test('resolves known user agents to source labels', () => {
|
||||||
|
expect(determineIntegrationSource('axios/0.27.2')).toBe('Axios');
|
||||||
|
expect(determineIntegrationSource('axios/1.4.0')).toBe('Axios');
|
||||||
|
expect(determineIntegrationSource('curl/8.6.0')).toBe('Curl');
|
||||||
|
expect(determineIntegrationSource('node-fetch/1.0.0')).toBe('Node');
|
||||||
|
expect(determineIntegrationSource('node')).toBe('Node');
|
||||||
|
expect(determineIntegrationSource('python-requests/2.31.0')).toBe('Python');
|
||||||
|
expect(determineIntegrationSource('Terraform-Provider-Unleash/1.1.1')).toBe(
|
||||||
|
'TerraformUnleash',
|
||||||
|
);
|
||||||
|
expect(determineIntegrationSource('Jira-Cloud-Unleash')).toBe(
|
||||||
|
'JiraCloudUnleash',
|
||||||
|
);
|
||||||
|
expect(determineIntegrationSource('OpenAPI-Generator/1.0.0/go')).toBe(
|
||||||
|
'OpenAPIGO',
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
determineIntegrationSource('Apache-HttpClient/4.5.13 (Java/11.0.22)'),
|
||||||
|
).toBe('Java');
|
||||||
|
expect(determineIntegrationSource('Go-http-client/1.1')).toBe('Go');
|
||||||
|
expect(
|
||||||
|
determineIntegrationSource(
|
||||||
|
'rest-client/2.0.2 (linux-gnu x86_64) ruby/2.1.7p400',
|
||||||
|
),
|
||||||
|
).toBe('RestClientRuby');
|
||||||
|
expect(determineIntegrationSource('No-http-client')).toBe('Other');
|
||||||
|
});
|
34
src/lib/middleware/integration-headers.ts
Normal file
34
src/lib/middleware/integration-headers.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import type { Request } from 'express';
|
||||||
|
|
||||||
|
const ORIGIN = 'origin';
|
||||||
|
const httpMatcher = /^https?:\/\//;
|
||||||
|
const userAgentMatches = [
|
||||||
|
{ label: 'Axios', matcher: /^axios/ },
|
||||||
|
{ label: 'Curl', matcher: /^curl/ },
|
||||||
|
{ label: 'Go', matcher: /^Go-http-client/ },
|
||||||
|
{ label: 'Python', matcher: /^python-requests/ },
|
||||||
|
{ label: 'Node', matcher: /^node/ },
|
||||||
|
{ label: 'Java', matcher: /^Apache-HttpClient.*Java/ },
|
||||||
|
{ label: 'JiraCloudUnleash', matcher: /^Jira-Cloud-Unleash/ },
|
||||||
|
{ label: 'TerraformUnleash', matcher: /^Terraform-Provider-Unleash/ },
|
||||||
|
{ label: 'OpenAPIGO', matcher: /^OpenAPI-Generator\/.*\/go/ },
|
||||||
|
{ label: 'RestClientRuby', matcher: /^rest-client\/.*ruby/ },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getFilteredOrigin = (request: Request): string | undefined => {
|
||||||
|
const origin = request.headers[ORIGIN];
|
||||||
|
if (origin && httpMatcher.test(origin)) {
|
||||||
|
return origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const determineIntegrationSource = (
|
||||||
|
userAgent: string,
|
||||||
|
): string | undefined => {
|
||||||
|
return (
|
||||||
|
userAgentMatches.find((candidate) => candidate.matcher.test(userAgent))
|
||||||
|
?.label ?? 'Other'
|
||||||
|
);
|
||||||
|
};
|
@ -69,6 +69,7 @@ describe('originMiddleware', () => {
|
|||||||
expect(eventBus.emit).toHaveBeenCalledWith(REQUEST_ORIGIN, {
|
expect(eventBus.emit).toHaveBeenCalledWith(REQUEST_ORIGIN, {
|
||||||
type: 'API',
|
type: 'API',
|
||||||
method: req.method,
|
method: req.method,
|
||||||
|
source: 'Other',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ describe('originMiddleware', () => {
|
|||||||
expect(loggerMock.info).toHaveBeenCalledWith('API request', {
|
expect(loggerMock.info).toHaveBeenCalledWith('API request', {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
userAgent: TEST_USER_AGENT,
|
userAgent: TEST_USER_AGENT,
|
||||||
|
origin: undefined,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import type { Request, Response, NextFunction } from 'express';
|
import type { Request, Response, NextFunction } from 'express';
|
||||||
import type { IUnleashConfig } from '../types';
|
import type { IUnleashConfig } from '../types';
|
||||||
import { REQUEST_ORIGIN, emitMetricEvent } from '../metric-events';
|
import { REQUEST_ORIGIN, emitMetricEvent } from '../metric-events';
|
||||||
|
import {
|
||||||
|
determineIntegrationSource,
|
||||||
|
getFilteredOrigin,
|
||||||
|
} from './integration-headers';
|
||||||
|
|
||||||
export const originMiddleware = ({
|
export const originMiddleware = ({
|
||||||
getLogger,
|
getLogger,
|
||||||
@ -23,13 +27,19 @@ export const originMiddleware = ({
|
|||||||
method: req.method,
|
method: req.method,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
const userAgent = req.headers['user-agent'];
|
||||||
|
const uaLabel = userAgent
|
||||||
|
? determineIntegrationSource(userAgent)
|
||||||
|
: 'Other';
|
||||||
logger.info('API request', {
|
logger.info('API request', {
|
||||||
method: req.method,
|
method: req.method,
|
||||||
userAgent: req.headers['user-agent'],
|
userAgent: req.headers['user-agent'],
|
||||||
|
origin: getFilteredOrigin(req),
|
||||||
});
|
});
|
||||||
emitMetricEvent(eventBus, REQUEST_ORIGIN, {
|
emitMetricEvent(eventBus, REQUEST_ORIGIN, {
|
||||||
type: 'API',
|
type: 'API',
|
||||||
method: req.method,
|
method: req.method,
|
||||||
|
source: uaLabel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user