summary refs log tree commit diff
path: root/nixos/modules/services/logging/logstash.nix
blob: 2f0eea505267f5e1fde967ba8050fc88ac17a1d2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
{ config, pkgs, ... }:

with pkgs.lib;

let

  cfg = config.services.logstash;

  listToConfig = list: "[ " + (concatStringsSep ", " (map exprToConfig list)) + " ]";

  hashToConfig = attrs:
    let
      attrNameToConfigList = name:
        [ (exprToConfig name)  (exprToConfig (getAttr name attrs)) ];
    in
      "[ " +
      (concatStringsSep ", " (map attrNameToConfigList (attrNames attrs))) +
      " ]";

  valueToConfig = nvpair: let name = nvpair.name; value = nvpair.value; in
    if (isAttrs value) && ((!(value ? __type)) || value.__type == "repeated")
      then ''
        ${name} {
          ${exprToConfig value}
        }
      ''
      else "${name} => ${exprToConfig value}";

  repeatedAttrsToConfig = values:
      concatStringsSep "\n" (map valueToConfig values);

  attrsToConfig = attrs:
    let
      attrToConfig = name: valueToConfig {
        inherit name;
        value = (getAttr name attrs);
      };
    in
      concatStringsSep "\n" (map attrToConfig (attrNames attrs));

  exprToConfig = expr:
    let
      isCustomType = expr: (isAttrs expr) && (expr ? __type);

      isFloat = expr: (isCustomType expr) && (expr.__type == "float");

      isHash = expr: (isCustomType expr) && (expr.__type == "hash");

      isRepeatedAttrs = expr: (isCustomType expr) && (expr.__type == "repeated");
    in
      if builtins.isBool expr then (if expr then "true" else "false") else
      if builtins.isString expr then ''"${expr}"'' else
      if builtins.isInt expr then toString expr else
      if isFloat expr then expr.value else
      if isList expr then listToConfig expr else
      if isHash expr then hashToConfig expr.value else
      if isRepeatedAttrs expr then repeatedAttrsToConfig expr.values
      else attrsToConfig expr;

  mergeConfigs = configs:
    let
      op = attrs: newAttrs:
        let
          isRepeated = newAttrs ? __type && newAttrs.__type == "repeated";
        in {
            values = attrs.values ++ (if isRepeated then newAttrs.values else
              map (name: { inherit name; value = getAttr name newAttrs; })
              (attrNames newAttrs));
          };
    in (foldl op { values = []; } configs) // { __type = "repeated"; };

in

{
  ###### interface

  options = {
    services.logstash = {
      enable = mkOption {
        default = false;
        description = ''
          Enable logstash.
        '';
      };

      inputConfig = mkOption {
        default = {};
        description = ''
          An attribute set (or an expression generated by mkNameValuePairs)
          representing a logstash configuration's input section.
          Logstash configs are name-value pairs, where values can be bools,
          strings, numbers, arrays, hashes, or other name-value pairs,
          and names are strings that can be repeated. Name-value pairs with no
          repeats are represented by attr sets. Bools, strings, ints, and
          arrays are mapped directly. Name-value pairs with repeats can be
          generated by the config.lib.logstash.mkNameValuePairs function, which
          takes a list of attrsets and combines them while preserving attribute
          name duplicates if they occur. Similarly, there are the mkFloat and
          mkHash functions, which take a string representation of a float and an
          attrset, respectively.
        '';
        merge = mergeConfigs;
      };

      filterConfig = mkOption {
        default = {};
        description = ''
          An attribute set (or an expression generated by mkNameValuePairs)
          representing a logstash configuration's filter section.
          See inputConfig description for details.
        '';
        merge = mergeConfigs;
      };

      outputConfig = mkOption {
        default = {};
        description = ''
          An attribute set (or an expression generated by mkNameValuePairs)
          representing a logstash configuration's output section.
          See inputConfig description for details.
        '';
        merge = mergeConfigs;
      };
    };
  };


  ###### implementation

  config = mkMerge [ {
    lib.logstash = {
      mkFloat = stringRep: { __type = "float"; value = stringRep; };

      mkHash = attrs: { __type = "hash"; value = attrs; };

      mkNameValuePairs = mergeConfigs;
    };
  } ( mkIf cfg.enable {
    systemd.services.logstash = with pkgs; {
      description = "Logstash daemon";

      wantedBy = [ "multi-user.target" ];

      path = [ jre ];

      script = "cd /tmp && exec java -jar ${logstash} agent -f ${writeText "logstash.conf" ''
        input {
          ${exprToConfig cfg.inputConfig}
        }

        filter {
          ${exprToConfig cfg.filterConfig}
        }

        output {
          ${exprToConfig cfg.outputConfig}
        }
      ''} &> /var/log/logstash.log";
    };
  })];
}