Metabrik-Repository/lib/Metabrik/Server/Logstash.pm
#
# $Id$
#
# server::logstash Brik
#
package Metabrik::Server::Logstash;
use strict;
use warnings;
use base qw(Metabrik::System::Process);
sub brik_properties {
return {
revision => '$Revision$',
tags => [ qw(unstable elk) ],
author => 'GomoR <GomoR[at]metabrik.org>',
license => 'http://opensource.org/licenses/BSD-3-Clause',
attributes => {
datadir => [ qw(datadir) ],
conf_file => [ qw(file) ],
log_file => [ qw(file) ],
version => [ qw(2.4.0|5.0.0|5.5.2) ],
no_output => [ qw(0|1) ],
binary => [ qw(binary_path) ],
},
attributes_default => {
version => '5.5.2',
no_output => 0,
log_file => 'logstash.log',
},
commands => {
install => [ ],
get_binary => [ ],
check_config => [ qw(conf_file) ],
start => [ qw(conf_file|OPTIONAL) ],
start_in_foreground => [ qw(conf_file|OPTIONAL) ],
stop => [ ],
generate_conf => [ qw(conf_file|OPTIONAL) ],
status => [ ],
},
require_modules => {
'Metabrik::File::Text' => [ ],
},
require_binaries => {
tar => [ ],
},
need_packages => {
ubuntu => [ qw(tar openjdk-8-jre-headless) ],
debian => [ qw(tar openjdk-8-jre-headless) ],
kali => [ qw(tar openjdk-8-jre-headless) ],
freebsd => [ qw(openjdk logstash) ],
},
};
}
sub brik_use_properties {
my $self = shift;
my $datadir = $self->datadir;
my $version = $self->version;
my $conf_file = $datadir."/logstash-$version.conf";
my $log_file = $datadir."/logstash-$version.log";
return {
attributes_default => {
conf_file => $conf_file,
log_file => $log_file,
},
};
}
sub get_binary {
my $self = shift;
my $binary = $self->binary;
if (! defined($binary)) {
my $datadir = $self->datadir;
my $version = $self->version;
$binary = $datadir.'/logstash-'.$version.'/bin/logstash';
}
$self->brik_help_run_file_not_found('get_binary', $binary) or return;
$self->log->verbose("get_binary: found binary [$binary]");
return $binary;
}
sub generate_conf {
my $self = shift;
my ($conf_file) = @_;
$conf_file ||= $self->conf_file;
my $conf =<<EOF
input {
file {
type => "apache"
path => "/var/log/www/example.com-access.log*"
add_field => { "site" => "www.example.com" }
start_position => "beginning" # Start from beginning of every files
sincedb_path => "/dev/null" # Read files entirely every times
ignore_older => "0" # Process every file, even older than 24 hours
}
}
filter {
if [message] =~ /: logfile turned over\$/ {
drop {}
}
if [type] == "apache" {
# Defining patterns:
# https://www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html
grok {
match => { "message" => "%{COMBINEDAPACHELOG}" }
overwrite => [ "message" ]
}
geoip {
source => "clientip"
target => "geoip"
add_tag => [ "apache-geoip" ]
}
date {
match => [ "timestamp" , "dd/MMM/yyyy:HH:mm:ss Z" ]
remove_field => [ "timestamp" ]
}
}
}
output {
if "_grokparsefailure" in [tags] {
null {}
}
if [type] == "apache" {
redis {
host => "127.0.0.1"
data_type => "list"
key => "logstash"
codec => json
congestion_interval => 1
congestion_threshold => 20000000
# Batch processing requires redis >= 2.4.0
batch => true
batch_events => 50
batch_timeout => 5
}
}
else {
stdout {
codec => json
}
}
}
EOF
;
my $ft = Metabrik::File::Text->new_from_brik_init($self) or return;
$ft->append(0);
$ft->overwrite(1);
$ft->write($conf, $conf_file) or return;
return $conf_file;
}
sub install {
my $self = shift;
my $datadir = $self->datadir;
my $version = $self->version;
my $url = 'https://artifacts.elastic.co/downloads/logstash/logstash-5.5.2.tar.gz';
if ($version eq '2.4.0') {
$url = 'https://download.elastic.co/logstash/logstash/logstash-2.4.0.tar.gz';
}
elsif ($version eq '5.0.0') {
$url = 'https://artifacts.elastic.co/downloads/logstash/logstash-5.0.0.tar.gz';
}
my $cw = Metabrik::Client::Www->new_from_brik_init($self) or return;
$cw->mirror($url, "$datadir/logstash.tar.gz") or return;
my $cwd = defined($self->shell) && $self->shell->pwd || '/tmp';
if (defined($self->shell)) {
$self->shell->run_cd($datadir) or return;
}
else {
chdir($datadir) or return $self->log->error("install: chdir: $!");
}
my $cmd = "tar zxvf logstash.tar.gz";
my $r = $self->execute($cmd) or return;
if (defined($self->shell)) {
$self->shell->run_cd($cwd) or return;
}
else {
chdir($cwd) or return $self->log->error("install: chdir: $!");
}
return 1;
}
sub check_config {
my $self = shift;
my ($conf_file) = @_;
$self->brik_help_run_undef_arg('start', $conf_file) or return;
$self->brik_help_run_file_not_found('start', $conf_file) or return;
my $datadir = $self->datadir;
my $version = $self->version;
my $log_file = $self->log_file;
my $binary = $self->get_binary or return;
my $cmd = "$binary -t -f $conf_file -l $log_file";
$self->log->info("check_config: started...");
return $self->system($cmd);
}
#
# logstash -f <config_file> -l <log_file> --debug
#
sub start {
my $self = shift;
my ($conf_file) = @_;
if ($self->status) {
return $self->info_process_is_running;
}
$conf_file ||= $self->conf_file;
$self->brik_help_run_undef_arg('start', $conf_file) or return;
$self->brik_help_run_file_not_found('start', $conf_file) or return;
# Make if a full path file
if ($conf_file !~ m{^/}) {
my $cwd = define($self->shell) && $self->shell->full_pwd || '/tmp';
$conf_file = $cwd.'/'.$conf_file;
}
my $log_file = $self->log_file;
my $no_output = $self->no_output;
my $binary = $self->get_binary or return;
$self->close_output_on_start($no_output);
$self->SUPER::start(sub {
$self->log->verbose("Within daemon");
my $cmd = "$binary -f $conf_file -l $log_file";
if ($self->log->level > 2) {
$cmd .= ' --debug';
}
$self->system($cmd);
$self->log->error("start: son failed to start");
exit(1);
});
return 1;
}
sub start_in_foreground {
my $self = shift;
my ($conf_file) = @_;
$conf_file ||= $self->conf_file;
$self->brik_help_run_undef_arg('start_in_foreground', $conf_file) or return;
$self->brik_help_run_file_not_found('start_in_foreground', $conf_file) or return;
if ($self->status) {
return $self->error_process_is_running;
}
my $log_file = $self->log_file;
my $binary = $self->get_binary or return;
my $cmd = "$binary -f $conf_file -l $log_file";
if ($self->log->level > 2) {
$cmd .= ' --debug';
}
return $self->system($cmd);
}
sub stop {
my $self = shift;
if (! $self->status) {
return $self->info_process_is_not_running;
}
my $conf_file = $self->conf_file;
my $string = "-f $conf_file";
my $pid = $self->get_pid_from_string($string) or return;
return $self->kill($pid);
}
sub status {
my $self = shift;
my $conf_file = $self->conf_file;
my $string = "-f $conf_file";
if ($self->is_running_from_string($string)) {
$self->verbose_process_is_running;
return 1;
}
$self->verbose_process_is_not_running;
return 0;
}
1;
__END__
=head1 NAME
Metabrik::Server::Logstash - server::logstash Brik
=head1 DESCRIPTION
Template to write a new Metabrik Brik.
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2014-2022, Patrice E<lt>GomoRE<gt> Auffret
You may distribute this module under the terms of The BSD 3-Clause License.
See LICENSE file in the source distribution archive.
=head1 AUTHOR
Patrice E<lt>GomoRE<gt> Auffret
=cut