pax_global_header00006660000000000000000000000064134107044070014512gustar00rootroot0000000000000052 comment=a0935ab2e7056feb3e8a90f5129990c9b335a587 logtop-logtop-0.7/000077500000000000000000000000001341070440700141665ustar00rootroot00000000000000logtop-logtop-0.7/.gitignore000066400000000000000000000000731341070440700161560ustar00rootroot00000000000000examples/example1 liblogtop.* logtop src/*.o src/libavl/*.ologtop-logtop-0.7/COPYRIGHT000066400000000000000000000024631341070440700154660ustar00rootroot00000000000000logtop is distributed under the following terms: Copyright (c) 2010 Palard Julien. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. logtop-logtop-0.7/ChangeLog000066400000000000000000000216551341070440700157510ustar00rootroot000000000000002013-08-10 18:04:37 +0200 Julien Palard * Documentation and example of the Python API 2013-08-10 17:45:24 +0200 Julien Palard * Documentation and example of the C API 2013-08-10 17:44:12 +0200 Julien Palard * FIX: Style 2013-08-10 17:44:01 +0200 Julien Palard * NEW: delete_logtop_state 2013-08-10 17:43:32 +0200 Julien Palard * FIX: Makefile to build liblogtop. 2013-07-26 10:07:37 +0200 Julien Palard * Version from Makefile 2013-07-26 09:57:55 +0200 Julien Palard * FIX: Memory leak in python binding 2013-04-12 11:23:33 +0200 Julien Palard * Adding some doc about python module dependencies 2013-03-20 11:36:33 +0100 Julien Palard * NEW: Give also global frequency to python module 2013-03-20 11:30:08 +0100 Julien Palard * NEW: Give frequency of every lines to python module 2013-03-20 10:01:36 +0100 Julien Palard * FIX: Continue to increment time when no new lines are fed 2013-03-20 09:54:29 +0100 Julien Palard * Cleaning .pyc files 2013-03-20 09:54:21 +0100 Julien Palard * Adding a test file for python module 2013-03-19 18:43:40 +0100 Julien Palard * Do not give simplified representation of the string to python, seems useless. 2013-03-14 00:28:52 +0100 Julien Palard * Adding python module via swig 2013-03-14 00:25:40 +0100 Julien Palard * Simplier to use a struct for swig 2013-03-13 23:37:58 +0100 Julien Palard * Adding a logtop_get function 2013-03-13 23:11:18 +0100 Julien Palard * Provide a logtop_delete function, renames for consistency 2013-03-13 19:11:18 +0100 Julien Palard * Renaming frequency -> logtop, more consistent 2013-03-13 19:06:24 +0100 Julien Palard * Renaming logtop -> main (see next commit) 2013-03-13 18:34:34 +0100 Julien Palard * Splitting code, first step to provide a lib_logtop. 2012-11-17 23:48:35 +0100 Julien Palard * UPDATE: Display improvment 2012-11-17 17:28:44 +0100 Julien Palard * ADD: check-syntax in Makefile for Flymake 2012-11-17 17:00:33 +0100 Julien Palard * FIX: Diplay consistency in curses mode 2012-11-17 16:59:57 +0100 Julien Palard * FIX: Update display every second even in the absence of new lines 2012-11-17 15:56:27 +0100 Julien Palard * FIX: ChangeLog readability 2012-11-17 15:08:14 +0100 Julien Palard * FIX: Dead lines at the bottom on the list. 2012-11-06 18:24:22 +0100 Julien Palard * FIX: Usage 2012-05-28 15:26:45 +0200 Julien Palard * FIX: Support LDFLAGS in Makefile 2012-05-21 15:01:50 +0200 Julien Palard * FIX: Forgot a debug. 2012-05-20 21:58:59 +0200 Julien Palard * Upgrading documentation and manpage. 2012-05-20 21:25:57 +0200 Julien Palard * Prit usage if stdin is a TTY. 2012-05-20 21:22:16 +0200 Julien Palard * Renaming without_curses in quiet. 2012-05-20 21:09:23 +0200 Julien Palard * NEW: Line by line reporting. 2012-05-20 19:40:49 +0200 Julien Palard * NEW: -q option to disable curses 2012-05-20 19:28:11 +0200 Julien Palard * Enhancing ccurses view. 2012-05-20 17:56:02 +0200 Julien Palard * Code uniformisation and -Wstrict-prototypes 2012-05-20 17:51:38 +0200 Julien Palard * NEW: Only print the top 10 elements at exit. 2012-05-20 16:33:48 +0200 Julien Palard * NEW: Handling SIGINT. 2011-05-31 18:43:43 +0200 Julien * Adding a missing build dependency in the README file (uthash-dev) 2011-05-31 18:31:07 +0200 Julien * Addin an 'About libavl' in the README file 2011-05-31 18:24:29 +0200 Julien * Updating README to reflect the actual implementation, and fixing a typo 2011-05-03 23:47:50 +0200 Julien * Replacing strings storage, from an AVL tree to an hashtable, little gain in performance (~20%) But keeping an AVL tree for the rank (as it needs to be sorted) 2011-04-16 12:30:42 +0200 Julien * FIX: If a line does not end with \n (the last?) it was reported as an empty string. NEW: Compatibility with lines ending with CR+LF, LF+CR, LF, or CR (and any other combinations !) 2011-04-16 12:17:22 +0200 Julien * Some corretion to the manpage 2011-04-16 12:07:46 +0200 Julien * Merge branch 'master' of github.com:JulienPalard/logtop 2011-04-16 12:06:25 +0200 Julien * Rewriting the README to remove livavl dependencies 2011-03-16 22:47:48 +0100 Julien Palard * FIX: Forgotten .o in libavl folder while make clean 2011-03-16 08:51:45 +0100 Julien Palard * Fixed a bug that makes some entry to appear more than once in the result. 2011-03-15 23:07:01 +0100 Julien Palard * Fixing display bug 2011-03-15 23:06:24 +0100 Julien Palard * Removing a useless strlen 2011-03-15 23:01:58 +0100 Julien Palard * traverse_log_lines can yield NULL, so we have to check it 2011-03-15 22:51:07 +0100 Julien Palard * Simple refactoring of writers 2011-03-15 20:41:29 +0100 Julien Palard * Removing one line in the stdout output module to keep the first line of result, previously dropped due to the appearing shell line of the user 2011-03-15 20:39:29 +0100 Julien Palard * Removing the unused UNUSED define and protecting STRINGIFY 2011-03-15 20:35:26 +0100 Julien Palard * Dropping dependency to Debian's libavl in favor of the pfaff's one, statically build 2011-01-16 20:32:26 +0100 Julien * Adding Debian changelog 2010-12-28 10:15:17 +0100 mandark * Refactorig for readeability 2010-12-28 09:46:32 +0100 mandark * Moving history structure to history.h 2010-12-28 09:42:26 +0100 mandark * Updating licences 2010-12-28 09:20:37 +0100 mandark * Refactoring for readeability 2010-12-28 08:51:29 +0100 mandark * Splitting history managment in history.c 2010-12-27 08:41:17 +0100 Julien * Fixing manpage 2010-12-12 20:57:50 +0100 mandark * FIX: More verbose help statement FIX: Removing deprecated initialisation of value while parsing arguments 2010-12-12 20:52:37 +0100 mandark * FIX: Fixing help to remove deprecated -c parameter 2010-12-12 20:51:31 +0100 mandark * ADD: Compile dependencies in README file 2010-12-12 00:18:40 +0100 Julien * NEW: Use ioctl to determine display height. 2010-12-05 20:13:58 +0100 Julien * Adding make install and a man 2010-12-05 20:13:02 +0100 Julien * Adding -v to get version and -h to get some help 2010-12-05 19:25:56 +0100 Julien * Fine tuning visualisation 2010-12-03 09:57:41 +0100 Julien * FIX: Replace specials chars like \r to dots to ensure graphical concistency 2010-11-14 22:22:03 +0100 Julien * FIX: display_height was non used in stdout display 2010-11-14 15:30:46 +0100 Julien * Style improvment and renaming 2010-11-13 21:45:03 +0100 Julien * Improving a bit the UI adding the number of log lines read by second ... 2010-11-13 21:27:32 +0100 Julien * Oops just forgotten -lefence in da Makefile :p 2010-11-13 21:24:51 +0100 Julien * Fix a bug in "get_newest_element_in_history" 2010-11-13 21:09:49 +0100 Julien * Fixing headers 2010-11-13 17:32:20 +0100 Julien * NEW: Now displaying loglines by seconds as : 917 25/s foobar 2010-11-13 15:33:54 +0100 Julien * ADD: Each history entity is now dated, i'll can add some feature like a rotation by duration. 2010-11-13 15:06:13 +0100 Julien * FIX: Bug during optparse 2010-11-13 15:00:27 +0100 Julien * Adding two new files after splitting source 2010-11-13 14:59:28 +0100 Julien * ADD: Dump last view on stdout leaving FIX: Restore curses leaving 2010-07-01 19:17:10 +0200 Julien * Add usage sample to the README 2010-07-01 19:09:12 +0200 Julien * Updating README : Adding dependencies 2010-07-01 19:05:05 +0200 Julien * EHLO World logtop-logtop-0.7/Makefile000066400000000000000000000026561341070440700156370ustar00rootroot00000000000000## ## Makefile for logtop ## ## Made by julien palard ## Login ## VERSION = 0.7.0 MINOR = 0 RELEASE = 0 NAME = logtop UNAME=$(shell uname -s) ifeq ($(UNAME),Darwin) LINKERNAME = lib$(NAME).dylib else LINKERNAME = lib$(NAME).so endif SONAME=$(LINKERNAME).$(VERSION) REALNAME=$(SONAME).$(MINOR).$(RELEASE) LIB_SRC = src/avl.c src/history.c \ src/logtop.c src/libavl/avl.c SRC = $(LIB_SRC) src/curses.c src/stdout.c src/main.c LIB_OBJ = $(LIB_SRC:.c=.o) OBJ = $(SRC:.c=.o) CC = gcc override INCLUDE += . LIB = $(shell pkg-config --libs ncursesw) #-lefence CFLAGS += -O3 -Wall -fPIC -Wextra -pedantic -Wstrict-prototypes -I$(INCLUDE) $(shell pkg-config --cflags ncursesw) RM = rm -fr LDFLAGS = $(NAME): $(OBJ) $(CC) -o $(NAME) $(OBJ) $(LIB) $(LDFLAGS) lib$(NAME): $(LIB_OBJ) $(CC) $(CFLAGS) --shared -o $(LINKERNAME) $(OBJ) $(LIB) $(LDFLAGS) install: $(NAME) mkdir -p $(DESTDIR)/usr/bin/ cp $(NAME) $(DESTDIR)/usr/bin/ python-module: swig -python *.i python ./setup.py build_ext --inplace python3-module: swig -python *.i python3 ./setup.py build_ext --inplace all: @make $(NAME) @make lib$(NAME) .c.o: $(CC) -c $(CFLAGS) $< -o $(<:.c=.o) clean: $(RM) $(NAME) src/*~ src/#*# src/*.o src/*.core \ src/libavl/*.o _logtop.* liblogtop.* \ logtop.py *.pyc build/ logtop_wrap.c re: clean all check-syntax: gcc -Isrc -Wall -Wextra -ansi -pedantic -o /dev/null -S ${CHK_SOURCES} logtop-logtop-0.7/NCURSES_COPYRIGHT000066400000000000000000000062051341070440700166660ustar00rootroot00000000000000This is the Debian prepackaged version of the ncurses library and terminfo utilities. ncurses/terminfo was originally written by Pavel Curtis and Zeyd M. Ben-Halim , and is currently held by the Free Software Foundation. This package was put together by Vaidhyanathan G Mayilrangam and Joel Klecker , using sources obtained from ftp://ftp.gnu.org/gnu/ncurses/ncurses-5.0.tar.gz. It is based somewhat on work done by Bruce Perens , David Engel . Michael Alan Dorman , Richard Braakman , James Troup , J.H.M. Dassen (Ray) , and Galen Hazelwood over various years. Changes: * added Debian package maintenance system files * changed Makefile.in's to compile with -D_REENTRANT * changed configure.in to support proper compilation of debugging libraries * Fixed tget{flag,num,str} in lib_termcap ("long" strings work now) Changes to terminfo.src: * Removed ich(1) from linux, screen, vt320 /**************************************************************************** * Copyright (c) 1998 Free Software Foundation, Inc. * * * * 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, distribute with modifications, 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 ABOVE 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. * * * * Except as contained in this notice, the name(s) of the above copyright * * holders shall not be used in advertising or otherwise to promote the * * sale, use or other dealings in this Software without prior written * * authorization. * ****************************************************************************/ logtop-logtop-0.7/README.md000066400000000000000000000061201341070440700154440ustar00rootroot00000000000000# Install ## Debian / Debian based ``` apt install logtop ``` ## Void Linux ``` xbps-install -S logtop ``` ## From sources Compile dependencies: `libncursesw5-dev`, `uthash-dev`. So on a Debian: ```bash session $ sudo apt install libncursesw5-dev uthash-dev Reading package lists... Done [...skipped for readability...] Processing triggers for man-db (2.8.2-1) ... $ make gcc -c -O3 -DVERSION=0.7.0 -Wall -fPIC -Wextra -ansi -pedantic -Wstrict-prototypes -I. src/avl.c -o src/avl.o [...skipped for readability...] $ make install ``` # Usage `logtop` displays a real-time count of strings received from stdin. It can be useful in some cases, like getting the IP flooding your server: ```bash session $ tail -f /var/log/apache2/access.log | cut -d' ' -f1 | logtop ``` Or the top buzzing article of your blog: ```bash session $ tail -f /var/log/apache2/access.log | cut -d' ' -f7 | grep article | logtop ``` # Python bindings Dependencies: `python-dev`, `swig`. ```bash session $ sudo apt install python-dev swig Reading package lists... Done [...] $ make python-module [...] $ python setup.py install ``` # Development I use a hashtable to store strings and an AVL tree to store frequencies, so I can fetch by string or fetch ordered by frequency to display the top-strings. # C API Logtop can be used by your C programs, you may to compile against logtop's sources (`src/{avl.c,history.c,logtop.c,libavl/avl.c}`) or against `liblogtop`, obtained using `make liblogtop`. C API is described in `logtop.h`, you need: ```C struct logtop *new_logtop(size_t history_size); void delete_logtop(struct logtop *this); void logtop_feed(struct logtop *this, char *line); struct logtop_state *logtop_get(struct logtop *this, size_t qte); double logtop_timespan(struct logtop *this); unsigned int logtop_qte_of_elements(struct logtop *this); ``` You can find an example of using the C API in `examples/example1.c`. # Python API `logtop` module exposes a logtop class containing: ```Python logtop.__init__(history_size) # to build a new logtop keeping # at most history_size lines. logtop.feed(line) # to feed a new line in logtop. logtop.get(qte_of_elements) # to get the top qte_of_elements lines. logtop.qte_of_elements() # to get the current total number of lines. logtop.timespan() # to get the duration from the oldest line to now. ``` timespan may be less than the runtime, as logtop drop old lines, to keep, at most, `history_size` lines, given in the constructor of the logtop class. # About libavl The libavl used here is the Ben Pfaff's one, statically build with logtop, as Ben want it to be (see INSTALL file and here: http://lists.debian.org/debian-devel/2001/07/msg01303.html) So this libavl is NOT packaged as a library for Debian, the libavl you'll found in Debian repositories is the Wessel Dankers's one. # About the project For copyright information, please see the file COPYRIGHT in this directory or in the files of the source tree. This project was initiated on 2010-06-29 by Palard Julien See http://julien.palard.fr or ask me questions at : julien at palard dot fr. logtop-logtop-0.7/doc/000077500000000000000000000000001341070440700147335ustar00rootroot00000000000000logtop-logtop-0.7/doc/logtop.1000066400000000000000000000043211341070440700163210ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH LOGTOP 1 "April 16, 2011" .\" Please adjust this date whenever revising the manpage. .SH "NAME" logtop \- Realtime log line rate analyser .SH "SYNOPSIS" .B logtop .RI [ OPTIONS ] .SH "DESCRIPTION" \fBlogtop\fP is a System Administrator tool analyzing line rate on stdin. It reads on stdin and print a constantly updated result displaying, in columns: Line number, count, frequency, and the actual line. $ tail \-f FILE | \fBlogtop\fP is the friendly version of: $ watch 'tail FILE | sort | uniq \-c | sort \-gr' .PP .SH "OPTIONS" .TP .B \-s, \-\-size=K Only keep K lines in memory, instead of 10000. .TP .B \-q, \-\-quiet Do not display a live view of the data, only display a top at exit. .TP .B \-l, \-\-line-by-line=K Print result line by line, in a machine friendly format, K is the number of result to print per line. Line by line format is : [%d %f %s\\t]*\\n %d : Number of occurences %f : Frequency of apparition %s : String (Control chars replaced by dots. .TP .B \-i, \-\-interval=K Interval between graphical updates, in seconds. Defaults to 1. .TP .B \-h, \-\-help Show summary of options. .TP .B \-v, \-\-version Show version of program. .SH "EXAMPLES" .PP Here are some \fBlogtop\fP usage examples. .PP .PP \fBtail \-f cache.log | grep \-o "HIT\\|MISS" | logtop\fR .PP Realtime hit / miss ratio on some caching software log file. .PP \fBtail \-f access.log | cut \-d' ' \-f1 | logtop \-s 10000\fR .PP Realtime most querying IPs on your server, as long as log lines in access.log starts with the client IP. .PP \fBtail \-f access.log | cut \-d' ' \-f7 | logtop \-s 10000\fR .PP Realtime most requested web pages in a NCSA like log file. .PP \fBcat auth.log | grep \-v "CRON" | grep \-o ": .*" | logtop \-q \-s 100000\fR .PP Display a one\-shot simple analyse of your auth.log. .SH "SEE ALSO" .BR watch (1) .br .SH "AUTHOR" logtop was written by Julien Palard. .PP This manual page was written by Julien Palard , for the Debian project (and may be used by others). logtop-logtop-0.7/examples/000077500000000000000000000000001341070440700160045ustar00rootroot00000000000000logtop-logtop-0.7/examples/example1.c000066400000000000000000000051011341070440700176610ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* This example is a basic demo of logtop C API. You may compile it using : $ cd examples $ cc example1.c ../src/{avl.c,history.c,logtop.c,libavl/avl.c} -o example1 or $ make liblogtop $ cd examples $ cc example1.c -llogtop -L.. -o example1 $ LD_LIBRARY_PATH=.. ./example1 */ #include #include #include "../src/logtop.h" int main() { struct logtop *logtop; struct logtop_state *state; unsigned int i; logtop = new_logtop(10000); /* Don't keep more than 10k elements */ logtop_feed(logtop, "foo"); logtop_feed(logtop, "bar"); logtop_feed(logtop, "foo"); logtop_feed(logtop, "bar"); logtop_feed(logtop, "baz"); logtop_feed(logtop, "baz"); logtop_feed(logtop, "baz"); state = logtop_get(logtop, 10); /* Get the top 10 */ printf("%d lines seen during %.2fs (%.2f lines/s):\n", state->count, state->timespan, state->frequency); i = 0; while (state->lines[i] != NULL) { printf(" \"%s\" seen %d times (%.2f lines/s)\n", state->lines[i]->string, state->lines[i]->count, state->lines[i]->frequency); i += 1; } delete_logtop_state(state); delete_logtop(logtop); return EXIT_SUCCESS; } logtop-logtop-0.7/examples/example1.py000066400000000000000000000006501341070440700200730ustar00rootroot00000000000000#!/usr/bin/env python3 import pprint import sys from logtop import logtop """ This example show a complete usage of the python API, It can be used like this: $ make python-module $ python3 -m venv logtop_venv $ . logtop_venv/bin/activate $ python3 -m pip install . $ $ cat /etc/passwd | cut -d: -f7 | python3 examples/example1.py """ l = logtop(10000) for line in sys.stdin: l.feed(line) pprint.pprint(l.get(10)) logtop-logtop-0.7/logtop.i000066400000000000000000000024651341070440700156530ustar00rootroot00000000000000%module logtop %{ #include "src/logtop.h" %} %typemap(out) struct logtop_state * { int i; PyObject *log_line; PyObject *lines; PyObject *tmp; $result = PyDict_New(); lines = PyList_New(0); while (result->lines[i] != NULL) { log_line = PyTuple_New(3); PyTuple_SetItem(log_line, 0, PyInt_FromLong(result->lines[i]->count)); PyTuple_SetItem(log_line, 1, PyFloat_FromDouble(result->lines[i]->frequency)); PyTuple_SetItem(log_line, 2, PyString_FromString(result->lines[i]->string)); PyList_Append(lines, log_line); Py_DECREF(log_line); i++; } PyDict_SetItemString($result, "lines", lines); Py_DECREF(lines); tmp = PyInt_FromLong(result->count); PyDict_SetItemString($result, "count", tmp); Py_DECREF(tmp); tmp = PyFloat_FromDouble(result->timespan); PyDict_SetItemString($result, "timespan", tmp); Py_DECREF(tmp); tmp = PyFloat_FromDouble(result->frequency); PyDict_SetItemString($result, "frequency", tmp); Py_DECREF(tmp); delete_logtop_state(result); } struct logtop { %extend { logtop(size_t history_size); ~logtop(); void feed(char *line); struct logtop_state *get(size_t qte); double timespan(); unsigned int qte_of_elements(); } }; logtop-logtop-0.7/setup.py000066400000000000000000000023171341070440700157030ustar00rootroot00000000000000#!/usr/bin/env python """ setup.py file for logtop """ from distutils.core import setup, Extension logtop_module = Extension('_logtop', sources=['logtop_wrap.c', 'src/logtop.c', 'src/avl.c', 'src/history.c', 'src/libavl/avl.c']) setup(name='logtop', version='0.2', author="Julien Palard", author_email="julien@palard.fr", description="""Live line frequency analyzer""", long_description="""logtop is a System Administrator tool analyzing line rate on stdin. It reads on stdin and print a constantly updated result displaying, in columns: Line number, count, frequency, and the actual line.""", keywords=['log', 'top', 'lines', 'analyzer', 'nginx', 'apache', 'varnish', 'tail'], url='https://github.com/JulienPalard/logtop', classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: BSD License'], ext_modules=[logtop_module], py_modules=["logtop"]) logtop-logtop-0.7/src/000077500000000000000000000000001341070440700147555ustar00rootroot00000000000000logtop-logtop-0.7/src/avl.c000066400000000000000000000110221341070440700156770ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #include #include #include "logtop.h" static int compare_log_lines_count(const void *log_line1, const void *log_line2, void *avl_param) { (void) avl_param; if (((log_line_t*)log_line1)->count != ((log_line_t*)log_line2)->count) { if (((log_line_t*)log_line2)->count > ((log_line_t*)log_line1)->count) return 1; else return -1; } return (strcmp(((log_line_t*)log_line1)->string, ((log_line_t*)log_line2)->string)); } static void die(void) { fputs("Ran out of memory, commit suicide for important tasks to live !", stderr); exit(EXIT_FAILURE); } static char *repr(const char *str) { char *clean; int i; clean = strdup(str); if (clean == NULL) return NULL; for (i = 0; clean[i] != '\0'; ++i) if (clean[i] > 0 && clean[i] < ' ') clean[i] = '.'; return clean; } static log_line_t *create_log_entry(struct logtop *this, char *string) { log_line_t *entry; entry = (log_line_t*)malloc(sizeof(*entry)); if (entry == NULL) die(); entry->count = 0; entry->string = strdup(string); if (entry->string == NULL) die(); entry->repr = repr(string); if (entry->repr == NULL) die(); HASH_ADD_KEYPTR(hh, this->strings, entry->string, strlen(entry->string), entry); avl_insert(this->top, entry); return entry; } static void delete_log_entry(struct logtop *this, log_line_t *log_entry) { HASH_DEL(this->strings, log_entry); free(log_entry->string); free(log_entry->repr); free(log_entry); } struct avl_table *new_avl(struct logtop *this) { return avl_create(compare_log_lines_count, this, NULL); } static void free_avl_element(void *avl_item, void *avl_param) { delete_log_entry((struct logtop*)avl_param, (log_line_t *)avl_item); } void delete_avl(struct logtop *this) { avl_destroy(this->top, free_avl_element); } log_line_t *avl_get(struct logtop *this, char *string) { log_line_t *node; HASH_FIND_STR(this->strings, string, node); if (node != NULL) return node; else return create_log_entry(this, string); } void avl_increment(struct logtop *this, log_line_t *log_entry) { avl_delete(this->top, log_entry); log_entry->count += 1; avl_insert(this->top, log_entry); } void avl_decrement(struct logtop *this, log_line_t *log_entry) { avl_delete(this->top, log_entry); log_entry->count -= 1; if (log_entry->count != 0) avl_insert(this->top, log_entry); else delete_log_entry(this, log_entry); } void avl_traverse(struct logtop*logtop, unsigned int length, void (*visitor)(void *data, int index, void *user_data), void *user_data) { struct avl_traverser trav; void *node; unsigned int last; struct avl_table *tree; tree = logtop->top; last = length; node = avl_t_first(&trav, tree); while (length-- > 0 && node != NULL) { visitor(node, last - length, user_data); node = avl_t_next(&trav); } } logtop-logtop-0.7/src/curses.c000066400000000000000000000067331341070440700164360ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "main.h" static WINDOW *window; static void update_winsize(void) { struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1) { gl_env.display_height = 24; gl_env.display_width = 80; } else { gl_env.display_height = ws.ws_row; gl_env.display_width = ws.ws_col; } } static void on_sigwinch(int sig __attribute__((unused))) { update_winsize(); endwin(); delwin(window); window = newwin(gl_env.display_height, gl_env.display_width, 0, 0); curses_update(); } void curses_setup() { update_winsize(); setup_sighandler(SIGWINCH, SA_RESTART, on_sigwinch); initscr(); window = newwin(gl_env.display_height, gl_env.display_width, 0, 0); } void curses_release() { delwin(window); endwin(); } struct display_data { double duration; unsigned int qte_of_elements; }; static void display_line(void *data, int index, void *display_data) { log_line_t *line; line = (log_line_t *)data; mvwprintw(window, index + 1, 0, "%4d %6d %8.2f %-*s", index, line->count, line->count / ((struct display_data*)display_data)->duration, gl_env.display_width - 21, line->repr); } static void display_header(struct display_data *display_data) { mvwprintw(window, 0, 0, "%u lines, %.2f lines/s", display_data->qte_of_elements, display_data->qte_of_elements / display_data->duration); mvwprintw(window, 1, 0, "RANK CNT LINE/S LINE"); mvwchgat(window, 1, 0, -1, A_REVERSE, 0, NULL); } void curses_update() { struct display_data display_data; display_data.duration = logtop_timespan(gl_env.logtop); display_data.qte_of_elements = logtop_qte_of_elements(gl_env.logtop); if (display_data.duration == 0) display_data.duration = 1; werase(window); display_header(&display_data); avl_traverse(gl_env.logtop, gl_env.display_height - 2, display_line, &display_data); wrefresh(window); } logtop-logtop-0.7/src/history.c000066400000000000000000000057651341070440700166370ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "logtop.h" /* ** If the element under history_start is null ** then the history is not full. */ unsigned int history_length(struct logtop *this) { if (this->history[this->history_start].log_entry == NULL) return this->history_start; else return this->history_size; } history_element_t *history_oldest_element(struct logtop *this) { if (this->history[this->history_start].log_entry != NULL) { return &(this->history[this->history_start]); } else { if (this->history_start == 0) return NULL; return &(this->history[0]); } } history_element_t *history_newest_element(struct logtop *this) { int newest_item_index; newest_item_index = this->history_start == 0 ? this->history_size - 1 : this->history_start - 1; if (this->history[newest_item_index].log_entry == NULL) return NULL; else return &(this->history[newest_item_index]); } void history_update(struct logtop *this, log_line_t *element) { history_element_t *history_element; log_line_t *log_entry; history_element = &(this->history[this->history_start]); log_entry = history_element->log_entry; if (log_entry != NULL) avl_decrement(this, log_entry); this->history[this->history_start].log_entry = element; this->history[this->history_start].time = time(NULL); this->history_start += 1; if (this->history_start >= this->history_size) this->history_start = 0; } history_element_t *new_history(struct logtop *this) { return calloc(sizeof(history_element_t), this->history_size); } void delete_history(struct logtop *this) { free(this->history); } logtop-logtop-0.7/src/libavl/000077500000000000000000000000001341070440700162265ustar00rootroot00000000000000logtop-logtop-0.7/src/libavl/avl.c000066400000000000000000000602511341070440700171600ustar00rootroot00000000000000/* Produced by texiweb from libavl.w. */ /* libavl - library for manipulation of binary trees. Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The author may be contacted at on the Internet, or write to Ben Pfaff, Stanford University, Computer Science Dept., 353 Serra Mall, Stanford CA 94305, USA. */ #include #include #include #include #include "avl.h" /* Creates and returns a new table with comparison function |compare| using parameter |param| and memory allocator |allocator|. Returns |NULL| if memory allocation failed. */ struct avl_table * avl_create (avl_comparison_func *compare, void *param, struct libavl_allocator *allocator) { struct avl_table *tree; assert (compare != NULL); if (allocator == NULL) allocator = &avl_allocator_default; tree = allocator->libavl_malloc (allocator, sizeof *tree); if (tree == NULL) return NULL; tree->avl_root = NULL; tree->avl_compare = compare; tree->avl_param = param; tree->avl_alloc = allocator; tree->avl_count = 0; tree->avl_generation = 0; return tree; } /* Search |tree| for an item matching |item|, and return it if found. Otherwise return |NULL|. */ void * avl_find (const struct avl_table *tree, const void *item) { const struct avl_node *p; assert (tree != NULL && item != NULL); for (p = tree->avl_root; p != NULL; ) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp < 0) p = p->avl_link[0]; else if (cmp > 0) p = p->avl_link[1]; else /* |cmp == 0| */ return p->avl_data; } return NULL; } /* Inserts |item| into |tree| and returns a pointer to |item|'s address. If a duplicate item is found in the tree, returns a pointer to the duplicate without inserting |item|. Returns |NULL| in case of memory allocation failure. */ void ** avl_probe (struct avl_table *tree, void *item) { struct avl_node *y, *z; /* Top node to update balance factor, and parent. */ struct avl_node *p, *q; /* Iterator, and parent. */ struct avl_node *n; /* Newly inserted node. */ struct avl_node *w; /* New root of rebalanced subtree. */ int dir; /* Direction to descend. */ unsigned char da[AVL_MAX_HEIGHT]; /* Cached comparison results. */ int k = 0; /* Number of cached results. */ assert (tree != NULL && item != NULL); z = (struct avl_node *) &tree->avl_root; y = tree->avl_root; dir = 0; for (q = z, p = y; p != NULL; q = p, p = p->avl_link[dir]) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp == 0) return &p->avl_data; if (p->avl_balance != 0) z = q, y = p, k = 0; da[k++] = dir = cmp > 0; } n = q->avl_link[dir] = tree->avl_alloc->libavl_malloc (tree->avl_alloc, sizeof *n); if (n == NULL) return NULL; tree->avl_count++; n->avl_data = item; n->avl_link[0] = n->avl_link[1] = NULL; n->avl_balance = 0; if (y == NULL) return &n->avl_data; for (p = y, k = 0; p != n; p = p->avl_link[da[k]], k++) if (da[k] == 0) p->avl_balance--; else p->avl_balance++; if (y->avl_balance == -2) { struct avl_node *x = y->avl_link[0]; if (x->avl_balance == -1) { w = x; y->avl_link[0] = x->avl_link[1]; x->avl_link[1] = y; x->avl_balance = y->avl_balance = 0; } else { assert (x->avl_balance == +1); w = x->avl_link[1]; x->avl_link[1] = w->avl_link[0]; w->avl_link[0] = x; y->avl_link[0] = w->avl_link[1]; w->avl_link[1] = y; if (w->avl_balance == -1) x->avl_balance = 0, y->avl_balance = +1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == +1| */ x->avl_balance = -1, y->avl_balance = 0; w->avl_balance = 0; } } else if (y->avl_balance == +2) { struct avl_node *x = y->avl_link[1]; if (x->avl_balance == +1) { w = x; y->avl_link[1] = x->avl_link[0]; x->avl_link[0] = y; x->avl_balance = y->avl_balance = 0; } else { assert (x->avl_balance == -1); w = x->avl_link[0]; x->avl_link[0] = w->avl_link[1]; w->avl_link[1] = x; y->avl_link[1] = w->avl_link[0]; w->avl_link[0] = y; if (w->avl_balance == +1) x->avl_balance = 0, y->avl_balance = -1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == -1| */ x->avl_balance = +1, y->avl_balance = 0; w->avl_balance = 0; } } else return &n->avl_data; z->avl_link[y != z->avl_link[0]] = w; tree->avl_generation++; return &n->avl_data; } /* Inserts |item| into |table|. Returns |NULL| if |item| was successfully inserted or if a memory allocation error occurred. Otherwise, returns the duplicate item. */ void * avl_insert (struct avl_table *table, void *item) { void **p = avl_probe (table, item); return p == NULL || *p == item ? NULL : *p; } /* Inserts |item| into |table|, replacing any duplicate item. Returns |NULL| if |item| was inserted without replacing a duplicate, or if a memory allocation error occurred. Otherwise, returns the item that was replaced. */ void * avl_replace (struct avl_table *table, void *item) { void **p = avl_probe (table, item); if (p == NULL || *p == item) return NULL; else { void *r = *p; *p = item; return r; } } /* Deletes from |tree| and returns an item matching |item|. Returns a null pointer if no matching item found. */ void * avl_delete (struct avl_table *tree, const void *item) { /* Stack of nodes. */ struct avl_node *pa[AVL_MAX_HEIGHT]; /* Nodes. */ unsigned char da[AVL_MAX_HEIGHT]; /* |avl_link[]| indexes. */ int k; /* Stack pointer. */ struct avl_node *p; /* Traverses tree to find node to delete. */ int cmp; /* Result of comparison between |item| and |p|. */ assert (tree != NULL && item != NULL); k = 0; p = (struct avl_node *) &tree->avl_root; for (cmp = -1; cmp != 0; cmp = tree->avl_compare (item, p->avl_data, tree->avl_param)) { int dir = cmp > 0; pa[k] = p; da[k++] = dir; p = p->avl_link[dir]; if (p == NULL) return NULL; } item = p->avl_data; if (p->avl_link[1] == NULL) pa[k - 1]->avl_link[da[k - 1]] = p->avl_link[0]; else { struct avl_node *r = p->avl_link[1]; if (r->avl_link[0] == NULL) { r->avl_link[0] = p->avl_link[0]; r->avl_balance = p->avl_balance; pa[k - 1]->avl_link[da[k - 1]] = r; da[k] = 1; pa[k++] = r; } else { struct avl_node *s; int j = k++; for (;;) { da[k] = 0; pa[k++] = r; s = r->avl_link[0]; if (s->avl_link[0] == NULL) break; r = s; } s->avl_link[0] = p->avl_link[0]; r->avl_link[0] = s->avl_link[1]; s->avl_link[1] = p->avl_link[1]; s->avl_balance = p->avl_balance; pa[j - 1]->avl_link[da[j - 1]] = s; da[j] = 1; pa[j] = s; } } tree->avl_alloc->libavl_free (tree->avl_alloc, p); assert (k > 0); while (--k > 0) { struct avl_node *y = pa[k]; if (da[k] == 0) { y->avl_balance++; if (y->avl_balance == +1) break; else if (y->avl_balance == +2) { struct avl_node *x = y->avl_link[1]; if (x->avl_balance == -1) { struct avl_node *w; assert (x->avl_balance == -1); w = x->avl_link[0]; x->avl_link[0] = w->avl_link[1]; w->avl_link[1] = x; y->avl_link[1] = w->avl_link[0]; w->avl_link[0] = y; if (w->avl_balance == +1) x->avl_balance = 0, y->avl_balance = -1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == -1| */ x->avl_balance = +1, y->avl_balance = 0; w->avl_balance = 0; pa[k - 1]->avl_link[da[k - 1]] = w; } else { y->avl_link[1] = x->avl_link[0]; x->avl_link[0] = y; pa[k - 1]->avl_link[da[k - 1]] = x; if (x->avl_balance == 0) { x->avl_balance = -1; y->avl_balance = +1; break; } else x->avl_balance = y->avl_balance = 0; } } } else { y->avl_balance--; if (y->avl_balance == -1) break; else if (y->avl_balance == -2) { struct avl_node *x = y->avl_link[0]; if (x->avl_balance == +1) { struct avl_node *w; assert (x->avl_balance == +1); w = x->avl_link[1]; x->avl_link[1] = w->avl_link[0]; w->avl_link[0] = x; y->avl_link[0] = w->avl_link[1]; w->avl_link[1] = y; if (w->avl_balance == -1) x->avl_balance = 0, y->avl_balance = +1; else if (w->avl_balance == 0) x->avl_balance = y->avl_balance = 0; else /* |w->avl_balance == +1| */ x->avl_balance = -1, y->avl_balance = 0; w->avl_balance = 0; pa[k - 1]->avl_link[da[k - 1]] = w; } else { y->avl_link[0] = x->avl_link[1]; x->avl_link[1] = y; pa[k - 1]->avl_link[da[k - 1]] = x; if (x->avl_balance == 0) { x->avl_balance = +1; y->avl_balance = -1; break; } else x->avl_balance = y->avl_balance = 0; } } } } tree->avl_count--; tree->avl_generation++; return (void *) item; } /* Refreshes the stack of parent pointers in |trav| and updates its generation number. */ static void trav_refresh (struct avl_traverser *trav) { assert (trav != NULL); trav->avl_generation = trav->avl_table->avl_generation; if (trav->avl_node != NULL) { avl_comparison_func *cmp = trav->avl_table->avl_compare; void *param = trav->avl_table->avl_param; struct avl_node *node = trav->avl_node; struct avl_node *i; trav->avl_height = 0; for (i = trav->avl_table->avl_root; i != node; ) { assert (trav->avl_height < AVL_MAX_HEIGHT); assert (i != NULL); trav->avl_stack[trav->avl_height++] = i; i = i->avl_link[cmp (node->avl_data, i->avl_data, param) > 0]; } } } /* Initializes |trav| for use with |tree| and selects the null node. */ void avl_t_init (struct avl_traverser *trav, struct avl_table *tree) { trav->avl_table = tree; trav->avl_node = NULL; trav->avl_height = 0; trav->avl_generation = tree->avl_generation; } /* Initializes |trav| for |tree| and selects and returns a pointer to its least-valued item. Returns |NULL| if |tree| contains no nodes. */ void * avl_t_first (struct avl_traverser *trav, struct avl_table *tree) { struct avl_node *x; assert (tree != NULL && trav != NULL); trav->avl_table = tree; trav->avl_height = 0; trav->avl_generation = tree->avl_generation; x = tree->avl_root; if (x != NULL) while (x->avl_link[0] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; } trav->avl_node = x; return x != NULL ? x->avl_data : NULL; } /* Initializes |trav| for |tree| and selects and returns a pointer to its greatest-valued item. Returns |NULL| if |tree| contains no nodes. */ void * avl_t_last (struct avl_traverser *trav, struct avl_table *tree) { struct avl_node *x; assert (tree != NULL && trav != NULL); trav->avl_table = tree; trav->avl_height = 0; trav->avl_generation = tree->avl_generation; x = tree->avl_root; if (x != NULL) while (x->avl_link[1] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; } trav->avl_node = x; return x != NULL ? x->avl_data : NULL; } /* Searches for |item| in |tree|. If found, initializes |trav| to the item found and returns the item as well. If there is no matching item, initializes |trav| to the null item and returns |NULL|. */ void * avl_t_find (struct avl_traverser *trav, struct avl_table *tree, void *item) { struct avl_node *p, *q; assert (trav != NULL && tree != NULL && item != NULL); trav->avl_table = tree; trav->avl_height = 0; trav->avl_generation = tree->avl_generation; for (p = tree->avl_root; p != NULL; p = q) { int cmp = tree->avl_compare (item, p->avl_data, tree->avl_param); if (cmp < 0) q = p->avl_link[0]; else if (cmp > 0) q = p->avl_link[1]; else /* |cmp == 0| */ { trav->avl_node = p; return p->avl_data; } assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = p; } trav->avl_height = 0; trav->avl_node = NULL; return NULL; } /* Attempts to insert |item| into |tree|. If |item| is inserted successfully, it is returned and |trav| is initialized to its location. If a duplicate is found, it is returned and |trav| is initialized to its location. No replacement of the item occurs. If a memory allocation failure occurs, |NULL| is returned and |trav| is initialized to the null item. */ void * avl_t_insert (struct avl_traverser *trav, struct avl_table *tree, void *item) { void **p; assert (trav != NULL && tree != NULL && item != NULL); p = avl_probe (tree, item); if (p != NULL) { trav->avl_table = tree; trav->avl_node = ((struct avl_node *) ((char *) p - offsetof (struct avl_node, avl_data))); trav->avl_generation = tree->avl_generation - 1; return *p; } else { avl_t_init (trav, tree); return NULL; } } /* Initializes |trav| to have the same current node as |src|. */ void * avl_t_copy (struct avl_traverser *trav, const struct avl_traverser *src) { assert (trav != NULL && src != NULL); if (trav != src) { trav->avl_table = src->avl_table; trav->avl_node = src->avl_node; trav->avl_generation = src->avl_generation; if (trav->avl_generation == trav->avl_table->avl_generation) { trav->avl_height = src->avl_height; memcpy (trav->avl_stack, (const void *) src->avl_stack, sizeof *trav->avl_stack * trav->avl_height); } } return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Returns the next data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ void * avl_t_next (struct avl_traverser *trav) { struct avl_node *x; assert (trav != NULL); if (trav->avl_generation != trav->avl_table->avl_generation) trav_refresh (trav); x = trav->avl_node; if (x == NULL) { return avl_t_first (trav, trav->avl_table); } else if (x->avl_link[1] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; while (x->avl_link[0] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; } } else { struct avl_node *y; do { if (trav->avl_height == 0) { trav->avl_node = NULL; return NULL; } y = x; x = trav->avl_stack[--trav->avl_height]; } while (y == x->avl_link[1]); } trav->avl_node = x; return x->avl_data; } /* Returns the previous data item in inorder within the tree being traversed with |trav|, or if there are no more data items returns |NULL|. */ void * avl_t_prev (struct avl_traverser *trav) { struct avl_node *x; assert (trav != NULL); if (trav->avl_generation != trav->avl_table->avl_generation) trav_refresh (trav); x = trav->avl_node; if (x == NULL) { return avl_t_last (trav, trav->avl_table); } else if (x->avl_link[0] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[0]; while (x->avl_link[1] != NULL) { assert (trav->avl_height < AVL_MAX_HEIGHT); trav->avl_stack[trav->avl_height++] = x; x = x->avl_link[1]; } } else { struct avl_node *y; do { if (trav->avl_height == 0) { trav->avl_node = NULL; return NULL; } y = x; x = trav->avl_stack[--trav->avl_height]; } while (y == x->avl_link[0]); } trav->avl_node = x; return x->avl_data; } /* Returns |trav|'s current item. */ void * avl_t_cur (struct avl_traverser *trav) { assert (trav != NULL); return trav->avl_node != NULL ? trav->avl_node->avl_data : NULL; } /* Replaces the current item in |trav| by |new| and returns the item replaced. |trav| must not have the null item selected. The new item must not upset the ordering of the tree. */ void * avl_t_replace (struct avl_traverser *trav, void *new) { void *old; assert (trav != NULL && trav->avl_node != NULL && new != NULL); old = trav->avl_node->avl_data; trav->avl_node->avl_data = new; return old; } /* Destroys |new| with |avl_destroy (new, destroy)|, first setting right links of nodes in |stack| within |new| to null pointers to avoid touching uninitialized data. */ static void copy_error_recovery (struct avl_node **stack, int height, struct avl_table *new, avl_item_func *destroy) { assert (stack != NULL && height >= 0 && new != NULL); for (; height > 2; height -= 2) stack[height - 1]->avl_link[1] = NULL; avl_destroy (new, destroy); } /* Copies |org| to a newly created tree, which is returned. If |copy != NULL|, each data item in |org| is first passed to |copy|, and the return values are inserted into the tree, with |NULL| return values taken as indications of failure. On failure, destroys the partially created new tree, applying |destroy|, if non-null, to each item in the new tree so far, and returns |NULL|. If |allocator != NULL|, it is used for allocation in the new tree. Otherwise, the same allocator used for |org| is used. */ struct avl_table * avl_copy (const struct avl_table *org, avl_copy_func *copy, avl_item_func *destroy, struct libavl_allocator *allocator) { struct avl_node *stack[2 * (AVL_MAX_HEIGHT + 1)]; int height = 0; struct avl_table *new; const struct avl_node *x; struct avl_node *y; assert (org != NULL); new = avl_create (org->avl_compare, org->avl_param, allocator != NULL ? allocator : org->avl_alloc); if (new == NULL) return NULL; new->avl_count = org->avl_count; if (new->avl_count == 0) return new; x = (const struct avl_node *) &org->avl_root; y = (struct avl_node *) &new->avl_root; for (;;) { while (x->avl_link[0] != NULL) { assert (height < 2 * (AVL_MAX_HEIGHT + 1)); y->avl_link[0] = new->avl_alloc->libavl_malloc (new->avl_alloc, sizeof *y->avl_link[0]); if (y->avl_link[0] == NULL) { if (y != (struct avl_node *) &new->avl_root) { y->avl_data = NULL; y->avl_link[1] = NULL; } copy_error_recovery (stack, height, new, destroy); return NULL; } stack[height++] = (struct avl_node *) x; stack[height++] = y; x = x->avl_link[0]; y = y->avl_link[0]; } y->avl_link[0] = NULL; for (;;) { y->avl_balance = x->avl_balance; if (copy == NULL) y->avl_data = x->avl_data; else { y->avl_data = copy (x->avl_data, org->avl_param); if (y->avl_data == NULL) { y->avl_link[1] = NULL; copy_error_recovery (stack, height, new, destroy); return NULL; } } if (x->avl_link[1] != NULL) { y->avl_link[1] = new->avl_alloc->libavl_malloc (new->avl_alloc, sizeof *y->avl_link[1]); if (y->avl_link[1] == NULL) { copy_error_recovery (stack, height, new, destroy); return NULL; } x = x->avl_link[1]; y = y->avl_link[1]; break; } else y->avl_link[1] = NULL; if (height <= 2) return new; y = stack[--height]; x = stack[--height]; } } } /* Frees storage allocated for |tree|. If |destroy != NULL|, applies it to each data item in inorder. */ void avl_destroy (struct avl_table *tree, avl_item_func *destroy) { struct avl_node *p, *q; assert (tree != NULL); for (p = tree->avl_root; p != NULL; p = q) if (p->avl_link[0] == NULL) { q = p->avl_link[1]; if (destroy != NULL && p->avl_data != NULL) destroy (p->avl_data, tree->avl_param); tree->avl_alloc->libavl_free (tree->avl_alloc, p); } else { q = p->avl_link[0]; p->avl_link[0] = q->avl_link[1]; q->avl_link[1] = p; } tree->avl_alloc->libavl_free (tree->avl_alloc, tree); } /* Allocates |size| bytes of space using |malloc()|. Returns a null pointer if allocation fails. */ void * avl_malloc (struct libavl_allocator *allocator, size_t size) { assert (allocator != NULL && size > 0); return malloc (size); } /* Frees |block|. */ void avl_free (struct libavl_allocator *allocator, void *block) { assert (allocator != NULL && block != NULL); free (block); } /* Default memory allocator that uses |malloc()| and |free()|. */ struct libavl_allocator avl_allocator_default = { avl_malloc, avl_free }; #undef NDEBUG #include /* Asserts that |avl_insert()| succeeds at inserting |item| into |table|. */ void (avl_assert_insert) (struct avl_table *table, void *item) { void **p = avl_probe (table, item); assert (p != NULL && *p == item); } /* Asserts that |avl_delete()| really removes |item| from |table|, and returns the removed item. */ void * (avl_assert_delete) (struct avl_table *table, void *item) { void *p = avl_delete (table, item); assert (p != NULL); return p; } logtop-logtop-0.7/src/libavl/avl.h000066400000000000000000000105541341070440700171660ustar00rootroot00000000000000/* Produced by texiweb from libavl.w. */ /* libavl - library for manipulation of binary trees. Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The author may be contacted at on the Internet, or write to Ben Pfaff, Stanford University, Computer Science Dept., 353 Serra Mall, Stanford CA 94305, USA. */ #ifndef AVL_H #define AVL_H 1 #include /* Function types. */ typedef int avl_comparison_func (const void *avl_a, const void *avl_b, void *avl_param); typedef void avl_item_func (void *avl_item, void *avl_param); typedef void *avl_copy_func (void *avl_item, void *avl_param); #ifndef LIBAVL_ALLOCATOR #define LIBAVL_ALLOCATOR /* Memory allocator. */ struct libavl_allocator { void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size); void (*libavl_free) (struct libavl_allocator *, void *libavl_block); }; #endif /* Default memory allocator. */ extern struct libavl_allocator avl_allocator_default; void *avl_malloc (struct libavl_allocator *, size_t); void avl_free (struct libavl_allocator *, void *); /* Maximum AVL height. */ #ifndef AVL_MAX_HEIGHT #define AVL_MAX_HEIGHT 32 #endif /* Tree data structure. */ struct avl_table { struct avl_node *avl_root; /* Tree's root. */ avl_comparison_func *avl_compare; /* Comparison function. */ void *avl_param; /* Extra argument to |avl_compare|. */ struct libavl_allocator *avl_alloc; /* Memory allocator. */ size_t avl_count; /* Number of items in tree. */ unsigned long avl_generation; /* Generation number. */ }; /* An AVL tree node. */ struct avl_node { struct avl_node *avl_link[2]; /* Subtrees. */ void *avl_data; /* Pointer to data. */ signed char avl_balance; /* Balance factor. */ }; /* AVL traverser structure. */ struct avl_traverser { struct avl_table *avl_table; /* Tree being traversed. */ struct avl_node *avl_node; /* Current node in tree. */ struct avl_node *avl_stack[AVL_MAX_HEIGHT]; /* All the nodes above |avl_node|. */ size_t avl_height; /* Number of nodes in |avl_parent|. */ unsigned long avl_generation; /* Generation number. */ }; /* Table functions. */ struct avl_table *avl_create (avl_comparison_func *, void *, struct libavl_allocator *); struct avl_table *avl_copy (const struct avl_table *, avl_copy_func *, avl_item_func *, struct libavl_allocator *); void avl_destroy (struct avl_table *, avl_item_func *); void **avl_probe (struct avl_table *, void *); void *avl_insert (struct avl_table *, void *); void *avl_replace (struct avl_table *, void *); void *avl_delete (struct avl_table *, const void *); void *avl_find (const struct avl_table *, const void *); void avl_assert_insert (struct avl_table *, void *); void *avl_assert_delete (struct avl_table *, void *); #define avl_count(table) ((size_t) (table)->avl_count) /* Table traverser functions. */ void avl_t_init (struct avl_traverser *, struct avl_table *); void *avl_t_first (struct avl_traverser *, struct avl_table *); void *avl_t_last (struct avl_traverser *, struct avl_table *); void *avl_t_find (struct avl_traverser *, struct avl_table *, void *); void *avl_t_insert (struct avl_traverser *, struct avl_table *, void *); void *avl_t_copy (struct avl_traverser *, const struct avl_traverser *); void *avl_t_next (struct avl_traverser *); void *avl_t_prev (struct avl_traverser *); void *avl_t_cur (struct avl_traverser *); void *avl_t_replace (struct avl_traverser *, void *); #endif /* avl.h */ logtop-logtop-0.7/src/logtop.c000066400000000000000000000065311341070440700164320ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "logtop.h" struct logtop *new_logtop(size_t history_size) { struct logtop *this; this = malloc(sizeof(*this)); if (this == NULL) return NULL; this->history_start = 0; this->history_size = history_size; this->strings = NULL; this->history = new_history(this); this->top = new_avl(this); return this; } void delete_logtop(struct logtop *this) { delete_history(this); delete_avl(this); free(this); } void logtop_feed(struct logtop *this, char *string) { log_line_t *element; element = avl_get(this, string); avl_increment(this, element); history_update(this, element); } static void logtop_get_fill(log_line_t *line, int index, struct logtop_state *state) { state->lines[index - 1] = line; if (state->timespan != 0) line->frequency = line->count / state->timespan; else line->frequency = HUGE_VAL; } struct logtop_state *logtop_get(struct logtop *this, size_t qte) { struct logtop_state *state; state = malloc(sizeof(*state)); if (state == NULL) goto fail_state; state->lines = calloc(qte + 1, sizeof(*state->lines)); if (state->lines == NULL) goto fail_lines; state->timespan = logtop_timespan(this); state->count = history_length(this); state->frequency = (state->timespan != 0) ? state->count / state->timespan : HUGE_VAL; avl_traverse(this, qte, (void (*)(void*, int, void*))logtop_get_fill, state); return state; fail_lines: free(state); fail_state: return NULL; } void delete_logtop_state(struct logtop_state *this) { free(this->lines); free(this); } double logtop_timespan(struct logtop *this) { history_element_t *oldest_element; oldest_element = history_oldest_element(this); if (oldest_element != NULL) return difftime(time(NULL), oldest_element->time); else return 0; } unsigned int logtop_qte_of_elements(struct logtop *this) { return history_length(this); } logtop-logtop-0.7/src/logtop.h000066400000000000000000000063211341070440700164340ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FREQUENCY_H__ #define __FREQUENCY_H__ #include #include #include "libavl/avl.h" #include "logtop.h" typedef struct s_log_line { char *string; char *repr; unsigned int count; double frequency; /* Not live, updated lazily in logto_get */ UT_hash_handle hh; } log_line_t; struct logtop_state { double timespan; unsigned int count; double frequency; log_line_t **lines; }; typedef struct s_history_element { log_line_t *log_entry; time_t time; } history_element_t; struct logtop { log_line_t *strings; struct avl_table *top; history_element_t *history; unsigned int history_start; unsigned int history_size; }; history_element_t *new_history(struct logtop *this); void delete_history(struct logtop *this); history_element_t *history_oldest_element(struct logtop *this); history_element_t *history_newest_element(struct logtop *this); unsigned int history_length(struct logtop *this); void history_update(struct logtop *this, log_line_t *element); struct avl_table *new_avl(struct logtop *this); void delete_avl(struct logtop *this); log_line_t *avl_get(struct logtop *this, char *); void avl_increment(struct logtop *this, log_line_t *); void avl_decrement(struct logtop *this, log_line_t *); void avl_traverse(struct logtop *this, unsigned int length, void (*visitor)(void *data, int index, void *user_data), void *user_data); struct logtop *new_logtop(size_t history_size); void delete_logtop(struct logtop *this); void logtop_feed(struct logtop *this, char *line); struct logtop_state *logtop_get(struct logtop *this, size_t qte); void delete_logtop_state(struct logtop_state *this); double logtop_timespan(struct logtop *this); unsigned int logtop_qte_of_elements(struct logtop *this); #endif logtop-logtop-0.7/src/main.c000066400000000000000000000143651341070440700160560ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "main.h" env_t gl_env; /** * Basic sig handling using sigaction. * Reset action to SIG_DFL if act is NULL. */ void setup_sighandler(int signum, int flags, void (*act)(int)) { struct sigaction sa; sigemptyset(&sa.sa_mask); if (act != NULL) sa.sa_handler = act; else sa.sa_handler = SIG_DFL; sa.sa_flags = flags; sigaction(signum, &sa, NULL); } static void update_display(int sig __attribute__((unused))) { time_t current_time; if (gl_env.quiet) return ; current_time = time(NULL); if (current_time < gl_env.last_update_time + gl_env.interval) return ; gl_env.last_update_time = current_time; if (gl_env.line_by_line) stdout_update(gl_env.line_by_line, 1); else curses_update(); alarm(1); } static void run(struct logtop *logtop) { char *string; size_t size; ssize_t str_length; string = NULL; size = 0; while ((str_length = getline(&string, &size, stdin)) != -1) { while (str_length > 0 && (string[str_length - 1] == '\n' || string[str_length - 1] == '\r')) { string[str_length - 1] = '\0'; str_length -= 1; } logtop_feed(logtop, string); } if (string != NULL) free(string); } static void usage_and_exit(int exit_code) { fprintf(exit_code == EXIT_SUCCESS ? stdout : stderr, "Usage: tail -f something | logtop [OPTIONS]\n" " -s, --size=NUM Number of log line to keep in memory\n" " Defaults to : " STRINGIFY(DEFAULT_HISTORY_SIZE) "\n" " -q, --quiet Quiet, only display a top 10 at exit.\n" " -l, --line-by-line=NUM Print result line by line\n" " in a machine friendly format,\n" " NUM: quantity of result by line.\n"); fprintf(exit_code == EXIT_SUCCESS ? stdout : stderr, " -i, --interval=NUM Interval between graphical updates,\n" " in seconds. Defaults to 1.\n" "\n" " Line by line format is : [%%d %%f %%s\\t]*\\n\n" " %%d : Number of occurences\n" " %%f : Frequency of apparition\n" " %%s : String (Control chars replaced by dots).\n" "\n"); exit(exit_code); } static void version_and_exit(void) { #define stringify(v) #v #define concat_version(v) "logtop v" stringify(v) "\n" fprintf(stdout, concat_version(VERSION)); exit(EXIT_SUCCESS); } static void parse_args(int ac, char **av) { int c; gl_env.history_size = 0; gl_env.quiet = 0; gl_env.last_update_time = 0; gl_env.line_by_line = 0; gl_env.interval = 1; while (1) { int option_index = 0; static struct option long_options[] = { {"size", 1, 0, 's'}, {"quiet", 0, 0, 'q'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"line-by-line", 1, 0, 'l'}, {"interval", 1, 0, 'i'}, {0, 0, 0, 0} }; c = getopt_long(ac, av, "qhvl:s:i:", long_options, &option_index); if (c == -1) break; switch (c) { case 'l': gl_env.line_by_line = atoi(optarg); break; case 'q': gl_env.quiet = 1; break; case 's': gl_env.history_size = atoi(optarg); break; case 'i': gl_env.interval = atoi(optarg); break; case 'v': version_and_exit(); case 'h': usage_and_exit(EXIT_SUCCESS); default: usage_and_exit(EXIT_FAILURE); } } if (isatty(fileno(stdin))) usage_and_exit(EXIT_FAILURE); if (gl_env.history_size == 0) gl_env.history_size = DEFAULT_HISTORY_SIZE; } static void at_exit(void) { if (!gl_env.quiet && !gl_env.line_by_line) curses_release(); if (gl_env.line_by_line) stdout_update(gl_env.line_by_line, 1); else stdout_update(10, 0); delete_logtop(gl_env.logtop); fflush(NULL); } static void on_sigint(int sig) { setup_sighandler(SIGINT, 0, NULL); at_exit(); kill(getpid(), sig); } int main(int ac, char **av) { setlocale(LC_ALL, ""); parse_args(ac, av); setup_sighandler(SIGINT, 0, on_sigint); setup_sighandler(SIGALRM, SA_RESTART, update_display); alarm(1); gl_env.last_update_time = time(NULL); gl_env.logtop = new_logtop(gl_env.history_size); if (!gl_env.quiet && !gl_env.line_by_line) curses_setup(); run(gl_env.logtop); at_exit(); return EXIT_SUCCESS; } logtop-logtop-0.7/src/main.h000066400000000000000000000041211341070440700160500ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __LOGTOP_H__ #define __LOGTOP_H__ #include #include "logtop.h" #ifndef STRINGIFY # define __LOGTOP_STRINGIFY(x) #x # define STRINGIFY(x) __LOGTOP_STRINGIFY(x) #endif #define DEFAULT_HISTORY_SIZE 10000 typedef struct s_env { int quiet; int line_by_line; unsigned int history_size; unsigned int display_width; unsigned int display_height; time_t last_update_time; time_t interval; struct logtop *logtop; } env_t; extern env_t gl_env; void curses_setup(void); void curses_release(void); void curses_update(void); void stdout_update(int nb_lines, int line_by_line); void setup_sighandler(int signum, int flags, void (*act)(int)); #endif logtop-logtop-0.7/src/stdout.c000066400000000000000000000062741341070440700164540ustar00rootroot00000000000000/* * Copyright (c) 2010 Julien Palard. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "main.h" struct display_data { double duration; unsigned int qte_of_elements; }; typedef void (*result_printer)(void *data, int index, void *metadata); static void display_line(void *data, int index, void *metadata) { log_line_t *line; line = (log_line_t *)data; printf("%4d %6d %8.2f %-*s\n", index, line->count, line->count / ((struct display_data *)metadata)->duration, gl_env.display_width - 21, line->repr); } static void display_result(void *data, int index __attribute__((unused)), void *metadata) { log_line_t *line; line = (log_line_t *)data; printf("%d %.2f %s\t", line->count, line->count / ((struct display_data *)metadata)->duration, line->repr); } static void display_header(struct display_data *display_data) { printf("%d lines, %.2f lines/s\n", display_data->qte_of_elements, display_data->qte_of_elements / (double)display_data->duration); printf("%s\n", "RANK CNT LINE/S LINE"); } static const result_printer printers[2] = {display_line, display_result}; void stdout_update(int nb_results, int line_by_line) { struct display_data display_data; display_data.duration = logtop_timespan(gl_env.logtop); display_data.qte_of_elements = logtop_qte_of_elements(gl_env.logtop); if (display_data.duration == 0) display_data.duration = 1; if (line_by_line) { avl_traverse(gl_env.logtop, nb_results, display_result, &display_data); printf("\n"); } else { display_header(&display_data); avl_traverse(gl_env.logtop, nb_results, display_line, &display_data); } } logtop-logtop-0.7/tests/000077500000000000000000000000001341070440700153305ustar00rootroot00000000000000logtop-logtop-0.7/tests/test.py000077500000000000000000000025231341070440700166660ustar00rootroot00000000000000#!/usr/bin/env python import sys import os.path as path sys.path.append(path.realpath(__file__ + '/../../')) from threading import Thread, Lock import time from logtop import logtop # use make python-module to build it def build_lines(): while True: for i in range(10): yield str(i % 3) time.sleep(1) time.sleep(10) class LogtopPrinter(Thread): def __init__(self): self.lock = Lock() self.logtop = logtop(10000) Thread.__init__(self) self.daemon = True def run(self): while True: self.lock.acquire() data = self.logtop.get(6) count = self.logtop.qte_of_elements() timespan = self.logtop.timespan() self.lock.release() if timespan == 0: timespan = 1 print "%.2f l/s (%d lines, %.2f seconds): %s" % (count / timespan, count, timespan, repr(data)) time.sleep(1) def feed(self, line): self.lock.acquire() self.logtop.feed(line) self.lock.release() printer = LogtopPrinter() printer.start() for line in build_lines(): printer.feed(line)