-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# newcat | ||
|
||
print (cat) only new lines in files (after previous run of newcat). This is useful to parse recent log records. | ||
|
||
## Install | ||
~~~ | ||
pipx install newcat | ||
~~~ | ||
|
||
## Example | ||
~~~bash | ||
$ echo This is line 1 > /tmp/test.log | ||
|
||
# works like normal /bin/cat now | ||
$ newcat /tmp/test.log | ||
This is line 1 | ||
|
||
# only new lines are printed | ||
$ echo This is line 2 >> /tmp/test.log | ||
$ newcat /tmp/test.log | ||
This is line 2 | ||
|
||
# no new lines => nothing on stderr | ||
$ newcat /tmp/test.log | ||
|
||
# despite file is having two lines | ||
$ cat /tmp/test.log | ||
This is line 1 | ||
This is line 2 | ||
~~~ | ||
|
||
## How does this magic work? | ||
newcat stores some info about file (including position in file) in *state file*. Default state file is `/tmp/.newcat.state`, but can be overriden with `NEWCAT_STATE` environment variable or `-s` / `--state` option. Normally, newcat prints file content starting from this position (which was saved before), thus lines which were already printed before, will not be printed on new newcat run. | ||
|
||
Sometime file content it rewritten (e.g. after `echo New content > /tmp/test.log`). To detect this situation newcat checks two things: | ||
- file inode changed | ||
- file size is less then old printed position | ||
|
||
if any of these conditions are met, file considered as rewritten and newcat prints it from the beginning. | ||
Note: this is not absolutely reliable, and there is a chance that sometimes newcat will not detect when file is overwritten. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#!/usr/bin/env python | ||
|
||
import argparse | ||
import json | ||
import os | ||
import sys | ||
import shutil | ||
|
||
__version__ = '0.0.1' | ||
|
||
state = dict() | ||
|
||
def get_args(): | ||
def_state = os.getenv('NEWCAT_STATE', '/tmp/.newcat.state') | ||
parser = argparse.ArgumentParser(description=f'newcat version {__version__}. print / cat only new lines in log files') | ||
parser.add_argument('-s','--state',metavar='PATH', default=def_state, help=f'path to state file. default: {def_state}') | ||
parser.add_argument('files', metavar='LOGFILE', nargs='+', help='log file(s) to read') | ||
return parser.parse_args() | ||
|
||
def cat(path: str, filestate: dict) -> None: | ||
with open(path ,"r") as fh: | ||
|
||
if not file_rewritten(path, filestate): | ||
fh.seek(filestate['pos']) | ||
|
||
shutil.copyfileobj(fsrc=fh, fdst=sys.stdout) | ||
filestate['pos'] = fh.tell() | ||
|
||
def create_state(path): | ||
s = dict() | ||
stat = os.stat(path) | ||
s['pos'] = 0 | ||
s['st_ino'] = stat.st_ino | ||
return s | ||
|
||
def file_rewritten(path: str, oldstate: dict) -> bool: | ||
|
||
stat = os.stat(path) | ||
|
||
if oldstate['st_ino'] != stat.st_ino: | ||
return True | ||
|
||
if oldstate['pos'] > stat.st_size: | ||
return True | ||
|
||
return False | ||
|
||
def main(): | ||
global state | ||
args=get_args() | ||
|
||
try: | ||
with open(args.state) as fh: | ||
state = json.load(fh) | ||
except FileNotFoundError: | ||
state = dict() | ||
for file in args.files: | ||
file = os.path.realpath(file) | ||
|
||
if file not in state or file_rewritten(file, state[file]): | ||
state[file] = create_state(file) | ||
|
||
cat(file, state[file]) | ||
|
||
|
||
with open(args.state, "w") as fh: | ||
json.dump(state, fh, indent=4, sort_keys=True) | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from setuptools import setup | ||
import os | ||
import sys | ||
|
||
from importlib.machinery import SourceFileLoader | ||
|
||
def read(fname): | ||
return open(os.path.join(os.path.dirname(__file__), fname)).read() | ||
|
||
def get_version(path): | ||
foo = SourceFileLoader(os.path.basename(path), path).load_module() | ||
return foo.__version__ | ||
|
||
setup( | ||
name='newcat', | ||
version=get_version('newcat'), | ||
packages=[], | ||
scripts=['newcat'], | ||
|
||
install_requires=[], | ||
|
||
url='https://github.com/yaroslaff/newcat', | ||
license='MIT', | ||
author='Yaroslav Polyakov', | ||
long_description=read('README.md'), | ||
long_description_content_type='text/markdown', | ||
author_email='[email protected]', | ||
description='print / cat only new lines in log files', | ||
python_requires='>=3.6', | ||
classifiers=[ | ||
'Development Status :: 3 - Alpha', | ||
|
||
# Indicate who your project is intended for | ||
'Intended Audience :: Developers', | ||
|
||
# Pick your license as you wish (should match "license" above) | ||
'License :: OSI Approved :: MIT License', | ||
|
||
# Specify the Python versions you support here. In particular, ensure | ||
# that you indicate whether you support Python 2, Python 3 or both. | ||
'Programming Language :: Python :: 3.6', | ||
], | ||
) |