Giter Club home page Giter Club logo

log_proxy's Introduction

log_proxy

Status (master branch)

GitHub CI Maintenance

What is it?

log_proxy is a tiny C utility for log rotation for apps that write their logs to stdout.

This is very useful, specially for 12-factor apps that write their logs to stdout.

It can be used to avoid loosing some logs if you use logrotate with copytruncate feature or to prevent a log file from filling your hard disk.

Features

  • usable as a pipe (myapp myapp_arg1 myapp_arg2 |log_proxy /log/myapp.log)
  • configurable log rotation suffix with strftime placeholders (for example: .%Y%m%d%H%M%S)
  • can limit the number of rotated files (and delete oldest)
  • can rotate files depending on their size (in bytes)
  • can rotate files depending on their age (in seconds)
  • does not need a specific log directory for a given app (you can have one directory with plenty of different log files from different apps)
  • several instances of the same app can log to the same file without issue (example: myapp arg1 |log_proxy --use-locks /log/myapp.log and myapp arg2 |log_proxy --use-locks /log/myapp.log can run at the same time)
  • configurable action (a command to execute) to run after each log rotation
  • rock solid (it's perfectly stable in our use case but we are waiting for other success stories to check this feature)
  • option to add a timestamp before each log line, thanks to mk-fg
  • really fast
  • do not eat a lot of memory
  • configurable with CLI options as well with env variables
  • usable as a wrapper to capture stdout and stderr (log_proxy_wrapper --stdout=/log/myapp.stdout --stderr=/log/myapp.stderr -- myapp myapp_arg1 myapp_arg2)
  • usable as a wrapper to capture stdout and stderr in the same file (log_proxy_wrapper --stdout=/log/myapp.log --stderr=STDOUT -- myapp myapp_arg1 myapp_arg2)
  • very few dependencies (only glib2 is required)
  • very easy to build (event on old distributions like CentOS 6)

How to install?

We provide Linux 64 bits binaries in releases section. There is virtually no requirement (you just need a Linux 64 bits distribution more recent than CentOS 6 (2011!)).

Of course, you can also build the tool by yourself (see at the end of this document).

To install the binary distribution:

# As root user (or with sudo)
bash -c "$(curl -fsSLk https://raw.githubusercontent.com/metwork-framework/log_proxy/master/installer/install.sh)"

Notes:

  • if you are very concerned about the security of your system and if you don't want to execute a remote root script on your system, please review the very small install script (it's just about downloading and installing two statically compiled binaries in /usr/local/bin/)
  • our binary distribution won't work on Alpine Linux because of glibc replacement but @tomalok is maintaining a log_proxy Alpine Linux package.

How to uninstall?

# As root user (or with sudo)
rm -f /usr/local/bin/log_proxy
rm -f /usr/local/bin/log_proxy_wrapper

Why this tool?

Why not using logrotate with copytruncate feature?

If you use myapp myapp_arg1 myapp_arg2 >/log/myapp.log 2>&1for example and if you can't stop easily your app (because it's a critical thing), you can configure logrotate with copytruncate feature to do the log rotation of /log/myapp.log but:

  • you may loose a few lines during log rotation (1)
  • the rotation is mainly time-based, so you can fill your storage if your app suddently start to be very very verbose

(1), see https://unix.stackexchange.com/questions/475524/how-copytruncate-actually-works

Please note also that copyrotate has an inherent race condition, in that it's possible that the writer will append a line to the logfile just after logrotate finished the copy and before it has issued the truncate operation. That race condition would cause it to lose those lines of log forever. That's why rotating logs using copytruncate is usually not recommended, unless it's the only possible way to do it.

Why developing another tool?

After reading: https://superuser.com/questions/291368/log-rotation-of-stdout and http://zpz.github.io/blog/log-rotation-of-stdout/, we reviewed plenty of existing tools (multilog, rotatelogs, piper...).

But none of them was ok with our needed features:

  • configurable log rotation on size AND age
  • no dedicated log directory for an app
  • (and) several instances of the same app can log to the same log file without issue

The piper tool was the more close but does not support the last feature (several instances to the same log file).

Usage

As a filter

your_app your_app_arg1 your_app_arg2 2>&1 |log_proxy --rotation-size=1000000 my_log_file_max_1MB.log

Full help:

$ ./log_proxy --help
Usage:
  log_proxy [OPTION?] LOGNAME  - log proxy

Help Options:
  -h, --help                Show help options

Application Options:
  -s, --rotation-size       maximum size (in bytes) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_SIZE or 104857600 (100MB))
  -t, --rotation-time       maximum lifetime (in seconds) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_TIME or 86400 (24H))
  -S, --rotation-suffix     strftime based suffix to append to rotated log files (default: content of environment variable LOGPROXY_ROTATION_SUFFIX or .%%Y%%m%%d%%H%%M%%S)
  -d, --log-directory       directory to store log files (default: content of environment variable LOGPROXY_LOG_DIRECTORY or current directory), directory is created if missing
  -n, --rotated-files       maximum number of rotated files to keep including main one (0 => no cleaning, default: content of environment variable LOGPROXY_ROTATED_FILES or 5)
  -T, --timestamps          strftime prefix to prepend to every output line (default: content of environment variable LOGPROXY_TIMESTAMPS or none)
  -c, --chmod               if set, chmod the logfile to this value, '0700' for example (default: content of environment variable LOGPROXY_CHMOD or NULL)
  -o, --chown               if set, try (if you don't have sufficient privileges, it will fail silently) to change the owner of the logfile to the given user value
  -g, --chgrp               if set, try (if you don't have sufficient privileges, it will fail silently) to change the group of the logfile to the given group value
  -m, --use-locks           use locks to append to main log file (useful if several process writes to the same file)
  -f, --fifo                if set, read lines on this fifo instead of stdin
  -r, --rm-fifo-at-exit     if set, drop fifo at then end of the program (you have to use --fifo option of course)

Optional environment variables to override defaults:
    LOGPROXY_ROTATION_SIZE
    LOGPROXY_ROTATION_TIME
    LOGPROXY_ROTATION_SUFFIX
    LOGPROXY_LOG_DIRECTORY
    LOGPROXY_ROTATED_FILES
    LOGPROXY_TIMESTAMPS

Example for rotation-size option :
- If log_proxy is run with the option --rotation-size on command line, rotation-size will take the provided value
- If the option --rotation-size is not provided on command line :
  - If the environment variable LOGPROXY_ROTATION_SIZE is set, rotation-size will take this value
  - If the environment variable LOGPROXY_ROTATION_SIZE is not set, rotation-size will take the default value 104857600

As a wrapper

log_proxy_wrapper --rotation-size=1000000 --stdout=my_log_file_max_1MB.log --stderr=STDOUT -- your_app your_app_arg1 your_app_arg2

Full help:

$ ./log_proxy_wrapper --help
Usage:
  log_proxy_wrapper [OPTION?] -- COMMAND [COMMAND_ARG1] [COMMAND_ARG2] [...] - log proxy

Help Options:
  -h, --help                Show help options

Application Options:
  -s, --rotation-size       maximum size (in bytes) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_SIZE or 104857600 (100MB))
  -t, --rotation-time       maximum lifetime (in seconds) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_TIME or 86400 (24H))
  -S, --rotation-suffix     strftime based suffix to append to rotated log files (default: content of environment variable LOGPROXY_ROTATION_SUFFIX or .%%Y%%m%%d%%H%%M%%S)
  -d, --log-directory       directory to store log files (default: content of environment variable LOGPROXY_LOG_DIRECTORY or current directory), directory is created if missing
  -n, --rotated-files       maximum number of rotated files to keep including main one (0 => no cleaning, default: content of environment variable LOGPROXY_ROTATED_FILES or 5)
  -T, --timestamps          strftime prefix to prepend to every output line (default: content of environment variable LOGPROXY_TIMESTAMPS or none)
  -c, --chmod               if set, chmod the logfile to this value, '0700' for example (default: content of environment variable LOGPROXY_CHMOD or NULL)
  -o, --chown               if set, try (if you don't have sufficient privileges, it will fail silently) to change the owner of the logfile to the given user value
  -g, --chgrp               if set, try (if you don't have sufficient privileges, it will fail silently) to change the group of the logfile to the given group value
  -m, --use-locks           use locks to append to main log file (useful if several process writes to the same file)
  -O, --stdout              stdout file path (NULL string (default) can be used to redirect to /dev/null)
  -E, --stderr              stderr file path (STDOUT string (default) can be used to redirect to the same file than stdout)
  -F, --fifo-tmp-dir        directory where to store tmp FIFO for log_proxy (default: content of environment variable TMPDIR if set, /tmp if not)

How to build?

Requirements

A Linux/Unix distribution with standard development tools (git, gcc, make, pkg-config) and glib2 library with devel support (provided for example in CentOS 6 in the glib2-devel standard package).

Build and install

git clone https://github.com/metwork-framework/log_proxy # or download/unpack a zip with the github interface
cd log_proxy
make

Then as root user or prefixed with sudo:

make install

This will install log_proxy and log_proxy_wrapper in /usr/local/bin, by default.  make install also supports PREFIX=... for installing (for example) into a /usr directory other than the one in /usr/local, and DESTDIR=... for installing into $DESTDIR/$PREFIX/bin which is useful when making packages.  

Contributing guide

See CONTRIBUTING.md file.

Code of Conduct

See CODE_OF_CONDUCT.md file.

Sponsors

(If you are officially paid to work on MetWork Framework, please contact us to add your company logo here!)

logo

log_proxy's People

Contributors

metworkbot avatar mk-fg avatar thebaptiste avatar thefab avatar tomalok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

log_proxy's Issues

log_proxy_wrapper fails with -O, -E, and -F, missing new options.

What happened to --stdout, --stderr, and --fifo-tmp-dir? I'm not seeing it in v0.4.0's log_proxy_wrapper --help...

$ /usr/bin/log_proxy_wrapper -F /var/log/log_proxy -O /var/log/docker.log -s 10485760 -t 0 -- /usr/bin/dockerd
Usage:
  log_proxy_wrapper [OPTION…] -- COMMAND [COMMAND_ARG1] [COMMAND_ARG2] [...] - log proxy

Help Options:
  -h, --help                Show help options

Application Options:
  -s, --rotation-size       maximum size (in bytes) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_SIZE or 104857600 (100MB))
  -t, --rotation-time       maximum lifetime (in seconds) for a log file before rotation (0 => no maximum, default: content of environment variable LOGPROXY_ROTATION_TIME or 86400 (24H))
  -S, --rotation-suffix     strftime based suffix to append to rotated log files (default: content of environment variable LOGPROXY_ROTATION_SUFFIX or .%%Y%%m%%d%%H%%M%%S)
  -d, --log-directory       directory to store log files (default: content of environment variable LOGPROXY_LOG_DIRECTORY or current directory), directory is created if missing
  -n, --rotated-files       maximum number of rotated files to keep including main one (0 => no cleaning, default: content of environment variable LOGPROXY_ROTATED_FILES or 5)
  -m, --use-locks           use locks to append to main log file (useful if several process writes to the same file)

Specifically, i'm trying to use --fifo-tmp-dir (via the -F shortcut) and it's not there.

I'd also expect to be seeing the recent --chmod, --chown, and --chgrp, too. Those are showing up in the help for log_proxy itself -- just not the the proxy...

Rotate files at fixed points in time

When you specify a rotation-time the log files are rotated in those intervals based on the start time of the log_proxy. It would be nice if it would also be possible to rotate the files at fixed points in time, e.g. every day at midnight or every new start of an hour (maybe with some kind of cron expression)

binaries are small, dependencies are not

log_proxy and log_proxy_wrapper are very small -- about 26 KiB each on an Alpine x86_64 host.

However, the lib dependencies that get pulled in, are well over 1 MiB...

# ldd /usr/bin/log_proxy_wrapper
	/lib/ld-musl-x86_64.so.1 (0x7f2ac7afa000)
	libglib-2.0.so.0 => /usr/lib/libglib-2.0.so.0 (0x7f2ac79f1000)
	libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f2ac7afa000)
	libpcre.so.1 => /usr/lib/libpcre.so.1 (0x7f2ac7995000)
	libintl.so.8 => /usr/lib/libintl.so.8 (0x7f2ac7988000)

# ls -lh /usr/lib/libglib-2.0.so.0.6400.4
-rwxr-xr-x    1 root     root     1019.6K Jul  4 11:50 /usr/lib/libglib-2.0.so.0.6400.4
# ls -lh /usr/lib/libintl.so.8.1.7
-rwxr-xr-x    1 root     root       41.8K Apr 19 16:02 /usr/lib/libintl.so.8.1.7
# ls -lh /usr/lib/libpcre.so.1.2.12
-rwxr-xr-x    1 root     root      361.7K Feb 13 11:04 /usr/lib/libpcre.so.1.2.12

Alpine Linux likes to keep things small. I'm curious if the total install size would be significantly less if statically compiled?

Are two separate binaries necessary?

Would there any significant space savings if log_proxy and log_proxy_wrapper were the same binary, but recognized by what name they were executed and acted accordingly?

Take busybox as an example. Lots of individual utilities sharing a lot of common code, a lot of hard links to one binary.

wrapper makes fifo in /tmp

As I explore using this for rotating logs from dockerd which is managed by OpenRC's supervise-daemon process, I see that log_proxy_wrapper creates FIFOs in /tmp by default but is overridable via the TMPDIR env var.

It would be handy if this were also explicitly overridable via a command line option, as modifying the TMPDIR itself may have additional consequences for the command being wrapped.

Logs lose permissions after rotation

alpine@m0:~$ ls -l /var/log/docker.log*
-rw-------    1 root     root       4958249 Nov 29 04:35 /var/log/docker.log
-rw-r--r--    1 root     docker    10485892 Nov 20 20:44 /var/log/docker.log.20201120204416
-rw-------    1 root     root      10823084 Nov 29 02:42 /var/log/docker.log.20201129024242

The init.d script sets the initial mode & ownership, but that's not persisted going forward.

It would be nice if either...

  • log_proxy would preserve the original mode/ownership
    ...or...
  • log_proxy could be provided options to set mode/ownership

problem when a maximum file size if set by ulimit

and when logfiles are bigger than this limit, log_proxy crash:

write(5, "2020-03-11T09:39:30.064118Z     "..., 127) = -1 EFBIG (File too large)
--- SIGXFSZ {si_signo=SIGXFSZ, si_code=SI_USER, si_pid=18052, si_uid=500} ---
+++ killed by SIGXFSZ +++

http://man7.org/linux/man-pages/man2/getrlimit.2.html

we can catch SIGXFSZ limit and force a rotation in this case? But I think we will loose some logs in that case?

maybe we should also automatically reduce the rotation-size at the rlimit - 10% or something like that?

large memory usage for some log_proxy instances

We run log_proxy running for several hosts and services (1000+ services). I think we have just one host with unusual memory usage for a small number of log_proxy instances:

USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root        1281  0.1  0.8 578928 256336 ?       Sl   Feb20   3:05 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs a.041257.log
root        1305  2.1  9.4 3529988 3004128 ?     Sl   Feb20  36:15 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs b.041257.log
root        1407  0.0  0.0 296264   164 ?        Sl   Feb20   0:07 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs c.041257.log
user1234    1432  0.0  0.0 296264   164 ?        Sl   Feb20   0:07 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs d.041257.log
redissvs    1466  0.0  0.0 296264   168 ?        Sl   Feb20   0:07 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs e.041257.log
dataser+    3604  0.0  0.2 366620 68908 ?        Sl   Feb20   0:51 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs f.041259.log
redissvs    3605  2.0  6.0 3562760 1911792 ?     Sl   Feb20  34:44 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs g.041259.log
root      317774  0.0  0.0 296264   204 ?        Sl   Feb20   0:06 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs h.102941.log
user1234  333011  0.0  0.0 296264   204 ?        Sl   Feb20   0:05 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs i.105938.log
root      376175  0.0  0.0 296264   204 ?        Sl   Feb20   0:05 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs k.120100.log
root      376180  0.0  0.0 296264   200 ?        Sl   Feb20   0:05 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs l.120100.log
user1234  508291  4.3 12.2 4188824 3893692 ?     Sl   Feb20  43:26 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs m.160201.log
user1234  537435  0.0  0.0 296264  1044 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs n.161501.log
dataser+  537437  0.0  0.0 296264  1000 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs o.161501.log
nginx     537441  0.0  0.0 296264  1048 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs p.161501.log
user1234  558255  3.0  8.1 2881436 2586316 ?     Sl   Feb20  29:28 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs q.163000.log
user1234  559646  4.4 12.3 4236592 3941460 ?     Sl   Feb20  42:52 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs r.163300.log
user1234  561055  0.3  0.6 511164 216008 ?       Sl   Feb20   2:55 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs s.163900.log
user1234  561360  0.0  0.0 296264  1064 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs t.164100.log
user1234  561366  2.0  4.9 1870932 1575812 ?     Sl   Feb20  19:20 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs u.164100.log
user1234  565690  0.0  0.0 296264  1096 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs v.164500.log
user1234  568826  0.0  0.0 296264  1052 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs w.165100.log
user1     662463  0.0  0.0 296264  1356 ?        Sl   Feb20   0:02 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs x.192412.log
user1234  815311  8.3 12.2 4205172 3910344 ?     Sl   00:00  43:39 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs y.000001.log
user1     969050  0.0  0.0 296264  1320 ?        Sl   04:21   0:00 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs z.042118.log
user1234 1046991  0.0  0.0 296264  1580 ?        Sl   07:31   0:00 /opt/bin/log_proxy -c 644 -n 0 -s 0 -t 0 -d /opt/logs 1.073107.log

It's the same log_proxy binary on all hosts. Any idea on what would cause this or how to debug?

Handling of double-quotes in command-line arguments of log_proxy_wrapper

Currently log_proxy_wrapper creates log_proxy subprocess like this:

gchar *cli = g_strdup_printf("log_proxy -s %li -t %li -S \"%s\" -n %i %s -r -f \"%s\" \"%s\"", rotation_size, rotation_time, rotation_suffix, rotated_files, use_locks_str, fifo_path, log_path);
gboolean spawn_res = g_spawn_command_line_async(cli, NULL);

Which I think should not work if any of the string values templated there have double-quote characters in them.

For example:

log_proxy_wrapper -S '.%Y%m%d_%H%M%S"' -O /tmp/test.log -- cat <<< test

I'd expect this to work for any valid strftime template, which can include double quotes, but it does not with current release version, with the following error:

(log_proxy_wrapper:121851): log_proxy-CRITICAL **: 03:08:45.541: can't spawn log_proxy -s 104857600 -t 86400 -S ".%Y%m%d_%H%M%S"" -T "(null)" -n 5  -r -f "/tmp/log_proxy_stdout_4c2e654e021d1c1d8acbfadbb111e995.fifo" "/tmp/test.log" => exit

Fix can probably be using a different g_spawn_* call, which passes through individual arguments to underlying exec syscall, instead of parsing them from a shell-like quoted string, which will always be unreliable and depending on that parsing logic.

introduce a log directory

Now, LOGNAME can be a file name or a complete file path. It's great.

But it would be nice to add a --log-directory option (default to LOGPROXY_LOG_DIRECTORY or empty) to add before LOGNAME to build the complete log file path.

For example:

[...] |log_proxy --log-directory=/tmp foo.log
# will log in /tmp/foo.log

[...] |log_proxy --log-directory=/tmp/foo bar/foo.log
# will log in /tmp/foo/bar/foo.log

[...] |log_proxy foo.log
# will log in foo.log (current directory) [if LOGPROXY_LOG_DIRECTORY is empty]

[...] |log_proxy foo.log
# will log in /tmp/foo/foo.log (current directory) [if LOGPROXY_LOG_DIRECTORY == "/tmp/foo"]

[...] |log_proxy /tmp/foo.log
# will log in /tmp/foo.log

[...] |log_proxy --log-directory=/foo /tmp/foo.log
# will log in /tmp/foo.log (because LOGNAME starts with `/`, `--log-directory` is ignored)

[...] |log_proxy --log-directory=/foo/ bar.log
# will raise an error (because we don't want any trailing `/` on log-directory)

It would be really nice for metwork plugins to be able to change all log files location with a simple env var change in the plugin env.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.