Skip to content
Snippets Groups Projects
Commit 1efab69e authored by ale's avatar ale
Browse files

initial commit

parents
Branches
No related tags found
No related merge requests found
COPYING 0 → 100644
Copyright (c) 2015, <ale@incal.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
nginx-log-peeker
================
Tool to peek at the NGINX access logs only when needed, without saving
them to disk.
NGINX can send its access logs to a named pipe (FIFO) on the
filesystem, and it does a reasonably good job at fault isolation if
the pipe can't be written to during normal operation. Unfortunately,
it will still block on reloads and other occasions unless there is an
active listener on the pipe. The purpose of this program is to
constantly read from the logs FIFO, and provide a copy of it to
clients connecting to the daemon through a local UNIX socket.
This allows real-time debugging and analysis, without necessarily
having to store logs to disk at any time (which may be undesirable for
compliance purposes).
## Usage
1. Create the FIFO used to communicate with NGINX, and make sure that
the user that NGINX is running as can write to it:
$ mkfifo --mode=0700 /var/run/nginx/log-fifo
$ chown www-data /var/run/nginx/log-fifo
2. Edit your `nginx.conf` to tell NGINX to write the HTTP access logs
to the named pipe:
http {
access_log /var/run/nginx/log-fifo
...
}
3. Start the nginx-log-peeker daemon:
$ nginx-log-peeker &
4. Connect to the log peeker UNIX socket to retrieve logs in real-time
whenever you need to debug something:
$ socat UNIX-CONNECT:/var/run/nginx/log-peek -
nginx-log-peeker (0.1) unstable; urgency=low
* Initial Release.
-- Autistici/Inventati <debian@autistici.org> Sat, 24 Jan 2015 16:14:07 +0000
9
Source: nginx-log-peeker
Section: web
Priority: optional
Maintainer: Autistici/Inventati <debian@autistici.org>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.4
Package: nginx-log-peeker
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Nginx log peeker.
Read Nginx HTTP access logs in real-time.
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: nginx-log-peeker
Source: <https://git.autistici.org/ai/nginx-log-peeker>
Files: *
Copyright: 2015 Autistici/Inventati <info@autistici.org>
License: MIT
Files: debian/*
Copyright: 2015 Autistici/Inventati <info@autistici.org>
License: MIT
License: MIT
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md
#!/bin/sh
### BEGIN INIT INFO
# Provides: nginx-log-peeker
# Required-Start: $local_fs $network $remote_fs $syslog
# Required-Stop: $local_fs $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Nginx log peeker.
# Description: Read Nginx access logs in real-time.
### END INIT INFO
# Author: Autistici/Inventati <debian@autistici.org>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="nginx-log-peeker"
NAME=nginx-log-peeker
DAEMON=/usr/sbin/nginx-log-peeker
DAEMON_ARGS=""
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE \
--make-pidfile --background --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
PKGDIR = $(CURDIR)/debian/nginx-log-peeker
%:
dh $@
override_dh_install:
install -d -m 755 -o root -g root $(PKGDIR)/usr/sbin
go build -o $(PKGDIR)/usr/sbin/nginx-log-peeker main.go
chmod 755 $(PKGDIR)/usr/sbin/nginx-log-peeker
chown root:root $(PKGDIR)/usr/sbin/nginx-log-peeker
3.0 (native)
main.go 0 → 100644
package main
import (
"flag"
"io"
"log"
"net"
"os"
"sync"
)
var (
logFifoPath = flag.String("log-fifo", "/var/run/nginx/log-fifo", "location of the NGINX logs FIFO")
socketPath = flag.String("socket", "/var/run/nginx/log-peek", "socket for local connections")
)
// Read from a named pipe (FIFO), re-opening the file whenever reading
// returns EPIPE (i.e., the pipe is closed from the other side).
type pipeReader struct {
path string
reader *os.File
}
func NewPipeReader(path string) (*pipeReader, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
return &pipeReader{
path: path,
reader: f,
}, nil
}
func (r *pipeReader) Close() error {
return r.reader.Close()
}
func (r *pipeReader) Read(buf []byte) (int, error) {
for {
n, err := r.reader.Read(buf)
if err != nil {
if err == io.ErrClosedPipe || err == io.EOF {
if r.reader, err = os.Open(r.path); err == nil {
continue
}
}
return 0, err
}
return n, nil
}
}
// Server listens on a UNIX socket and multiplexes data read from a
// pipeReader onto eventual connections. Data is consumed from the
// pipeReader even if there are no connections. It is assumed that
// clients can consume input as quickly as it is written, otherwise
// the whole Server will block waiting for them (which isn't a
// particularly clever design, but it works for the intended use
// case).
type Server struct {
lock sync.Mutex
pipe *pipeReader
conns map[net.Conn]struct{}
listener *net.UnixListener
}
func NewServer(pipe *pipeReader, socketPath string) (*Server, error) {
os.Remove(socketPath)
l, err := net.ListenUnix("unix", &net.UnixAddr{socketPath, "unix"})
if err != nil {
return nil, err
}
return &Server{
pipe: pipe,
listener: l,
conns: make(map[net.Conn]struct{}),
}, nil
}
func (s *Server) readLogs() {
buf := make([]byte, 65536)
for {
n, err := s.pipe.Read(buf)
if err != nil {
log.Fatal(err)
}
data := buf[:n]
s.lock.Lock()
for conn := range s.conns {
_, err := conn.Write(data)
if err != nil {
delete(s.conns, conn)
conn.Close()
}
}
s.lock.Unlock()
}
}
func (s *Server) Run() {
go s.readLogs()
for {
conn, err := s.listener.AcceptUnix()
if err != nil {
log.Fatal(err)
}
s.lock.Lock()
s.conns[conn] = struct{}{}
s.lock.Unlock()
}
}
func main() {
log.SetFlags(0)
flag.Parse()
pipe, err := NewPipeReader(*logFifoPath)
if err != nil {
log.Fatal(err)
}
srv, err := NewServer(pipe, *socketPath)
if err != nil {
log.Fatal(err)
}
srv.Run()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment