diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6623be753d783..f2f29ee37757f 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -340,6 +340,8 @@
./services/logging/rsyslogd.nix
./services/logging/syslog-ng.nix
./services/logging/syslogd.nix
+ ./services/logging/beats/apm-server.nix
+ ./services/logging/beats/metricbeat.nix
./services/mail/clamsmtp.nix
./services/mail/davmail.nix
./services/mail/dkimproxy-out.nix
diff --git a/nixos/modules/services/logging/beats/apm-server.nix b/nixos/modules/services/logging/beats/apm-server.nix
new file mode 100644
index 0000000000000..4a3622590a2fc
--- /dev/null
+++ b/nixos/modules/services/logging/beats/apm-server.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ beatslib = import ./lib.nix { inherit lib pkgs; };
+
+ mkApmServerConfig = cfg: {
+
+ apm-server.host = "${cfg.listenAddress}:${toString cfg.port}";
+
+ };
+
+
+in
+
+{
+ ###### interface
+
+ options = {
+
+ services.beats.apm-server = recursiveUpdate
+ (beatslib.mkCommonOptions { name = "apm-server"; defaults = { package = pkgs.elastic-apm-server; }; })
+ {
+
+ listenAddress = mkOption {
+ description = "Address on which to listen.";
+ type = types.str;
+ default = "localhost";
+ };
+
+ port = mkOption {
+ description = "Port on which to listen.";
+ type = types.int;
+ default = 8200;
+ };
+
+ elasticsearch.indices = mkOption {
+ description = "Array of index selector rules";
+ type = with types; listOf attrs;
+ default = [
+ {
+ index = "apm-%{[beat.version]}-sourcemap";
+ when.contains.processor.event = "sourcemap";
+ }
+ {
+ index = "apm-%{[beat.version]}-error-%{+yyyy.MM.dd}";
+ when.contains.processor.event = "error";
+ }
+ {
+ index = "apm-%{[beat.version]}-transaction-%{+yyyy.MM.dd}";
+ when.contains.processor.event = "transaction";
+ }
+ {
+ index = "apm-%{[beat.version]}-span-%{+yyyy.MM.dd}";
+ when.contains.processor.event = "span";
+ }
+ {
+ index = "apm-%{[beat.version]}-metric-%{+yyyy.MM.dd}";
+ when.contains.processor.event = "metric";
+ }
+ {
+ index = "apm-%{[beat.version]}-onboarding-%{+yyyy.MM.dd}";
+ when.contains.processor.event = "onboarding";
+ }
+ ];
+ };
+
+ };
+
+ };
+
+ config = beatslib.mkNixosConfig {
+ mkBeatConfig = mkApmServerConfig;
+ cfg = config.services.beats.apm-server;
+ };
+
+}
diff --git a/nixos/modules/services/logging/beats/lib.nix b/nixos/modules/services/logging/beats/lib.nix
new file mode 100644
index 0000000000000..bd37ac1c1da45
--- /dev/null
+++ b/nixos/modules/services/logging/beats/lib.nix
@@ -0,0 +1,145 @@
+{ lib, pkgs, ... }:
+
+with lib;
+
+{
+
+ mkCommonOptions = { name, defaults }: {
+
+ enable = mkEnableOption "Enable ${name}.";
+
+ package = mkOption {
+ description = "${name} package to use.";
+ type = types.package;
+ default = defaults.package;
+ };
+
+ executable = mkOption {
+ description = "Name of the executable within to run.";
+ type = types.str;
+ default = defaults.executable or name;
+ };
+
+ name = mkOption {
+ description = "Name of the beat";
+ type = types.str;
+ default = defaults.name or name;
+ };
+
+ tags = mkOption {
+ description = "Tags to place on the shipped log messages";
+ type = types.listOf types.str;
+ default = [];
+ };
+
+ stateDir = mkOption {
+ description = "The state directory.";
+ type = types.str;
+ default = defaults.stateDir or "/var/lib/${name}";
+ };
+
+ loglevel = mkOption {
+ description = "Logging verbosity level.";
+ type = types.enum [ "debug" "info" "warning" "error" "fatal" ];
+ default = "warning";
+ };
+
+ extraConfig = mkOption {
+ description = "Additional config to be added to the beats config.json.";
+ type = types.attrs;
+ default = {};
+ };
+
+ elasticsearch = {
+ hosts = mkOption {
+ description = "Elasticsearch hosts";
+ type = with types; listOf str;
+ default = [ "localhost:9200" ];
+ };
+
+ username = mkOption {
+ description = "Username for elasticsearch basic auth.";
+ type = types.nullOr types.str;
+ default = null;
+ };
+
+ password = mkOption {
+ description = "Password for elasticsearch basic auth.";
+ type = types.nullOr types.str;
+ default = null;
+ };
+ };
+
+ kibana = {
+ host = mkOption {
+ description = "Host where kibana is reachable.";
+ type = types.str;
+ default = "localhost:5601";
+ };
+ };
+
+ };
+
+ mkNixosConfig = { cfg, mkBeatConfig }: let
+
+ mkCommonBeatConfig = cfg: {
+ inherit (cfg) tags;
+
+ path = {
+ data = "${cfg.stateDir}/data";
+ logs = "${cfg.stateDir}/logs";
+ };
+
+ logging.level = cfg.loglevel;
+
+ output = { inherit (cfg) elasticsearch; };
+
+ setup.template = {
+ fields = "${cfg.package}/share/fields.yml";
+ settings.index = {
+ number_of_shards = 1;
+ codec = "best_compression";
+ };
+ };
+
+ setup.kibana = {
+ inherit (cfg.kibana) host;
+ };
+
+ setup.dashboards = {
+ enabled = true;
+ directory ="${cfg.package}/share/kibana";
+ };
+ };
+
+ beatConfig = foldl' recursiveUpdate {} [
+ (mkCommonBeatConfig cfg)
+ (mkBeatConfig cfg)
+ cfg.extraConfig
+ ];
+ beatConfigJSON = pkgs.writeText "${cfg.name}.json" (builtins.toJSON beatConfig);
+
+ in mkIf cfg.enable {
+
+ systemd.services.${cfg.name} = {
+ wantedBy = [ "multi-user.target" ];
+
+ after = let
+ hasLocalKibana = lib.strings.hasInfix "localhost" cfg.kibana.host;
+ hasLocalElasticsearch = (findSingle (x: lib.strings.hasInfix "localhost" x) null "multiple" cfg.elasticsearch.hosts) != null;
+ in []
+ ++ optional hasLocalKibana "kibana.service"
+ ++ optional hasLocalElasticsearch "elasticsearch.service";
+
+ preStart = ''
+ mkdir -p ${beatConfig.path.data}
+ mkdir -p ${beatConfig.path.logs}
+ '';
+
+ serviceConfig = {
+ ExecStart = "${cfg.package}/bin/${cfg.executable} -c ${beatConfigJSON}";
+ };
+ };
+ };
+
+}
diff --git a/nixos/modules/services/logging/beats/metricbeat.nix b/nixos/modules/services/logging/beats/metricbeat.nix
new file mode 100644
index 0000000000000..c40ef0cde99e6
--- /dev/null
+++ b/nixos/modules/services/logging/beats/metricbeat.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ beatslib = import ./lib.nix { inherit lib pkgs; };
+
+ mkMetricbeatConfig = cfg: {
+
+ metricbeat.modules = cfg.modules;
+
+ };
+
+in
+{
+ options = {
+
+ services.beats.metricbeat = beatslib.mkCommonOptions {
+ name = "metricbeat";
+ defaults = { package = pkgs.metricbeat; };
+ } // {
+
+ modules = mkOption {
+ description = ''
+ Modules Configuration.
+
+ See
+ '';
+ type = with types; listOf attrs;
+ default = [];
+ };
+
+ };
+ };
+
+ config = beatslib.mkNixosConfig {
+ mkBeatConfig = mkMetricbeatConfig;
+ cfg = config.services.beats.metricbeat;
+ };
+
+}
diff --git a/nixos/tests/elk.nix b/nixos/tests/elk.nix
index e7ae023f3ff26..68f064887e713 100644
--- a/nixos/tests/elk.nix
+++ b/nixos/tests/elk.nix
@@ -96,6 +96,16 @@ let
unit_count: 1
'';
};
+
+ beats.apm-server = {
+ enable = true;
+ extraConfig = {
+ setup.dashboards = {
+ always_kibana = true;
+ retry.enabled = true;
+ };
+ };
+ };
};
};
};
@@ -125,6 +135,10 @@ let
$one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"flowers\"}}}' | jq .hits.total | grep -v 0");
$one->waitUntilSucceeds("curl --silent --show-error '${esUrl}/_search' -H 'Content-Type: application/json' -d '{\"query\" : { \"match\" : { \"message\" : \"dragons\"}}}' | jq .hits.total | grep 0");
+ # See if apm-server is healthy.
+ $one->waitForUnit("apm-server.service");
+ $one->waitUntilSucceeds("curl --silent --show-error 'http://localhost:8200/' | grep 'ok'");
+
# Test elasticsearch-curator.
$one->systemctl("stop logstash");
$one->systemctl("start elasticsearch-curator");
diff --git a/pkgs/servers/misc/elastic-apm-server/default.nix b/pkgs/servers/misc/elastic-apm-server/default.nix
new file mode 100644
index 0000000000000..c6c2a18698d57
--- /dev/null
+++ b/pkgs/servers/misc/elastic-apm-server/default.nix
@@ -0,0 +1,62 @@
+{ elk6Version
+, enableUnfree ? true
+, stdenv
+, fetchurl
+, makeWrapper
+, autoPatchelfHook
+}:
+
+with stdenv.lib;
+let
+
+ inherit (builtins) elemAt;
+ info = splitString "-" stdenv.hostPlatform.system;
+ arch = elemAt info 0;
+ plat = elemAt info 1;
+ shas =
+ if enableUnfree
+ then {
+ "x86-linux" = "0xa7jfsmg6mzw881k7j8rkph586dpcl7afycdds70n43ixrqsp07";
+ "x86_64-linux" = "0cw0xqx4zyanhbh6irpkn9anw00navwqnfhzpwxpmkmn7w4lj64b";
+ "x86_64-darwin" = "0b15m4j11qirbkq30i7m5xd51jz39ym8qiah18276vc6i7h13zbf";
+ }
+ else {
+ "x86-linux" = "0iq34g4ypbdbwbaiyaicj0zj9c0xdryd6b3y0dxcdbhywjn32a5v";
+ "x86_64-linux" = "09nb2n4mn1bp4iprgrks3d2chxsd7a5wzys969vfxxc54y9bhy8x";
+ "x86_64-darwin" = "1nr75mygiza6l5v3daqsf2cpb47hkjpv0mnajlhacpf24cvvxlkb";
+ };
+
+in stdenv.mkDerivation (rec {
+ name = "apm-server-${optionalString (!enableUnfree) "oss-"}${version}";
+ version = elk6Version;
+
+ src = fetchurl {
+ url = "https://artifacts.elastic.co/downloads/apm-server/${name}-${plat}-${arch}.tar.gz";
+ sha256 = shas."${stdenv.hostPlatform.system}" or (throw "Unknown architecture");
+ };
+
+ buildInputs = [ makeWrapper ];
+
+ installPhase = ''
+ mkdir -p $out/{bin,share}
+ cp apm-server $out/bin
+ cp -r apm-server.yml fields.yml kibana $out/share
+ '';
+
+ passthru = { inherit enableUnfree; };
+
+ meta = {
+ description = "Open Source Application Performance Monitoring";
+ license = if enableUnfree then licenses.elastic else licenses.asl20;
+ platforms = platforms.unix;
+ };
+} // optionalAttrs enableUnfree {
+ dontPatchELF = true;
+ nativeBuildInputs = [ autoPatchelfHook ];
+ postFixup = ''
+ for exe in $(find $out/bin -executable -type f); do
+ echo "patching $exe..."
+ patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$exe"
+ done
+ '';
+})
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 90daabcf890f8..71a88ec9269fd 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -2534,6 +2534,11 @@ in
callPackage ../servers/search/elasticsearch/plugins.nix { }
);
+ elastic-apm-server = callPackage ../servers/misc/elastic-apm-server { };
+ elastic-apm-server-oss = callPackage ../servers/misc/elastic-apm-server {
+ enableUnfree = false;
+ };
+
embree2 = callPackage ../development/libraries/embree/2.x.nix { };
emem = callPackage ../applications/misc/emem { };