├── .gitignore ├── CLog.py ├── README.md └── example.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | test.log -------------------------------------------------------------------------------- /CLog.py: -------------------------------------------------------------------------------- 1 | __author__ = 'baniu.yao' 2 | 3 | 4 | import inspect 5 | import logging 6 | import re 7 | 8 | 9 | def get_class_from_frame(fr): 10 | args, _, _, value_dict = inspect.getargvalues(fr) 11 | if len(args) and args[0] == 'self': 12 | instance = value_dict.get('self', None) 13 | if instance: 14 | return getattr(instance, '__class__', None) 15 | return None 16 | 17 | 18 | class CLog(object): 19 | def __init__(self, log_file_path='./test.log'): 20 | logging.basicConfig(filename=log_file_path, 21 | level=logging.DEBUG, filemode='aw', 22 | format='%(asctime)s [%(chain)s] %(message)s') 23 | 24 | def get_file_name_in_full_path(self, file_path): 25 | return file_path.split('/')[-1] 26 | 27 | def get_meta_data(self): 28 | frames = inspect.stack() 29 | chain_list = [] 30 | for i in range(0, len(frames)): 31 | _, file_path, _, func_name, _, _ = frames[i] 32 | file_name = self.get_file_name_in_full_path(file_path) 33 | try: 34 | args = re.findall('\((.*)\)', frames[i+1][-2][0])[0] 35 | except IndexError, e: 36 | func_name = get_class_from_frame(frames[2][0]).__name__ 37 | args = '' 38 | current_chain = '%s:%s(%s)' % (file_name, func_name, args) 39 | chain_list.append(current_chain) 40 | chain_list.reverse() 41 | return ' --> '.join(chain_list[:-2]) 42 | 43 | def write(self, message): 44 | chain = self.get_meta_data() 45 | logging.info(message, extra={'chain': chain}) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Python-CLog 2 | ================ 3 | 4 | When I print log in python, what made me confused is that 'What this line of log comes from'. I think this is the circumstance almost every pythoner has encountered. If there is a way of logging, which can print the full stack of call-chain, it must be great. For this purpose, I write this small but userful module for python. I call this CLog which means Chain-Log. 5 | 6 | What CLog do? 7 | ---------------- 8 | 9 | When you use CLog to print log, you can get the caller chain where you print log, such as 'Foo() --> foo1(name)'. How Clog does that? I use inspect module contained in Python 2.7 to get the complete frame in python run-time environment. A frame, briefly explained, is a collection of some piece of code. The details of frame are not going to be introduced here and you can find those in some articles describes the internal mechanism of Python. 10 | 11 | How to use CLog 12 | ---------------- 13 | 14 | Since CLog is closely attached in Python internals, you just need to import this module and initialize it with a file name. And while you would like to print log, just use a 'write' method of instance. Let us see example.py. 15 | 16 | 17 | class FooClass(object): 18 | 19 | def foo(self, text): 20 | log = CLog() 21 | log.write('hello world') 22 | 23 | if __name__ == '__main__': 24 | fc = FooClass() 25 | fc.foo('def') 26 | 27 | While you run python example.py, you will get 'test.log' in your PWD and its content is like: 28 | 2014-03-30 19:09:35,582 [example.py:FooClass() --> example.py:foo('def')] hello world 29 | 30 | It is really cool. In log file, there is file name, class name, function name and its parameters. Every body is clearly where this log comes from and I think this will help pythoner a lot. 31 | 32 | Why only 'write' method? 33 | ---------------------- 34 | 35 | There is only one method for writng logs, and no log level concept is available in CLog. Because I think it is not neccessary for this. A 'write' method is enough. 36 | 37 | TODO 38 | ---------------------- 39 | 40 | Add option to use sysout for output logs. Currently, CLog only supports print logs to file. 41 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | __author__ = 'baniu.yao' 2 | 3 | from CLog import CLog 4 | 5 | class FooClass(object): 6 | 7 | def foo(self, text): 8 | log = CLog() 9 | log.write('hello world') 10 | 11 | if __name__ == '__main__': 12 | fc = FooClass() 13 | fc.foo('def') 14 | --------------------------------------------------------------------------------