Skip to content

Commit

Permalink
Metricbeat: Support multiple apache versions
Browse files Browse the repository at this point in the history
Apache before version 2.4.16 had less information in its status
page. Add support to both, old and new status pages.
  • Loading branch information
jsoriano committed Mar 19, 2018
1 parent dcab193 commit db217fa
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ https://github.com/elastic/beats/compare/v6.0.0-beta2...master[Check the HEAD di
- Add filtering option by exact device names in system.diskio. `diskio.include_devices`. {pull}6085[6085]
- Fix dealing with new process status codes in Linux kernel 4.14+. {pull}6306[6306]
- Add config option for windows/perfmon metricset to ignore non existent counters. {pull}6432[6432]
- Support apache status pages for versions older than 2.4.16. {pull}6450[6450]

*Packetbeat*

Expand Down
5 changes: 5 additions & 0 deletions metricbeat/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ services:
apache:
build: ./module/apache/_meta

apache_2_4_12:
build:
context: ./module/apache/_meta
dockerfile: Dockerfile.2.4.12

ceph:
build: ./module/ceph/_meta

Expand Down
4 changes: 2 additions & 2 deletions metricbeat/docs/modules/apache.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ HTTPD] servers.
[float]
=== Compatibility

The Apache metricsets were tested with Apache 2.4.20 and are expected to work with all versions
>= 2.2.31 and >= 2.4.16.
The Apache metricsets were tested with Apache 2.4.12 and 2.4.20 and are expected to work with
all versions >= 2.2.31 and >= 2.4.16.


[float]
Expand Down
4 changes: 4 additions & 0 deletions metricbeat/module/apache/_meta/Dockerfile.2.4.12
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM httpd:2.4.12
RUN apt-get update && apt-get install -y curl
HEALTHCHECK --interval=1s --retries=90 CMD curl -f http://localhost
COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf
4 changes: 2 additions & 2 deletions metricbeat/module/apache/_meta/docs.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ HTTPD] servers.
[float]
=== Compatibility

The Apache metricsets were tested with Apache 2.4.20 and are expected to work with all versions
>= 2.2.31 and >= 2.4.16.
The Apache metricsets were tested with Apache 2.4.12 and 2.4.20 and are expected to work with
all versions >= 2.2.31 and >= 2.4.16.


[float]
Expand Down
1 change: 1 addition & 0 deletions metricbeat/module/apache/_meta/env
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
APACHE_HOST=apache
APACHE_OLD_HOST=apache_2_4_12
APACHE_PORT=80
35 changes: 33 additions & 2 deletions metricbeat/module/apache/status/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,40 @@ var (
"15": c.Float("Load15", s.Optional),
},
}

// Schema used till apache 2.4.12
schemaOld = s.Schema{
"total_accesses": c.Int("Total Accesses"),
"total_kbytes": c.Int("Total kBytes"),
"requests_per_sec": c.Float("ReqPerSec", s.Optional),
"bytes_per_sec": c.Float("BytesPerSec", s.Optional),
"workers": s.Object{
"busy": c.Int("BusyWorkers"),
"idle": c.Int("IdleWorkers"),
},
"uptime": s.Object{
"uptime": c.Int("Uptime"),
},
"connections": s.Object{
"total": c.Int("ConnsTotal", s.Optional),
"async": s.Object{
"writing": c.Int("ConnsAsyncWriting", s.Optional),
"keep_alive": c.Int("ConnsAsyncKeepAlive", s.Optional),
"closing": c.Int("ConnsAsyncClosing", s.Optional),
},
},
}
)

func applySchema(event common.MapStr, fullEvent map[string]interface{}) *s.Errors {
applicableSchema := schema
if _, found := fullEvent["ServerUptimeSeconds"]; !found {
applicableSchema = schemaOld
}
_, err := applicableSchema.ApplyTo(event, fullEvent)
return err
}

// Map body to MapStr
func eventMapping(scanner *bufio.Scanner, hostname string) (common.MapStr, *s.Errors) {
var (
Expand Down Expand Up @@ -140,9 +172,8 @@ func eventMapping(scanner *bufio.Scanner, hostname string) (common.MapStr, *s.Er
"total": totalAll,
},
}
_, err := schema.ApplyTo(event, fullEvent)

return event, err
return event, applySchema(event, fullEvent)
}

/*
Expand Down
2 changes: 1 addition & 1 deletion metricbeat/module/apache/status/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ func TestFetchEventContents(t *testing.T) {
assert.EqualValues(t, 63, event["total_kbytes"])

uptime := event["uptime"].(common.MapStr)
assert.EqualValues(t, 1026782, uptime["server_uptime"])
assert.EqualValues(t, 1026782, uptime["uptime"])
assert.EqualValues(t, 1026782, uptime["server_uptime"])
}

// TestFetchTimeout verifies that the HTTP request times out and an error is
Expand Down
56 changes: 43 additions & 13 deletions metricbeat/tests/system/test_apache.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@

APACHE_FIELDS = metricbeat.COMMON_FIELDS + ["apache"]

APACHE_STATUS_FIELDS = ["hostname", "total_accesses", "total_kbytes",
"requests_per_sec", "bytes_per_sec", "bytes_per_request",
"workers.busy", "workers.idle", "uptime", "cpu",
"connections", "load", "scoreboard"]
APACHE_STATUS_FIELDS = [
"hostname", "total_accesses", "total_kbytes",
"requests_per_sec", "bytes_per_sec", "bytes_per_request",
"workers.busy", "workers.idle", "uptime", "cpu",
"connections", "load", "scoreboard"
]

CPU_FIELDS = ["load", "user", "system", "children_user",
"children_system"]
APACHE_OLD_STATUS_FIELDS = [
"hostname", "total_accesses", "total_kbytes",
"requests_per_sec", "bytes_per_sec",
"workers.busy", "workers.idle", "uptime",
"connections", "scoreboard"
]


CPU_FIELDS = [
"load", "user", "system", "children_user", "children_system"
]


class ApacheStatusTest(metricbeat.BaseTest):
Expand All @@ -37,7 +48,7 @@ def test_output(self):

found = False
# Waits until CPULoad is part of the status
while found == False:
while not found:
res = urllib2.urlopen(hosts[0] + "/server-status?auto").read()
if "CPULoad" in res:
found = True
Expand All @@ -52,16 +63,35 @@ def test_output(self):
self.assertEqual(len(output), 1)
evt = output[0]

# Verify the required fields are present.
self.assertItemsEqual(self.de_dot(APACHE_FIELDS), evt.keys())
apache_status = evt["apache"]["status"]
self.assertItemsEqual(self.de_dot(APACHE_STATUS_FIELDS), apache_status.keys())
self.assertItemsEqual(self.de_dot(CPU_FIELDS), apache_status["cpu"].keys())
# There are more fields that could be checked.
self.verify_fields(evt)

# Verify all fields present are documented.
self.assert_fields_are_documented(evt)

def verify_fields(self, evt):
self.assertItemsEqual(self.de_dot(APACHE_FIELDS), evt.keys())
apache_status = evt["apache"]["status"]
self.assertItemsEqual(
self.de_dot(APACHE_STATUS_FIELDS), apache_status.keys())
self.assertItemsEqual(
self.de_dot(CPU_FIELDS), apache_status["cpu"].keys())
# There are more fields that could be checked.

def get_hosts(self):
return ['http://' + os.getenv('APACHE_HOST', 'localhost') + ':' +
os.getenv('APACHE_PORT', '80')]


class ApacheOldStatusTest(ApacheStatusTest):

COMPOSE_SERVICES = ['apache_2_4_12']

def verify_fields(self, evt):
self.assertItemsEqual(self.de_dot(APACHE_FIELDS), evt.keys())
apache_status = evt["apache"]["status"]
self.assertItemsEqual(
self.de_dot(APACHE_OLD_STATUS_FIELDS), apache_status.keys())

def get_hosts(self):
return ['http://' + os.getenv('APACHE_OLD_HOST', 'localhost') + ':' +
os.getenv('APACHE_PORT', '80')]

0 comments on commit db217fa

Please sign in to comment.