forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[APM] adds telemetry to APM (elastic#25513)
* [APM] adds telemetry to APM * [APM] Code and readability improvements for APM Telemetry * [APM] fixed failing tests for apm-telemetry and service routes * [APM] fix lint issues for APM Telemetry
- Loading branch information
Showing
7 changed files
with
335 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"apm-telemetry": { | ||
"properties": { | ||
"has_any_services": { | ||
"type": "boolean" | ||
}, | ||
"services_per_agent": { | ||
"properties": { | ||
"python": { | ||
"type": "long", | ||
"null_value": 0 | ||
}, | ||
"java": { | ||
"type": "long", | ||
"null_value": 0 | ||
}, | ||
"nodejs": { | ||
"type": "long", | ||
"null_value": 0 | ||
}, | ||
"js-base": { | ||
"type": "long", | ||
"null_value": 0 | ||
}, | ||
"ruby": { | ||
"type": "long", | ||
"null_value": 0 | ||
}, | ||
"go": { | ||
"type": "long", | ||
"null_value": 0 | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
152 changes: 152 additions & 0 deletions
152
x-pack/plugins/apm/server/lib/apm_telemetry/__test__/apm_telemetry.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { | ||
AgentName, | ||
APM_TELEMETRY_DOC_ID, | ||
ApmTelemetry, | ||
createApmTelementry, | ||
getSavedObjectsClient, | ||
storeApmTelemetry | ||
} from '../apm_telemetry'; | ||
|
||
describe('apm_telemetry', () => { | ||
describe('createApmTelementry', () => { | ||
it('should create a ApmTelemetry object with boolean flag and frequency map of the given list of AgentNames', () => { | ||
const apmTelemetry = createApmTelementry([ | ||
AgentName.GoLang, | ||
AgentName.NodeJs, | ||
AgentName.GoLang, | ||
AgentName.JsBase | ||
]); | ||
expect(apmTelemetry.has_any_services).toBe(true); | ||
expect(apmTelemetry.services_per_agent).toMatchObject({ | ||
[AgentName.GoLang]: 2, | ||
[AgentName.NodeJs]: 1, | ||
[AgentName.JsBase]: 1 | ||
}); | ||
}); | ||
it('should ignore undefined or unknown AgentName values', () => { | ||
const apmTelemetry = createApmTelementry([ | ||
AgentName.GoLang, | ||
AgentName.NodeJs, | ||
AgentName.GoLang, | ||
AgentName.JsBase, | ||
'example-platform' as any, | ||
undefined as any | ||
]); | ||
expect(apmTelemetry.services_per_agent).toMatchObject({ | ||
[AgentName.GoLang]: 2, | ||
[AgentName.NodeJs]: 1, | ||
[AgentName.JsBase]: 1 | ||
}); | ||
}); | ||
}); | ||
|
||
describe('storeApmTelemetry', () => { | ||
let server: any; | ||
let apmTelemetry: ApmTelemetry; | ||
let savedObjectsClientInstance: any; | ||
|
||
beforeEach(() => { | ||
savedObjectsClientInstance = { create: jest.fn() }; | ||
const callWithInternalUser = jest.fn(); | ||
const internalRepository = jest.fn(); | ||
server = { | ||
savedObjects: { | ||
SavedObjectsClient: jest.fn(() => savedObjectsClientInstance), | ||
getSavedObjectsRepository: jest.fn(() => internalRepository) | ||
}, | ||
plugins: { | ||
elasticsearch: { | ||
getCluster: jest.fn(() => ({ callWithInternalUser })) | ||
} | ||
} | ||
}; | ||
apmTelemetry = { | ||
has_any_services: true, | ||
services_per_agent: { | ||
[AgentName.GoLang]: 2, | ||
[AgentName.NodeJs]: 1, | ||
[AgentName.JsBase]: 1 | ||
} | ||
}; | ||
}); | ||
|
||
it('should call savedObjectsClient create with the given ApmTelemetry object', () => { | ||
storeApmTelemetry(server, apmTelemetry); | ||
expect(savedObjectsClientInstance.create.mock.calls[0][1]).toBe( | ||
apmTelemetry | ||
); | ||
}); | ||
|
||
it('should call savedObjectsClient create with the apm-telemetry document type and ID', () => { | ||
storeApmTelemetry(server, apmTelemetry); | ||
expect(savedObjectsClientInstance.create.mock.calls[0][0]).toBe( | ||
'apm-telemetry' | ||
); | ||
expect(savedObjectsClientInstance.create.mock.calls[0][2].id).toBe( | ||
APM_TELEMETRY_DOC_ID | ||
); | ||
}); | ||
|
||
it('should call savedObjectsClient create with overwrite: true', () => { | ||
storeApmTelemetry(server, apmTelemetry); | ||
expect(savedObjectsClientInstance.create.mock.calls[0][2].overwrite).toBe( | ||
true | ||
); | ||
}); | ||
}); | ||
|
||
describe('getSavedObjectsClient', () => { | ||
let server: any; | ||
let savedObjectsClientInstance: any; | ||
let callWithInternalUser: any; | ||
let internalRepository: any; | ||
|
||
beforeEach(() => { | ||
savedObjectsClientInstance = { create: jest.fn() }; | ||
callWithInternalUser = jest.fn(); | ||
internalRepository = jest.fn(); | ||
server = { | ||
savedObjects: { | ||
SavedObjectsClient: jest.fn(() => savedObjectsClientInstance), | ||
getSavedObjectsRepository: jest.fn(() => internalRepository) | ||
}, | ||
plugins: { | ||
elasticsearch: { | ||
getCluster: jest.fn(() => ({ callWithInternalUser })) | ||
} | ||
} | ||
}; | ||
}); | ||
|
||
it('should use internal user "admin"', () => { | ||
getSavedObjectsClient(server); | ||
|
||
expect(server.plugins.elasticsearch.getCluster).toHaveBeenCalledWith( | ||
'admin' | ||
); | ||
}); | ||
|
||
it('should call getSavedObjectsRepository with a cluster using the internal user context', () => { | ||
getSavedObjectsClient(server); | ||
|
||
expect( | ||
server.savedObjects.getSavedObjectsRepository | ||
).toHaveBeenCalledWith(callWithInternalUser); | ||
}); | ||
|
||
it('should return a SavedObjectsClient initialized with the saved objects internal repository', () => { | ||
const result = getSavedObjectsClient(server); | ||
|
||
expect(result).toBe(savedObjectsClientInstance); | ||
expect(server.savedObjects.SavedObjectsClient).toHaveBeenCalledWith( | ||
internalRepository | ||
); | ||
}); | ||
}); | ||
}); |
59 changes: 59 additions & 0 deletions
59
x-pack/plugins/apm/server/lib/apm_telemetry/apm_telemetry.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { Server } from 'hapi'; | ||
import { countBy } from 'lodash'; | ||
|
||
// Support telemetry for additional agent types by appending definitions in | ||
// mappings.json and the AgentName enum. | ||
|
||
export enum AgentName { | ||
Python = 'python', | ||
Java = 'java', | ||
NodeJs = 'nodejs', | ||
JsBase = 'js-base', | ||
Ruby = 'ruby', | ||
GoLang = 'go' | ||
} | ||
|
||
export interface ApmTelemetry { | ||
has_any_services: boolean; | ||
services_per_agent: { [agentName in AgentName]?: number }; | ||
} | ||
|
||
export const APM_TELEMETRY_DOC_ID = 'apm-telemetry'; | ||
|
||
export function createApmTelementry( | ||
agentNames: AgentName[] = [] | ||
): ApmTelemetry { | ||
const validAgentNames = agentNames.filter(agentName => | ||
Object.values(AgentName).includes(agentName) | ||
); | ||
return { | ||
has_any_services: validAgentNames.length > 0, | ||
services_per_agent: countBy(validAgentNames) | ||
}; | ||
} | ||
|
||
export function storeApmTelemetry( | ||
server: Server, | ||
apmTelemetry: ApmTelemetry | ||
): void { | ||
const savedObjectsClient = getSavedObjectsClient(server); | ||
savedObjectsClient.create('apm-telemetry', apmTelemetry, { | ||
id: APM_TELEMETRY_DOC_ID, | ||
overwrite: true | ||
}); | ||
} | ||
|
||
export function getSavedObjectsClient(server: Server): any { | ||
const { SavedObjectsClient, getSavedObjectsRepository } = server.savedObjects; | ||
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster( | ||
'admin' | ||
); | ||
const internalRepository = getSavedObjectsRepository(callWithInternalUser); | ||
return new SavedObjectsClient(internalRepository); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
export { | ||
ApmTelemetry, | ||
AgentName, | ||
storeApmTelemetry, | ||
createApmTelementry, | ||
APM_TELEMETRY_DOC_ID | ||
} from './apm_telemetry'; | ||
export { makeApmUsageCollector } from './make_apm_usage_collector'; |
42 changes: 42 additions & 0 deletions
42
x-pack/plugins/apm/server/lib/apm_telemetry/make_apm_usage_collector.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { Server } from 'hapi'; | ||
import { | ||
APM_TELEMETRY_DOC_ID, | ||
ApmTelemetry, | ||
createApmTelementry, | ||
getSavedObjectsClient | ||
} from './apm_telemetry'; | ||
|
||
// TODO this type should be defined by the platform | ||
interface KibanaHapiServer extends Server { | ||
usage: { | ||
collectorSet: { | ||
makeUsageCollector: any; | ||
register: any; | ||
}; | ||
}; | ||
} | ||
|
||
export function makeApmUsageCollector(server: KibanaHapiServer): void { | ||
const apmUsageCollector = server.usage.collectorSet.makeUsageCollector({ | ||
type: 'apm', | ||
fetch: async (): Promise<ApmTelemetry> => { | ||
const savedObjectsClient = getSavedObjectsClient(server); | ||
try { | ||
const apmTelemetrySavedObject = await savedObjectsClient.get( | ||
'apm-telemetry', | ||
APM_TELEMETRY_DOC_ID | ||
); | ||
return apmTelemetrySavedObject.attributes; | ||
} catch (err) { | ||
return createApmTelementry(); | ||
} | ||
} | ||
}); | ||
server.usage.collectorSet.register(apmUsageCollector); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters