-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first pass of basic osquery usage stats collection
- Loading branch information
Showing
12 changed files
with
390 additions
and
23 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
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,8 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
export * from './recorder' |
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,48 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
export interface RouteUsageMetric { | ||
call_count: number; | ||
error_count: number; | ||
} | ||
|
||
// TODO: use ES for this recording | ||
class UsageRecorder { | ||
private counts = new Map<string, number>() | ||
public incrementCallCount(route: string, increment: number = 1) { | ||
const count = this.counts.get(route) ?? 0 | ||
this.counts.set(route, count + increment) | ||
} | ||
public getCallCount(route: string): number { | ||
return this.counts.get(route) ?? 0; | ||
} | ||
|
||
private errors = new Map<string, number>() | ||
public incrementErrorCount(route: string, increment: number = 1) { | ||
const count = this.errors.get(route) ?? 0 | ||
this.errors.set(route, count + increment) | ||
} | ||
public getErrorCount(route: string): number { | ||
return this.errors.get(route) ?? 0; | ||
} | ||
|
||
public getRouteMetric(route: string): RouteUsageMetric { | ||
return { | ||
call_count: this.getCallCount(route), | ||
error_count: this.getErrorCount(route) | ||
} | ||
} | ||
} | ||
|
||
let usageRecorder: UsageRecorder; | ||
|
||
export const getUsageRecorder = (): UsageRecorder => { | ||
if (usageRecorder == null) { | ||
usageRecorder = new UsageRecorder() | ||
} | ||
return usageRecorder | ||
} |
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,39 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { CoreSetup } from '../../../../../src/core/server'; | ||
import { CollectorFetchContext } from '../../../../../src/plugins/usage_collection/server'; | ||
import { getBeatUsage, getLiveQueryUsage } from './fetchers'; | ||
import { CollectorDependencies, usageSchema } from './types'; | ||
|
||
export type RegisterCollector = (deps: CollectorDependencies) => void; | ||
export async function getInternalSavedObjectsClient(core: CoreSetup) { | ||
return core.getStartServices().then(async ([coreStart]) => { | ||
return coreStart.savedObjects.createInternalRepository(); | ||
}); | ||
} | ||
|
||
export const registerCollector: RegisterCollector = ({ usageCollection }) => { | ||
if (!usageCollection) { | ||
return; | ||
} | ||
const collector = usageCollection.makeUsageCollector<any>({ | ||
type: 'osquery', | ||
schema: usageSchema, | ||
isReady: () => true, | ||
fetch: async ({ esClient }: CollectorFetchContext): Promise<any> => { | ||
return { | ||
beat_metrics: { | ||
usage: await getBeatUsage(esClient), | ||
}, | ||
live_query_usage: getLiveQueryUsage(), | ||
}; | ||
}, | ||
}); | ||
|
||
usageCollection.registerCollector(collector); | ||
}; |
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,8 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
export const METRICS_INDICES = 'logs-elastic_agent.osquerybeat*'; |
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,121 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { | ||
SingleBucketAggregate, | ||
TopHitsAggregate, | ||
ValueAggregate, | ||
} from '@elastic/elasticsearch/api/types'; | ||
import {getUsageRecorder} from '../routes/usage' | ||
import { ElasticsearchClient } from '../../../../../src/core/server'; | ||
import { METRICS_INDICES } from './constants'; | ||
|
||
export interface MetricEntry { | ||
max?: number; | ||
latest?: number; | ||
avg?: number; | ||
} | ||
|
||
export interface BeatMetricAggregation { | ||
rss: MetricEntry; | ||
cpuMs: MetricEntry; | ||
} | ||
|
||
// TODO: pipe this through ES | ||
export function getLiveQueryUsage() { | ||
const usageRecorder = getUsageRecorder() | ||
return usageRecorder.getRouteMetric('live_query') | ||
} | ||
|
||
export async function getBeatUsage(esClient: ElasticsearchClient) { | ||
// is there a better way to get these aggregates? | ||
// needs a time window limit to make sure the reports are fresh | ||
// XXX: these aggregates conflate agents, they should be broken out by id | ||
// XXX: currently cpu is recorded as a duration rather than a load % | ||
const { body: metricResponse } = await esClient.search({ | ||
body: { | ||
size: 0, | ||
aggs: { | ||
lastDay: { | ||
filter: { | ||
range: { | ||
'@timestamp': { | ||
gte: 'now-24h', | ||
lte: 'now', | ||
}, | ||
}, | ||
}, | ||
aggs: { | ||
latest: { | ||
top_hits: { | ||
sort: [ | ||
{ | ||
'@timestamp': { | ||
order: 'desc', | ||
}, | ||
}, | ||
], | ||
size: 1, | ||
}, | ||
}, | ||
max_rss: { | ||
max: { | ||
field: 'monitoring.metrics.beat.memstats.rss', | ||
}, | ||
}, | ||
avg_rss: { | ||
avg: { | ||
field: 'monitoring.metrics.beat.memstats.rss', | ||
}, | ||
}, | ||
max_cpu: { | ||
max: { | ||
field: 'monitoring.metrics.beat.cpu.total.time.ms', | ||
}, | ||
}, | ||
avg_cpu: { | ||
avg: { | ||
field: 'monitoring.metrics.beat.cpu.total.time.ms', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
index: METRICS_INDICES, | ||
}); | ||
const lastDayAggs = metricResponse.aggregations?.lastDay as SingleBucketAggregate; | ||
const result: BeatMetricAggregation = { | ||
rss: {}, | ||
cpuMs: {}, | ||
}; | ||
|
||
// XXX: discrimating the union types gets hairy when attempting to genericize, figure out a fix! | ||
if ('max_rss' in lastDayAggs) { | ||
result.rss.max = (lastDayAggs.max_rss as ValueAggregate).value | ||
} | ||
|
||
if ('avg_rss' in lastDayAggs) { | ||
result.rss.avg = (lastDayAggs.max_rss as ValueAggregate).value | ||
} | ||
|
||
if ('max_cpu' in lastDayAggs) { | ||
result.cpuMs.max = (lastDayAggs.max_cpu as ValueAggregate).value | ||
} | ||
|
||
if ('avg_cpu' in lastDayAggs) { | ||
result.cpuMs.avg = (lastDayAggs.max_cpu as ValueAggregate).value | ||
} | ||
|
||
if ('latest' in lastDayAggs) { | ||
const latest = (lastDayAggs.latest as TopHitsAggregate).hits.hits[0]?._source?.monitoring.metrics.beat; | ||
result.cpuMs.latest = latest.cpu.total.time.ms; | ||
result.rss.latest = latest.memstats.rss; | ||
} | ||
|
||
return result; | ||
} |
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,15 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { CollectorDependencies } from './types'; | ||
import { registerCollector } from './collector'; | ||
|
||
export type InitUsageCollectors = (deps: CollectorDependencies) => void; | ||
|
||
export const initUsageCollectors: InitUsageCollectors = (dependencies) => { | ||
registerCollector(dependencies); | ||
}; |
Oops, something went wrong.