**Filebeat => Logstash => Elasticsearch <= Kibana**
Having a dashboard in place before starting data injection is advisable: so congiguring kibana should the first step. This should be done with the provided bash script which also will correctly set up the dashboard in kibana:
cd kibana
chmod +x ./install-dashboard.sh
./install-dashboard.sh -h=https://localhost:5601 -n=MyCompany -u=elastic -s=geoserver-space
To make a default.ndjson file compatibile with install-dashboard.sh
all you need to do is:
-
export from kibana the dashboard with all its related objects, save it as
default.ndjson
. -
if you changed CustomerNamePlaceHolder to MyCompany with the
install-dashboard.sh
script explained here issue:sed -i "s/PubliAcqua/CustomerNamePlaceHolder/g" default.ndjson
-
test it making a new empty space (i.e. "geoserver-test-space") in kibana, upload your default.ndjson, checking that index pattern and each other dashobard objects, including the ones added/modified are working:
./install-dashboard.sh -h=https://localhost:5601 -n=MyCompany -u=elastic -s=geoserver-test-space
-
Once everything looks fine you may share your
default.ndjson
making a PR
Here you can find some examples to configure logstash in a way to normalize GeoServer logs and audits correctly.
Inside logstash.conf
configure input and output stanzas as needed
logstash/pipelines/logstash.conf
logstash/patterns/{geoserver-audit,geoserver-log}
Configuring logstash.yml is out of the scope of the logstash filters for Geoserver, for kubernetes you can probably go fine with the default provided.
For default filebeat installation you should just need to copy files in place and restart filebeat:
cp filebeat/conf.d/{geoserver-audit,geoserver-log} /etc/filebeat/conf.d/
The official logstash Docker image may be used to test for logstash configuration compliance. You may want to remove options "--debug --config.debug"
from the command if you are testing it automatically dropping unneeded verbosity.
docker run --rm -v $HOME/Development/analytics/logstash/pipelines:/checkvolume docker.elastic.co/logstash/logstash:7.8.1 logstash --debug --config.debug --config.test_and_exit -f /checkvolume/logstash.conf
- https://grokconstructor.appspot.com/
- https://grokdebug.herokuapp.com/
- Embedded Kibana Grok Debugger
Tip for a nicely formatted configmap (applying this config map may produce a single liner in the logstash.conf: section of the yaml) you may treasure this for any config map using configuration files:
kubectl get -o yaml cm [YOUR CONFIGMAP NAME] | sed -E 's/[[:space:]]+\\n/\\n/g' | kubectl apply -f -
Example config map for logstash 7.8.x deployed in kubernetes:
apiVersion: v1
kind: ConfigMap
data:
geoserver-audit: |
LAYERS (?:[\w\s:,-]+)
USERNAME (?:[\w]+)
ERRORMESSAGE (?:[\w]+)
SERVICEVERSION (?:[\d\.]+)
QUERYSTRING (?:[-A-Za-z0-9 &='.,;:_/+#]+)
BBOX %{NUMBER:BBox1:float},%{NUMBER:BBox2:float},%{NUMBER:BBox3:float},%{NUMBER:BBox4:float}
GEOSERVER_AUDIT (%{INT:RequestId})?,(%{IPORHOST:ServerHost})?,(%{WORD:Service})?,(%{SERVICEVERSION:ServiceVersion})?,(%{WORD:Operation})?,(%{WORD:SubOperation})?,(\"%{LAYERS:Layers}\")?,(\"%{BBOX:BBox}\")?,(\"%{URIPATH:RequestPath}\")?,(\"%{QUERYSTRING:QueryString}\")?,\"(%{DATA:RequestBody})?\",(%{WORD:RequestMethod})?,(\"%{TIMESTAMP_ISO8601:StartTime}\")?,(\"%{TIMESTAMP_ISO8601:EndTime}\")?,(%{POSINT:ResponseTime:int})?,(\"(%{IPORHOST:ClientAddress})?(:)?(%{NUMBER:ClientPort})?\")?,(\")?(%{USERNAME:User})?(\")?,(%{QS:UserAgent})?,(%{POSINT:ResponseHTTPStatus:int})?,(%{POSINT:ResponseLength:int})?,(%{QS:ResponseContentType})?,(\"%{WORD:Error}\")?,(\"%{ERRORMESSAGE:ErrorMessage}\")?
geoserver-log: |
TIMESTAMP_GSLOG %{YEAR}-%{MONTHNUM}-%{MONTHDAY} %{HOUR}:%{MINUTE}:%{SECOND},%{INT}?
GSLOG %{TIMESTAMP_GSLOG} %{WORD:LogLevel} %{GREEDYDATA:logMessage}
logstash.conf: |
input {
beats {
port => 5044
}
}
output {
elasticsearch {
index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM}"
hosts => [ "${ES_HOSTS}" ]
user => "${ES_USER}"
password => "${ES_PASSWORD}"
cacert => '/etc/logstash/certificates/ca.crt'
}
}
filter {
mutate {
add_field => { "client_name" => "ProjectName" }
}
if "geoserver-audit" in [type] {
grok {
patterns_dir => ["/usr/share/logstash/patterns/"]
match => { "message" => "%{INT:RequestId},%{IPORHOST:ServerHost},(%{WORD:Service})?,((?<ServiceVersion>[\d\.]+))?,(%{WORD:Operation})?,(%{WORD:SubOperation})?,\"((?<Layers>[-\w\s:,]+))?\",(\"%{BASE10NUM:BBox1:float},%{BASE10NUM:BBox2:float},%{BASE10NUM:BBox3:float},%{BASE10NUM:BBox4:float}\")?,\"(%{URIPATH:RequestPath})?\",\"((?<QueryString>[-?A-Za-z0-9&='<> ().,;:_/+#]+))?\",\"(%{DATA:RequestBody})?\",%{WORD:RequestMethod},\"%{TIMESTAMP_ISO8601:StartTime}\",\"%{TIMESTAMP_ISO8601:EndTime}\",(%{NUMBER:ResponseTime:int})?,\"%{IPORHOST:ClientAddress}(:)?(%{NUMBER:ClientPort})?\",%{QS:remoteUser},%{QS:UserAgent},%{NUMBER:ResponseHTTPStatus:int},%{NUMBER:ResponseLength:int},%{QS:ResponseContentType},\"(%{WORD:geowebcache-cache-result})?\",(%{QS:geowebcache-cache-miss-reason})?,\"(%{WORD:Error})?\",(%{QS:ErrorMessage})?"
}
add_tag => [ "grokked"]
}
kv {
source => "QueryString"
field_split => "&"
transform_key => "uppercase"
}
mutate {
lowercase => [ "Error"]
uppercase => [ "Service"]
rename => [ "Service", "SERVICE"]
convert => { "RequestId" => "integer"}
convert => { "WIDTH" => "integer"}
convert => { "HEIGHT" => "integer"}
convert => { "Error" => "boolean"}
add_tag => [ "geoserver", "audit", "geoserver-audit" ]
}
if ![TILED] {
mutate { add_field => { "TILED" => "false" } }
}
mutate {
convert => { "TILED" => "boolean"}
}
date {
match => ["StartTime", "ISO8601"]
target => "@timestamp"
add_tag => ["dated"]
}
geoip {
source => "ClientAddress"
target => "geoip"
add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}" ]
}
}
else if "geoserver-logs" in [type] {
mutate {
add_tag => [ "geoserver", "log", "geoserver-logs", "geoserver-log"]
}
grok {
patterns_dir => [ "/usr/share/logstash/patterns/" ]
match => {
"message" =>[ "%{GSLOG}" ]
}
add_tag => ["grokked"]
}
if "DEBUG" in [logLevel] or "TRACE" in [logLevel] or "INFO" in [logLevel] {
drop { }
}
}
}
logstash.yml: |
http.host: "0.0.0.0"
path.config: /usr/share/logstash/pipeline
In case there is the need of a dedicated user for the dashboard one can create them by issuing this json code to elasticsearch, first declare a role change geoserver-space
accordigly on how you installed the dashboard:
PUT /api/security/role/kibana_ro_role
{
"elasticsearch": {
"cluster" : [ ],
"indices" : [ ]
},
"kibana": [
{
"base": [],
"feature": {
"visualize": ["all"],
"dashboard": ["read", "url_create"]
},
"spaces": ["geoserver-space"]
}
]
}
Create an user with kibana role
POST /_security/user/jacknich
{
"password" : "j@rV1s",
"roles" : [ "kibana_ro_role" ],
"full_name" : "Jack Nicholson",
"email" : "jacknich@example.com"
}
Test user login
curl -u jacknich:j@rV1s http://localhost:9200/_cluster/health