• Stars
    star
    174
  • Rank 219,104 (Top 5 %)
  • Language
    Erlang
  • License
    Apache License 2.0
  • Created over 5 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

systemd utilities for Erlang applications

systemd

Hex.pm HexDocs Hex.pm License GitHub Workflow Status Codecov

Simple library for notifying systemd about process state.

Features

  • NOTIFY_SOCKET communication with supervising process.
  • Watchdog process will be started automatically (if not disabled). It will also handle sending keep-alive messages automatically.
  • Fetching file descritors passed by the supervisor.
  • journal logger handler and formatters.

Installation

Just add this to your rebar.config:

{deps, [systemd]}.

Or in case of Mix project, to your mix.exs:

defp deps do
  [
    {:systemd, "~> 0.6"}
  ]
end

Then call systemd:notify(ready) when your application is ready to work/accept connections or add systemd:ready() as a child of your application's main supervisor.

Non-systemd systems

This application and all functions within are safe to call even in non-systemd and non-Linux OSes. In case if there is no systemd configuration options then all functions will simply work as (almost) no-ops.

Usage

Assuming you have my_app.service unit like that

[Unit]
Description=My Awesome App

[Service]
User=appuser
Group=appgroup
# This will allow using `systemd:notify/1` for informing the system supervisor
# about application status.
Type=notify
# Application need to start in foreground instead of forking into background,
# otherwise it may be not correctly detected and system will try to start it
# again.
ExecStart=/path/to/my_app start
# Enable watchdog process, which will expect messages in given timeframe,
# otherwise it will restart the process as a defunct. It should be managed
# automatically by `systemd` application in most cases and will send messages
# twice as often as requested.
#
# You can force failure by using `systemd:watchdog(trigger)` or manually ping
# systemd watchdog via `systemd:watchdog(ping)`.
WatchdogSec=10s
Restart=on-failure

[Install]
WantedBy=multi-user.target

You can inform systemd about state of your application. To do so just call:

% Erlang
systemd:notify(ready).
# Elixir
:systemd.notify(:ready)

This will make systemctl start my_app.service to wait until application is up and running.

If you want to restart your application you can notify systemd about it with:

% Erlang
systemd:notify(reloading).
# Elixir
:systemd.notify(:reloading)

Message about application shutting down will be handled automatically for you.

For simplification of readiness notification there is systemd:ready() function that returns child specs for temporary process that can be used as a part of your supervision tree to mark the point when application is ready, ex.:

% Erlang
-module(my_app_sup).

-behaviour(supervisor).

-export([start_link/1,
         init/1]).

start_link(Opts) ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, Opts).

init(_Opts) ->
    SupFlags = #{
      strategy => one_for_one
    },
    Children = [
      my_app_db:child_spec(),
      my_app_webserver:child_spec(),
      systemd:ready(),
      my_app_periodic_job:child_spec()
    ],

    {ok, {SupFlags, Children}}.
# Elixir
defmodule MyProject.Application do
  use Application

  def start(_type, _opts) do
    children = [
      MyProject.Repo,
      MyProjectWeb.Endpoint,
      :systemd.ready() # <- IMPORTANT - this is a function call (it returns the proper child spec)
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

Logs

To handle logs you have 2 possible options:

  • Output data to standard output or error with special prefixes. This approach is much simpler and straightforward, however do not support structured logging and multiline messages.
  • Use datagram socket with special communication protocol. This requires a little bit more effort to set up, but seamlessly supports structured logging and multiline messages.

This library supports both formats, and it is up to You which one (or both?) your app will decide to use.

Erlang

Standard error

There is systemd_kmsg_formatter which formats data using kmsg-like level prefixes can be used with any logger that outputs to standard output or standard error if this is attached to the journal. By default systemd library will update all handlers that use logger_std_h with type standard_io or standard_error that are attached to the journal (it is automatically detected via JOURNAL_STREAM environment variable). You can disable that behaviour by setting:

% Erlang
[
  {systemd, [{auto_formatter, false}]}
].

For custom loggers you can use this formatter by adding new option parent to the formatter options that will be used as "upstream" formatter, ex.:

logger:add_handler(example_handler, logger_disk_log_h, #{
  formatter => {systemd_kmsg_formatter, #{parent => logger_formatter,
                                          template => [msg]},
  config => #{
    file => "/var/log/my_app.log"
  }
}).
Datagram socket

This one requires systemd application to be started to spawn some processes required for handling sockets, so the best way to handle it is to add predefined systemd handlers after your application starts:

logger:add_handlers(systemd),
logger:remove_handler(default).

Be aware that this one is not guaranteed to work on non-systemd systems, so if You aren't sure if that application will be ran on systemd-enabled OS then you shouldn't use it as an only logger solution in your application or you can end with no logger attached at all.

This handler should not be used with systemd_kmsg_formatter as this will result with pointless kmsg-like prefixes in the log messages.

You can also "manually" configgure handler if you want to configure formatter:

logger:add_handler(my_handler, systemd_journal_h, #{
  formatter => {my_formatter, FormatterOpts}
}),
logger:remove_handler(default).

Elixir

This assumes Elixir 1.10+, as earlier versions do not use Erlang's logger module for dispatching logs.

Standard error

systemd has Erlang's logger backend, which mean that you have 2 ways of achieving what is needed:

  1. Disable Elixir's backends and just rely on default Erlang's handler:
# config/config.exs
config :logger,
  backends: [],
  handle_otp_reports: false,
  handle_sasl_reports: false

And then allow systemd to make its magic that is used in "regular" Erlang code.

  1. "Manually" add handler that will use systemd_kmsg_formatter:
# In application start/2 callback
:ok = :logger.add_handler(
  :my_handler,
  :logger_std_h,
  %{formatter: {:systemd_kmsg_formatter, %{}}}
)
Logger.remove_backend(:console)

However remember, that currently (as Elixir 1.11) there is no "Elixir formatter" for Erlang's logger implementation, so you can end with Erlang-style formatting of the metadata in the logs.

Datagram socket

You can use Erlang-like approach, which is:

# In application start/2 callback
:logger.add_handlers(:systemd)
Logger.remove_backend(:console)

Or you can manually configure the handler:

# In application start/2 callback
:logger.add_handler(
  :my_handler,
  :systemd_journal_h,
  %{formatter: {MyFormatter, formatter_opts}}
)
Logger.remove_backend(:console)

Be aware that this one is not guaranteed to work on non-systemd systems, so if You aren't sure if that application will be ran on systemd-enabled OS then you shouldn't use it as an only logger solution in your application or you can end with no logger attached at all.

This handler should not be used with :systemd_kmsg_formatter as this will result with pointless kmsg-like prefixes in the log messages.

License

See LICENSE.

More Repositories

1

mix_unused

Find unused functions in your project
Elixir
208
star
2

vanilla-ujs

UJS without jQuery dependency
JavaScript
194
star
3

dolores

Local development reverse proxy
Rust
57
star
4

phoenix_bakery

Better compression for your Phoenix assets
Elixir
54
star
5

ecto_function

Simple macro for generating SQL's functions macro wrappers
Elixir
49
star
6

nix-elixir

Nix
47
star
7

sad.vim

Quick search and replace for Vim
Vim Script
46
star
8

dotfiles

List of my awesome dotfiles :)
Vim Script
45
star
9

agnoster

Agnoster for Fish 🐠
Shell
43
star
10

defconst

Helper macros for defining constant values in modules
Elixir
41
star
11

ernie

