pantheon-systems/journal-2-logstash

Name: journal-2-logstash

Owner: Pantheon

Description: ship the journal to logstash servers

Created: 2016-02-03 03:38:43.0

Updated: 2018-04-08 23:53:44.0

Pushed: 2018-04-09 01:44:13.0

Homepage: null

Size: 103

Language: Go

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

journal-2-logstash

Circle CI Coverage Status

Securely ship JSON formatted logs from systemd's journald to logstash (ELK).

Features

Usage

Config
systemd-journal-gatewayd

The default configuration for s-j-gatewayd is to listen on a TCP socket. Override to listen on a unix sock in a protected path that journal-2-logstash will have access to:

t]
ription=Journal Gateway Service Socket

ket]
enStream=/run/systemd-journal-gatewayd.sock

tall]
edBy=sockets.target
journal-2-logstash
t]
ription=Journal 2 Logstash shipper

vice]
nd this service's output to the console instead of the journal to avoid a logging loop.
dardOutput=tty
dardError=tty

ronment=JOURNAL2LOGSTASH_URL=logstash:4000
ronment=JOURNAL2LOGSTASH_DEBUG=true
ronment=JOURNAL2LOGSTASH_SOCKET=/run/systemd-journal-gatewayd.sock
ronment=JOURNAL2LOGSTASH_STATE_FILE=/etc/journal2logstash.state

S keys and certs are not included in the container and should be mounted
to /etc/certs at runtime.
ronment=JOURNAL2LOGSTASH_TLS_KEY=/etc/certs/logger.key
ronment=JOURNAL2LOGSTASH_TLS_CERT=/etc/certs/logger.crt
ronment=JOURNAL2LOGSTASH_TLS_CA=/etc/certs/ca.crt

Start=/opt/journal-2-logstash/journal-2-logstash
art=on-failure
artSec=2s

tall]
edBy=multi-user.target
Logstash Receiver Config

Use the following configuration for the logstash receiver. This configuration requires all clients to present a certificate trusted by a CA in the cacert bundle.

Filtering rules convert journald's JSON format into Logstash JSON format.

t {
tcp {
        port => "4000"
        codec => "json"
        ssl_enable => "true"
        ssl_key => "/path/to/server.pem"
        ssl_cert => "/path/to/server.pem"
        ssl_cacert => "/path/to/ca_bundle.pem"
        ssl_verify => "true"
        tags => ["systemd_journal_json"]
     }



er {

convert timestamp and message fields from journald format into logstash format

 ("systemd_journal_json" in [tags]) {
### -- convert microsecond to millisecond timestamp for logstash
### -- extract into logstash `@timestamp` field.
### -- remove `__REALTIME_TIMESTAMP` field.
ruby   { code   => "event['__REALTIME_TIMESTAMP'] = event['__REALTIME_TIMESTAMP'].to_i / 1000" }
date   { match  => ["__REALTIME_TIMESTAMP", "UNIX_MS"] }
mutate { remove => ["__REALTIME_TIMESTAMP"] }

### -- convert journald `MESSAGE` to logstash's `message` field.
mutate { rename => { "MESSAGE" => "message" } }

### -- remove systemd fields you don't care about here
mutate { remove => ["__CURSOR", "_BOOT_ID"] }



put {
tdout { codec => "rubydebug" }

Developing

Building:

Testing:

Docker demoing / testing

Requirements:

Included in the repo is a docker-compose.yml in the test/ directory that will spinup two containers:

  1. logger a fedora container running systemd as pid 1 and systemd-journald, systemd-journal-gatewayd, and the journal-2-logstash binary running.
  2. logstash a logstash server instance configured to listen for JSON over TLS from the logger container.

Run make docker_up to start the containers. After the containers start (the logstash container takes the longest) you should immediately see messages flow into the logstash container and will be printed to stdout with the rubydebug logstash output plugin.

ke docker_up

er_1   |
er_1   | Welcome to Fedora 22 (Twenty Two)!
er_1   |
er_1   | Set hostname to <5ef5a7ea14ee>.

er_1   | 2016/02/12 18:11:31 Could not load cursor (open /etc/journal2logstash.state: no such file or directory). Will start reading from 'last boot time'.
er_1   | 2016/02/12 18:11:31 Error connecting to logstash: dial tcp 172.17.0.2:4000: getsockopt: connection refused
er_1   | 2016/02/12 18:11:33 Could not load cursor (open /etc/journal2logstash.state: no such file or directory). Will start reading from 'last boot time'.
er_1   | 2016/02/12 18:11:33 Error connecting to logstash: dial tcp 172.17.0.2:4000: getsockopt: connection refused



tash_1 | {
tash_1 |          "__MONOTONIC_TIMESTAMP" => "59420873093",
tash_1 |                       "PRIORITY" => "6",
tash_1 |                           "_UID" => "0",
tash_1 |                           "_GID" => "0",
tash_1 |                    "_MACHINE_ID" => "9d52a846ee1a4be2b2d6e563162c6aa3",
tash_1 |                      "_HOSTNAME" => "a586fdbd6d5b",
tash_1 |                "SYSLOG_FACILITY" => "3",
tash_1 |                     "_TRANSPORT" => "journal",
tash_1 |                      "CODE_FILE" => "../src/core/unit.c",
tash_1 |                      "CODE_LINE" => "1412",
tash_1 |                  "CODE_FUNCTION" => "unit_status_log_starting_stopping_reloading",
tash_1 |              "SYSLOG_IDENTIFIER" => "systemd",
tash_1 |                     "MESSAGE_ID" => "7d4958e842da4a758f6c1cdc7b36dcc5",
tash_1 |                           "_PID" => "1",
tash_1 |                          "_COMM" => "systemd",
tash_1 |                           "_EXE" => "/usr/lib/systemd/systemd",
tash_1 |                       "_CMDLINE" => "/usr/sbin/init",
tash_1 |                 "_CAP_EFFECTIVE" => "3fffffffff",
tash_1 |                "_SYSTEMD_CGROUP" => "/",
tash_1 |                           "UNIT" => "systemd-journal-gatewayd.service",
tash_1 |     "_SOURCE_REALTIME_TIMESTAMP" => "1455301048170788",
tash_1 |                       "@version" => "1",
tash_1 |                     "@timestamp" => "2016-02-12T18:17:28.171Z",
tash_1 |                           "host" => "172.17.0.3",
tash_1 |                           "tags" => [
tash_1 |         [0] "journal_json"
tash_1 |     ],
tash_1 |                        "message" => "Starting Journal Gateway Service..."
tash_1 | }

From another terminal you can generate log messages inside the logger container:

ho "hello there" | make docker_log

Observe the log message flow from journald -> journal-2-logstash -> logstash:

er_1   | 2016/02/12 18:19:15 [DEBUG] Received from journal: { "__CURSOR" : "s=aae5e906525c4c00be5e9d5026bbf40a;i=35c;b=7f0ea6b7d19f47f9a9bc928f32512ae7;m=ddc296aab;t=52b96b46b549a;x=e92c6b237a40211b", "__REALTIME_TIMESTAMP" : "1455301155574938", "__MONOTONIC_TIMESTAMP" : "59528276651", "_BOOT_ID" : "7f0ea6b7d19f47f9a9bc928f32512ae7", "PRIORITY" : "6", "_UID" : "0", "_GID" : "0", "_MACHINE_ID" : "9d52a846ee1a4be2b2d6e563162c6aa3", "_HOSTNAME" : "63c73cd1ec2e", "_TRANSPORT" : "stdout", "MESSAGE" : "hello there", "_PID" : "97", "_COMM" : "cat" } }

tash_1 | {
tash_1 |     "__MONOTONIC_TIMESTAMP" => "59528276651",
tash_1 |                  "PRIORITY" => "6",
tash_1 |                      "_UID" => "0",
tash_1 |                      "_GID" => "0",
tash_1 |               "_MACHINE_ID" => "9d52a846ee1a4be2b2d6e563162c6aa3",
tash_1 |                 "_HOSTNAME" => "63c73cd1ec2e",
tash_1 |                "_TRANSPORT" => "stdout",
tash_1 |                      "_PID" => "97",
tash_1 |                     "_COMM" => "cat",
tash_1 |                  "@version" => "1",
tash_1 |                "@timestamp" => "2016-02-12T18:19:15.574Z",
tash_1 |                      "host" => "172.17.0.3",
tash_1 |                      "tags" => [
tash_1 |         [0] "journal_json"
tash_1 |     ],
tash_1 |                   "message" => "hello there"
tash_1 | }
TODO
Misc Notes

curl -H'Accept: application/event-stream' -H 'Range: entries=:-1:1' 'localhost:19531/entries'


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.