Efficient binary encoding for your data (based on Erlang's External Terms Format)
Rust
36
star
12

gen_icmp

ICMP protocol implementation for Erlang without NIFs
Erlang
34
star
13

xdg-rlz

Configure non-conforming applications to use XDG Base Directory specification
Shell
33
star
14

ssh_signature

Erlang
32
star
15

asyncdo.vim

Dumb simple async task runner for Vim
Vim Script
28
star
16

pg-utils

Collection of small utilities for maintaining PSQL databases
Shell
25
star
17

magritte

Ceci n'est une pipe
Elixir
24
star
18

lumberjack

Web based log viewer for Erlang and Elixir applications
Elixir
23
star
19

watermelon

BDD testing library for Elixir
Elixir
19
star
20

vim-backscratch

Small scratches for Vim, feels nice
Vim Script
19
star
21

commoner

Elixir
19
star
22

plug_telemetry_server_timing

Plug for providing Telemetry metrics within browser DevTools
Elixir
18
star
23

sass-960gs

It's a standalone 960.gs SCSS framework that provide fixed and float grid.
Ruby
16
star
24

plug_systemd_example

Example Elixir application that integrates with systemd features
Elixir
16
star
25

mix_machine

Machine output for Mix tasks
Elixir
16
star
26

ucl-rs

Rust bindings to libucl
Rust
15
star
27

logger_colorful

Simple Erlang's logger's formatters wrapper that adds colours to the messages
Erlang
14
star
28

redmine_contacts

This plugin a free clone of Highrise CRM which you can manage customers and contacts, track tasks and store files. Based on Kirill Bezrukov's Contacts plugin.
Ruby
13
star
29

ecto_olap

Data analytics helpers for Ecto and PostgreSQL
Elixir
12
star
30

consulate

Erlang port mapper module that uses Consul instead of EPMD
Erlang
12
star
31

plug_early_hints

Elixir
12
star
32

stream_state

Stateful generative testing for Elixir implemented on top of StreamData
Elixir
11
star
33

biontech

Makefile
9
star
34

enough

Overload protected servers calls
Erlang
7
star
35

fmt

Elixir
7
star
36

ex_unit_embedded

Define ExUnit tests alongside your private functions to test them.
Elixir
7
star
37

advent-of-code

6
star
38

logger_datadog

Elixir Logger backend for sending logs directly to DataDog
Elixir
5
star
39

low_voltage

HTML
5
star
40

plug_clacks

Remember the fallen
Elixir
5
star
41

soma

Rust
5
star
42

range_set

range_set
Elixir
4
star
43

usnip.vim

Minimal snippets manager for (Neo)Vim
Vim Script
4
star
44

credo_code_climate

Plugin for Credo to produce CodeClimate-compatible JSON output
Elixir
4
star
45

phoenix_brotli_compressor

Elixir
4
star
46

pivotaltracker.vim

Completion source for PivotalTracker stories in Vim 8 and NeoVim
Vim Script
4
star
47

asyncomplete-pivotaltracker.vim

Vim Script
3
star
48

vim-encpipe

Vim Script
3
star
49

mix_elvis

Run Elvis linter via Mix
Elixir
3
star
50

nomadex

Nomad's API client library in Elixir
Elixir
3
star
51

bare-erlang

Implementation of BARE encoding in Erlang (mirror)
Erlang
3
star
52

logpick

Erlang
2
star
53

labeouf

PoC do-like notation for Rust
Rust
2
star
54

minisign

Erlang
2
star
55

e9p

Erlang
2
star
56

advent-of-code-2017

My Advent of Code 2017 solutions
Awk
1
star
57

make-example

Makefile
1
star
58

argonaut

Elixir
1
star
59

niemier.pl

1
star
60

puppet-gitdeploy

Puppet
1
star
61

elixir-bench

Elixir
1
star
62

ironic

Elixir
1
star
63

git-riff

Super simple git hooks launcher
Shell
1
star
64

poc-fs-bug

Elixir
1
star
65

example_error

Elixir
1
star
66

hauleth

HTML
1
star
67

logger_telemetry

Erlang
1
star