├── arts ├── demo.png ├── hide-msg-demo.png ├── highlight-demo.png ├── pycharm-build.jpg ├── separate-demo.png ├── trans-msg-demo.png └── trans-tag-demo.png ├── debug.sh ├── MANIFEST.in ├── .gitignore ├── .idea ├── vcs.xml ├── modules.xml ├── inspectionProfiles │ └── Project_Default.xml └── dictionaries │ └── Jacksgong.xml ├── demo-conf ├── demo-config.yml ├── filedownloader.yml └── demo-log-file.log ├── .travis.yml ├── okcat ├── logseparator.py ├── helper.py ├── trans.py ├── terminalcolor.py ├── logregex.py ├── logfile_parser.py ├── confloader.py ├── __init__.py ├── logprocessor.py └── adb.py ├── CHANGELOG.md ├── setup.py ├── README-zh.md ├── README.md └── LICENSE.txt /arts/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/demo.png -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pip uninstall okcat --yes 3 | python3 setup.py install 4 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Include the license file 2 | include LICENSE.txt 3 | include README.md 4 | -------------------------------------------------------------------------------- /arts/hide-msg-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/hide-msg-demo.png -------------------------------------------------------------------------------- /arts/highlight-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/highlight-demo.png -------------------------------------------------------------------------------- /arts/pycharm-build.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/pycharm-build.jpg -------------------------------------------------------------------------------- /arts/separate-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/separate-demo.png -------------------------------------------------------------------------------- /arts/trans-msg-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/trans-msg-demo.png -------------------------------------------------------------------------------- /arts/trans-tag-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jacksgong/okcat/HEAD/arts/trans-tag-demo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/workspace.xml 2 | /.idea/misc.xml 3 | /.idea/okcat.iml 4 | /.idea/modules.xml 5 | *.pyc 6 | *.egg-info 7 | /dist 8 | /build 9 | .DS_Store -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /demo-conf/demo-config.yml: -------------------------------------------------------------------------------- 1 | log-line-regex: 'date,time,process,thread,level,tag,message = "(\S*) *(\S*) *(\d*) *(\d*) ([A-Z]) ([^:]*): (.*?)$"' 2 | 3 | trans-msg-map: 4 | 'Unknown': '未知' 5 | 6 | trans-tag-map: 7 | 'SystemServiceManager': '[System Service Manager]' 8 | 'RescueParty': '[恢复部分]' 9 | 10 | highlight-list: 11 | - 'complete' 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: python 3 | python: 4 | - "2.7" 5 | - "3.3" 6 | - "3.4" 7 | - "3.5" 8 | - "3.6" 9 | - "3.7" 10 | - "3.8" 11 | - "3.9" 12 | 13 | install: 14 | # - travis_retry pip install okcat 15 | - bash debug.sh 16 | script: 17 | - okcat help 18 | - cd demo-conf 19 | - okcat -y=demo-config demo-log-file.log 20 | -------------------------------------------------------------------------------- /.idea/dictionaries/Jacksgong.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dalvik 5 | dest 6 | dreamtobe 7 | ducktype 8 | dumpsys 9 | gids 10 | jacksgong 11 | jakewharton 12 | logcat 13 | okcat 14 | pids 15 | popen 16 | proc 17 | prog 18 | sharkey 19 | stdin 20 | vdiwef 21 | 22 | 23 | -------------------------------------------------------------------------------- /okcat/logseparator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | import re 19 | 20 | __author__ = 'JacksGong' 21 | 22 | 23 | class LogSeparator: 24 | separator_rex_list = list() 25 | pre_separate_key = None 26 | 27 | def __init__(self, separator_rex_list): 28 | for regex_string in separator_rex_list: 29 | self.separator_rex_list.append(re.compile(r'%s' % regex_string)) 30 | 31 | def process(self, msg): 32 | key = None 33 | for regex in self.separator_rex_list: 34 | matched_obj = regex.match(msg) 35 | if matched_obj is not None: 36 | key = matched_obj.groups()[0] 37 | break 38 | 39 | if self.pre_separate_key is None: 40 | if key is None: 41 | key = "unknown" 42 | self.pre_separate_key = key 43 | return key 44 | elif key is not None and self.pre_separate_key != key: 45 | return key 46 | else: 47 | return None 48 | -------------------------------------------------------------------------------- /okcat/helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | import re 19 | import sys 20 | 21 | from os import environ, getcwd 22 | 23 | from os.path import exists 24 | 25 | __author__ = 'JacksGong' 26 | 27 | LOG_LEVELS = 'VDIWEF' 28 | LOG_LEVELS_MAP = dict([(LOG_LEVELS[i], i) for i in range(len(LOG_LEVELS))]) 29 | 30 | NO_HOME_PATH = re.compile(r'~/(.*)') 31 | HOME_PATH = environ['HOME'] 32 | 33 | 34 | # get the home case path 35 | def handle_home_case(path): 36 | path = path.strip() 37 | if path.startswith('~/'): 38 | path = HOME_PATH + '/' + NO_HOME_PATH.match(path).groups()[0] 39 | return path 40 | 41 | 42 | def is_path(path): 43 | if path.startswith('/') or path.startswith('~/') or path.startswith('./'): 44 | return True 45 | 46 | if exists(path): 47 | return True 48 | return False 49 | 50 | 51 | def get_conf_path(conf_name): 52 | if not conf_name.endswith('.yml'): 53 | conf_name = conf_name + '.yml' 54 | 55 | cur_path_yml = '%s/%s' % (getcwd(), conf_name) 56 | if exists(cur_path_yml): 57 | result = cur_path_yml 58 | else: 59 | result = '~/.okcat/' + conf_name 60 | 61 | print('using config on %s' % result) 62 | return result 63 | 64 | 65 | def print_unicode(line): 66 | if sys.version_info >= (3, 0): 67 | print(bytes.decode(line)) 68 | else: 69 | print(line) 70 | 71 | def line_rstrip(line): 72 | if sys.version_info >= (3, 0): 73 | return line.rstrip() 74 | else: 75 | try: 76 | return line.decode('utf-8').rstrip() 77 | except UnicodeDecodeError: 78 | return line.rstrip() -------------------------------------------------------------------------------- /okcat/trans.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | Copyright (C) 2017 Jacksgong(jacksgong.com) 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | """ 19 | from okcat.terminalcolor import colorize, allocate_color, BLACK 20 | 21 | __author__ = 'JacksGong' 22 | 23 | 24 | class Trans: 25 | trans_msg_map = None 26 | trans_tag_map = None 27 | hide_msg_list = None 28 | 29 | def __init__(self, trans_msg_map, trans_tag_map, hide_msg_list): 30 | self.trans_msg_map = trans_msg_map 31 | self.trans_tag_map = trans_tag_map 32 | self.hide_msg_list = hide_msg_list 33 | 34 | def trans_msg(self, msg): 35 | if self.trans_msg_map is None: 36 | return msg 37 | 38 | for key in self.trans_msg_map: 39 | if msg.startswith(key): 40 | value = self.trans_msg_map[key] 41 | return u'| %s | %s' % (colorize(value, fg=allocate_color(value)), msg) 42 | 43 | return msg 44 | 45 | def trans_tag(self, tag, msg): 46 | if self.trans_tag_map is None or tag is None: 47 | return msg 48 | 49 | for key in self.trans_tag_map: 50 | if key in tag: 51 | prefix = self.trans_tag_map[key] 52 | return u'%s %s' % (colorize(prefix, bg=allocate_color(prefix)), msg) 53 | 54 | return msg 55 | 56 | def hide_msg(self, msg): 57 | if self.hide_msg_list is None: 58 | return msg 59 | 60 | # print("get hide msg list: %s and len(%d)" % (self.hide_msg_list, len(msg))) 61 | # if msg.__len__() > 100: 62 | # return msg 63 | 64 | for gray_msg in self.hide_msg_list: 65 | if msg.startswith(gray_msg): 66 | return colorize(msg, fg=BLACK) 67 | 68 | return msg 69 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.4.0 2 | 3 | 2024-02-27 4 | 5 | - Feature: Support `ignore-tag-list` and `ignore-msg-list` configuration to ignore messages by defined 6 | 7 | # 1.3.0 8 | 9 | 2020-08-13 10 | 11 | - Fix: fix on the special timestamp format stackoverflow issue temporary 12 | - Fix: fix decode utf-8 failed 13 | 14 | # 1.1.9 15 | 16 | 2019-08-23 17 | 18 | - Feature: Implement 'from' keyword from config yaml file means append rather than overwrite. 19 | 20 | # 1.1.7 21 | 22 | 2019-01-11 23 | 24 | - Fix: Fix wrong config field on the `log-line-regex` or `adb-log-line-regex` from 'data' to the correct one 'date' 25 | 26 | # 1.1.6 27 | 28 | 2018-06-26 29 | 30 | - Fix: fix encode issue on python3 - by [Ryfthink](https://github.com/Ryfthink) 31 | 32 | # 1.1.5 33 | 34 | 2018-06-24 35 | 36 | - Fix: some dvices adb lost connection, closes #9 - by [Ryfthink](https://github.com/Ryfthink) 37 | 38 | # 1.1.4 39 | 40 | 2018-05-24 41 | 42 | - Feat: support 'from' keyword to let yml config file extends from exist yml file 43 | 44 | # 1.1.3 45 | 46 | 2017-12-01 47 | 48 | - Feat: support combine and parse multiple log-files once time 49 | 50 | # 1.1.2 51 | 52 | 2017-11-17 53 | 54 | - Fix: fix unicode decode error on setup on windows system closes #4 55 | 56 | # 1.1.1 57 | 58 | 2017-10-10 59 | 60 | - Feat: show tips instead of crash when user don't provide config-file name to parse log file. Closes #2 61 | 62 | # 1.1.0 63 | 64 | 2017-09-27 65 | 66 | - Fix: fix import file failed on python 3.x 67 | 68 | # 1.0.9 69 | 70 | 2017-09-16 71 | 72 | - Fix: missing parentheses in call to 'print' error occurred on python 3.x Closes #1 73 | 74 | # 1.0.8 75 | 76 | 2017-09-04 77 | 78 | - Feat: handle the case of adb connection is lost when using adb logcat 79 | 80 | # 1.0.7 81 | 82 | 2017-09-03 83 | 84 | - Enhance: print each line when it has been parsed immediately rather than waiting for parsing whole file to handle case of large log file 85 | 86 | # 1.0.6 87 | 88 | 2017-09-1 89 | 90 | - Fix: cover the case of there is no 'level' keyword on `log-line-regex` case. 91 | - Enhance: add `help` param on okcat, such as `okcat help`. 92 | - Enhance: support `--hide-same-tags` param 93 | - Fix: output all log when `log-line-regex` can't parse 94 | - Fix: handle case of only message is valid 95 | - Fix: fix print non-match content when the log can't match regex 96 | - Fix: fix the default adb regex may wrong for some special case 97 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | from setuptools import setup, find_packages 19 | 20 | # Get the long description from the README file 21 | # noinspection PyArgumentList 22 | setup( 23 | name="OkCat", 24 | version="1.4.0", 25 | packages=find_packages(exclude=['demo-conf', 'arts']), 26 | 27 | # Project uses reStructuredText, so ensure that the docutils get 28 | # installed or upgraded on the target machine 29 | install_requires=['PyYAML>=3.12'], 30 | 31 | # metadata for upload to PyPI 32 | author="Jacksgong", 33 | author_email="igzhenjie@gmail.com", 34 | description="An powerful log processor", 35 | long_description='More detail please move to https://github.com/Jacksgong/okcat', 36 | license="Apache2", 37 | keywords="okcat log 'log processor' 'log filter'", 38 | url="https://github.com/Jacksgong/okcat", 39 | 40 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 41 | classifiers=[ 42 | # How mature is this project? Common values are 43 | # 3 - Alpha 44 | # 4 - Beta 45 | # 5 - Production/Stable 46 | 'Development Status :: 5 - Production/Stable', 47 | 48 | # Pick your license as you wish (should match "license" above) 49 | 'License :: OSI Approved :: Apache Software License', 50 | 51 | # Specify the Python versions you support here. In particular, ensure 52 | # that you indicate whether you support Python 2, Python 3 or both. 53 | 'Programming Language :: Python :: 2.7', 54 | 'Programming Language :: Python :: 3', 55 | 'Programming Language :: Python :: 3.3', 56 | 'Programming Language :: Python :: 3.4', 57 | 'Programming Language :: Python :: 3.5', 58 | 'Programming Language :: Python :: 3.6', 59 | 'Programming Language :: Python :: 3.7', 60 | 'Programming Language :: Python :: 3.8', 61 | 'Programming Language :: Python :: 3.9', 62 | ], 63 | entry_points={ 64 | 'console_scripts': [ 65 | 'okcat=okcat:main' 66 | ] 67 | } 68 | ) 69 | -------------------------------------------------------------------------------- /demo-conf/filedownloader.yml: -------------------------------------------------------------------------------- 1 | # we will filter out logs with the provided package (name) 2 | # this 'package' keyword is just using for android adb logcat 3 | package: com.liulishuo.filedownloader.demo 4 | 5 | # this 'log-line-regex' is just a regex for one line log 6 | # now we support keyword: 'date' 'time' 'level' 'tag' 'process' 'thread' 'message' 7 | # you don't have to provide all keyword, but you have to provide at least the 'message' 8 | # such as: 'message="(\S*)"' 9 | log-line-regex: 'date,time,level,tag,process,thread,message = "(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 10 | 11 | # on the case of filter logs from Android adb logcat, we using 'adb logcat -v brief -v threadtime' command to obtain logcat 12 | # in the normal case you don't need ot provide this config, because there is a perfect one on the okcat internal 13 | # but if you want to customize the regex log from adb logcat, it's free to define it such below 14 | # adb-log-line-regex: 'date,time,process,thread,level,tag,message="(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 15 | 16 | # separator regex list 17 | # you can provide multiple regex to separate serial logs 18 | separator-regex-list: 19 | # on this case, if one line log match 'call start Url\[([^\]]*)\]' regex we will separate logs with \n and output a indie line with the '([^\]]*)' value as the title of separate 20 | - 'call start Url\[([^\]]*)\]' 21 | 22 | # tag keyword list 23 | # this list keyword is using for filter out which log need to be output 24 | # all provided keyword will be using for compare with each line tag, if a line with tag not contain any keyword on 'tag-keyword-list' it will be ignore to output 25 | tag-keyword-list: 26 | - 'FileDownloader' 27 | 28 | # translate message map 29 | # if a message on a line start with provide keyword on the 'trans-msg-map' we will add the value of the keyword on the start of the message, and the word of value will be colored to highlight it 30 | trans-msg-map: 31 | # such as this case: 32 | # origin message: 'filedownloader:lifecycle:over xxx' 33 | # after translate: '| Task OVER | filedownloader:lifecycle:over xxx' 34 | 'filedownloader:lifecycle:over': 'Task OVER' 35 | 'fetch data with': 'Start Fetch' 36 | 37 | # translate tag map 38 | # if a tag on a line contain provide keyword on the 'trans-tag-map' we will add the value of the keyword on the start of the message, and the background of the value word will be colored to highlight it 39 | trans-tag-map: 40 | # such as this case: 41 | # origin message: 'FileDownloader.DownloadTaskHunter xxx' 42 | # after translate: 'FileDownloader.DownloadTaskHunter [Status Change] xxx' 43 | 'DownloadTaskHunter': '[Status Change]' 44 | 'ConnectTask': '[Request]' 45 | 46 | # hide message list 47 | # if a message on a line start with provide value on the 'hide-msg-list` and the length of the message is less than 100 word, it would be colored with gray to hide 48 | hide-msg-list: 49 | # here we hide message start with 'notify progress' and '~~~callback' because it is too frequently to output and useless in most case 50 | - 'notify progress' 51 | - '~~~callback' 52 | 53 | # highlight list 54 | # if any value on the 'highlight-list' display on any message, the background of the value word would be colored to highlight it 55 | highlight-list: 56 | - 'Path[' 57 | - 'Url[' 58 | - 'Tag[' 59 | - 'range[' 60 | -------------------------------------------------------------------------------- /okcat/terminalcolor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | Copyright (C) 2017 Jacksgong(jacksgong.com) 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | """ 19 | 20 | __author__ = 'jacks.gong' 21 | 22 | 23 | class BashColors: 24 | def __init__(self): 25 | pass 26 | 27 | HEADER = '\033[95m' 28 | BLUE = '\033[94m' 29 | GREEN = '\033[92m' 30 | WARNING = '\033[93m' 31 | FAIL = '\033[91m' 32 | END = '\033[0m' 33 | BOLD = '\033[1m' 34 | UNDERLINE = '\033[4m' 35 | 36 | 37 | def print_header(msg): 38 | print(BashColors.HEADER + msg + BashColors.END) 39 | 40 | 41 | def print_exit(msg): 42 | print(BashColors.FAIL + msg + BashColors.END) 43 | 44 | 45 | def print_error(msg): 46 | print(BashColors.FAIL + msg + BashColors.END) 47 | 48 | 49 | def print_tips(msg): 50 | print(BashColors.UNDERLINE + msg + BashColors.END) 51 | 52 | 53 | def print_warn(msg): 54 | print(BashColors.WARNING + msg + BashColors.END) 55 | 56 | 57 | def print_key(msg): 58 | print(BashColors.GREEN + msg + BashColors.END) 59 | 60 | 61 | def print_blue(msg): 62 | print(BashColors.BLUE + msg + BashColors.END) 63 | 64 | 65 | def print_content_tips(msg): 66 | msg = BashColors.UNDERLINE + msg + BashColors.END 67 | print(msg) 68 | return msg + "\n" 69 | 70 | 71 | def print_content_header(msg): 72 | msg = BashColors.HEADER + msg + BashColors.END 73 | print_content(msg) 74 | return msg + "\n" 75 | 76 | 77 | def print_content(msg): 78 | print(msg) 79 | return msg 80 | 81 | 82 | # -------------- color ----------------------------- 83 | RESET = '\033[0m' 84 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) 85 | 86 | 87 | def termcolor(fg=None, bg=None): 88 | codes = [] 89 | if fg is not None: codes.append('3%d' % fg) 90 | if bg is not None: codes.append('10%d' % bg) 91 | return '\033[%sm' % ';'.join(codes) if codes else '' 92 | 93 | 94 | def colorize(message, fg=None, bg=None): 95 | if fg is None: 96 | if bg == BLACK: 97 | fg = WHITE 98 | else: 99 | fg = BLACK 100 | 101 | return termcolor(fg, bg) + message + RESET 102 | 103 | 104 | TAGTYPES = { 105 | 'V': colorize(' V ', fg=WHITE, bg=BLACK), 106 | 'D': colorize(' D ', fg=BLACK, bg=BLUE), 107 | 'I': colorize(' I ', fg=BLACK, bg=GREEN), 108 | 'W': colorize(' W ', fg=BLACK, bg=YELLOW), 109 | 'E': colorize(' E ', fg=BLACK, bg=RED), 110 | 'F': colorize(' F ', fg=BLACK, bg=RED), 111 | } 112 | 113 | # for random color 114 | KNOWN_TAGS = { 115 | 'dalvikvm': WHITE, 116 | 'Process': WHITE, 117 | 'ActivityManager': WHITE, 118 | 'ActivityThread': WHITE, 119 | 'AndroidRuntime': CYAN, 120 | 'jdwp': WHITE, 121 | 'StrictMode': WHITE, 122 | 'DEBUG': YELLOW, 123 | } 124 | LAST_USED = [RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN] 125 | LOG_FLOW_KEY_LAST_USED = [RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN] 126 | 127 | 128 | def allocate_color(_key, loop_color=LAST_USED): 129 | if _key not in KNOWN_TAGS: 130 | KNOWN_TAGS[_key] = loop_color[0] 131 | 132 | _color = KNOWN_TAGS[_key] 133 | if _color in LAST_USED: 134 | loop_color.remove(_color) 135 | loop_color.append(_color) 136 | return _color 137 | -------------------------------------------------------------------------------- /okcat/logregex.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | 19 | import re 20 | 21 | from okcat.terminalcolor import print_warn 22 | 23 | __author__ = 'jacks.gong' 24 | 25 | # val = 'date,time,process,thread,level,tag,message = "(.\S*) (.\S*) (\d*) (\d*) ([V|I|D|W|E]) ([^:]*): (.*)"' 26 | REGEX_EXP_RE = re.compile(r"([^ =]*) *= *[\"|'](.*)[\"|']") 27 | ALL_SUPPORT_KEY = ["date", "time", "process", "thread", "level", "tag", "message"] 28 | DEPRECATED_DATE_KEY = "data" 29 | 30 | class LogRegex: 31 | key_order = list() 32 | 33 | regex = None 34 | 35 | def __init__(self, regex_exp): 36 | keys, regex = REGEX_EXP_RE.match(regex_exp).groups() 37 | process_key_order = keys.split(',') 38 | 39 | self.regex = re.compile(r"%s" % regex) 40 | for key in process_key_order: 41 | key = key.strip() 42 | if key in ALL_SUPPORT_KEY: 43 | self.key_order.append(key) 44 | elif key == DEPRECATED_DATE_KEY: 45 | print_warn("please change 'data' to 'date' because this wrong word has been fixed on the current version, for the temporary we treat it as 'date'") 46 | self.key_order.append('date') 47 | else: 48 | print_warn("not support key[%s] only support: %s" % (key, ALL_SUPPORT_KEY)) 49 | 50 | print("find regex: " + self.key_order.__str__() + " with " + regex) 51 | 52 | def parse(self, line): 53 | date = None 54 | time = None 55 | process = None 56 | thread = None 57 | level = None 58 | tag = None 59 | message = None 60 | 61 | values = self.regex.match(line) 62 | 63 | if values is None: 64 | return date, time, level, tag, process, thread, message 65 | 66 | # print(values.groups().__str__()) 67 | i = 0 68 | for value in values.groups(): 69 | key = self.key_order[i] 70 | i += 1 71 | if key == "date": 72 | date = value 73 | elif key == "time": 74 | time = value 75 | elif key == "process": 76 | process = value 77 | elif key == "thread": 78 | thread = value 79 | elif key == "level": 80 | level = value 81 | elif key == "tag": 82 | tag = value 83 | elif key == "message": 84 | message = value 85 | 86 | return date, time, level, tag, process, thread, message 87 | 88 | contain_date = None 89 | contain_time = None 90 | contain_thread = None 91 | contain_tag = None 92 | contain_level = None 93 | 94 | def is_contain_date(self): 95 | if self.contain_date is None: 96 | self.contain_date = self.is_contain_key("date") 97 | return self.contain_date 98 | 99 | def is_contain_time(self): 100 | if self.contain_time is None: 101 | self.contain_time = self.is_contain_key("time") 102 | return self.contain_time 103 | 104 | def is_contain_thread(self): 105 | if self.contain_thread is None: 106 | self.contain_thread = self.is_contain_key("thread") 107 | return self.contain_thread 108 | 109 | def is_contain_tag(self): 110 | if self.contain_tag is None: 111 | self.contain_tag = self.is_contain_key("tag") 112 | return self.contain_tag 113 | 114 | def is_contain_level(self): 115 | if self.contain_level is None: 116 | self.contain_level = self.is_contain_key("level") 117 | return self.contain_level 118 | 119 | def is_contain_key(self, key): 120 | return key in self.key_order 121 | -------------------------------------------------------------------------------- /okcat/logfile_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | import re 19 | from os.path import exists 20 | 21 | from okcat.confloader import ConfLoader 22 | from okcat.helper import get_conf_path, print_unicode 23 | from okcat.logprocessor import LogProcessor 24 | from okcat.terminalcolor import colorize, allocate_color 25 | 26 | TIME_REGEX = r'\d{2}-\d{2} .*\d{2}:\d{2}:\d{2}\.\d+' 27 | 28 | 29 | class LogFileParser: 30 | filePaths = [] 31 | valid = False 32 | processor = None 33 | hideSameTags = None 34 | logStreams = [] 35 | cacheLines = [] 36 | lineTimes = [] 37 | 38 | def __init__(self, file_paths, hide_same_tags): 39 | self.filePaths = file_paths 40 | self.hideSameTags = hide_same_tags 41 | 42 | def setup(self, yml_file_name): 43 | for path in self.filePaths: 44 | if not exists(path): 45 | exit("log path: %s is not exist!" % path) 46 | self.processor = LogProcessor(self.hideSameTags) 47 | 48 | loader = ConfLoader() 49 | loader.load(get_conf_path(yml_file_name)) 50 | 51 | self.processor.setup_trans(trans_msg_map=loader.get_trans_msg_map(), 52 | trans_tag_map=loader.get_trans_tag_map(), 53 | hide_msg_list=loader.get_hide_msg_list()) 54 | self.processor.setup_separator(separator_rex_list=loader.get_separator_regex_list()) 55 | self.processor.setup_highlight(highlight_list=loader.get_highlight_list()) 56 | self.processor.setup_condition(tag_keywords=loader.get_tag_keyword_list()) 57 | log_line_regex = loader.get_log_line_regex() 58 | if log_line_regex is None: 59 | log_line_regex = 'date,time,process,thread,level,tag,message = "(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): (.*?)$"' 60 | print("you don't provide 'log_line-regex' for parse each line on file, so we use this one as default:") 61 | print(log_line_regex + "\n") 62 | self.processor.setup_regex_parser(regex_exp=log_line_regex) 63 | 64 | def color_line(self, line): 65 | msg_key, line_buf, match_precondition = self.processor.process(line) 66 | 67 | if not match_precondition: 68 | return 69 | 70 | if msg_key is not None: 71 | print('') 72 | print_unicode(u''.join(colorize(msg_key + ": ", fg=allocate_color(msg_key))).encode('utf-8').lstrip()) 73 | 74 | print_unicode(u''.join(line_buf).encode('utf-8').lstrip()) 75 | 76 | def popup_cache_line(self, popup_index): 77 | need_read_stream = self.logStreams[popup_index] 78 | new_line = need_read_stream.readline() 79 | if new_line: 80 | match_result = re.search(TIME_REGEX, new_line) 81 | if match_result: 82 | self.lineTimes.insert(popup_index, match_result.group()) 83 | self.cacheLines.insert(popup_index, new_line) 84 | else: 85 | self.color_line(new_line) 86 | self.popup_cache_line(popup_index) 87 | else: 88 | need_read_stream.close() 89 | self.logStreams.pop(popup_index) 90 | 91 | def process(self): 92 | origin_index = 0 93 | for path in self.filePaths: 94 | stream = open(path, "r") 95 | self.logStreams.append(stream) 96 | self.popup_cache_line(origin_index) 97 | origin_index += 1 98 | 99 | while self.cacheLines: 100 | min_index = self.lineTimes.index(min(self.lineTimes)) 101 | self.lineTimes.pop(min_index) 102 | selected_line = self.cacheLines.pop(min_index) 103 | self.color_line(selected_line) 104 | self.popup_cache_line(min_index) 105 | -------------------------------------------------------------------------------- /okcat/confloader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | """ 5 | Copyright (C) 2017 Jacksgong(jacksgong.com) 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | """ 19 | 20 | import yaml 21 | 22 | from okcat.helper import handle_home_case, get_conf_path 23 | 24 | __author__ = 'JacksGong' 25 | 26 | 27 | class ConfLoader: 28 | yml_conf = None 29 | from_yml_conf = None 30 | 31 | def __init__(self): 32 | pass 33 | 34 | def load(self, yml_file_path): 35 | with open(handle_home_case(yml_file_path), 'r') as stream: 36 | try: 37 | self.yml_conf = yaml.load(stream, yaml.SafeLoader) 38 | from_conf_path = self.get_from() 39 | if from_conf_path is not None: 40 | self.from_yml_conf = ConfLoader() 41 | self.from_yml_conf.load(get_conf_path(from_conf_path)) 42 | 43 | # print(u'find yml configuration on %s:' % yml_file_path) 44 | # self.dump() 45 | 46 | except yaml.YAMLError as exc: 47 | print(exc) 48 | 49 | def get_from(self): 50 | return self.get_value('from') 51 | 52 | def get_package(self): 53 | return self.get_value('package') 54 | 55 | def get_tag_keyword_list(self): 56 | return self.get_value('tag-keyword-list') 57 | 58 | def get_trans_msg_map(self): 59 | return self.get_value('trans-msg-map') 60 | 61 | def get_trans_tag_map(self): 62 | return self.get_value('trans-tag-map') 63 | 64 | def get_hide_msg_list(self): 65 | return self.get_value('hide-msg-list') 66 | 67 | def get_ignore_msg_list(self): 68 | return self.get_value('ignore-msg-list') 69 | 70 | def get_ignore_tag_list(self): 71 | return self.get_value('ignore-tag-list') 72 | 73 | def get_highlight_list(self): 74 | return self.get_value('highlight-list') 75 | 76 | def get_log_line_regex(self): 77 | return self.get_value('log-line-regex') 78 | 79 | def get_adb_log_line_regex(self): 80 | return self.get_value('adb-log-line-regex') 81 | 82 | def get_separator_regex_list(self): 83 | return self.get_value('separator-regex-list') 84 | 85 | def get_value(self, keyword): 86 | if keyword not in self.yml_conf or self.yml_conf[keyword] is None: 87 | if keyword != 'from' and self.from_yml_conf is not None: 88 | return self.from_yml_conf.get_value(keyword) 89 | else: 90 | return None 91 | 92 | origin = self.yml_conf[keyword] 93 | if self.from_yml_conf is not None and self.from_yml_conf.get_value(keyword) is not None: 94 | values = self.from_yml_conf.get_value(keyword) 95 | if type(origin) == list: 96 | origin.extend(values) 97 | if type(origin) == dict: 98 | origin.update(values) 99 | 100 | return origin 101 | 102 | def dump(self): 103 | print('from: %s' % self.get_from()) 104 | print('package: %s' % self.get_package()) 105 | print('log-line-regex: %s' % self.get_log_line_regex()) 106 | print('adb-log-line-regex: %s' % self.get_adb_log_line_regex()) 107 | self.dump_list('tag-keyword-list') 108 | self.dump_unicode_map('trans-msg-map') 109 | self.dump_unicode_map('trans-tag-map') 110 | self.dump_list('hide-msg-list') 111 | self.dump_list('ignore-msg-list') 112 | self.dump_list('ignore-tag-list') 113 | self.dump_list('highlight-list') 114 | self.dump_list('separator-regex-list') 115 | 116 | def dump_unicode_map(self, map_key): 117 | unicode_map = self.get_value(map_key) 118 | if unicode_map is None: 119 | print('%s: None' % map_key) 120 | else: 121 | print('%s:' % map_key) 122 | for key in unicode_map: 123 | print(u' "%s" : "%s"' % (key, unicode_map[key])) 124 | 125 | def dump_list(self, list_key): 126 | cur_list = self.get_value(list_key) 127 | if cur_list is None: 128 | print('%s: None' % list_key) 129 | else: 130 | print('%s: ' % list_key) 131 | for value in cur_list: 132 | print(' - %s' % value) 133 | -------------------------------------------------------------------------------- /okcat/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | import argparse 19 | from sys import argv 20 | 21 | from okcat.adb import Adb 22 | from okcat.helper import LOG_LEVELS, is_path 23 | from okcat.logfile_parser import LogFileParser 24 | from okcat.terminalcolor import print_tips, print_blue, print_warn, print_header, print_exit 25 | 26 | __author__ = 'JacksGong' 27 | __version__ = '1.4.0' 28 | __description__ = 'This python script used for combine several Android projects to one project.' 29 | 30 | 31 | def main(): 32 | print("-------------------------------------------------------") 33 | print(" OkCat v" + __version__) 34 | print("") 35 | print("Thanks for using okcat! Now, the doc is available on: ") 36 | print_blue(" https://github.com/Jacksgong/okcat") 37 | print("") 38 | print(" Have Fun!") 39 | print("-------------------------------------------------------") 40 | 41 | parser = argparse.ArgumentParser(description='Filter logcat by package name') 42 | parser.add_argument('package_or_path', nargs='*', 43 | help='This can be Application package name(s) or log file path(if the file from path is exist)') 44 | parser.add_argument('-y', '--yml_file_name', dest='yml', help='Using yml file you config on ~/.okcat folder') 45 | parser.add_argument('--hide-same-tags', dest='hide_same_tags', action='store_true', 46 | help='Do not display the same tag name') 47 | 48 | # following args are just for parser 49 | # parser.add_argument('-k', '--keyword', dest='keyword', action='append', help='You can filter you care about log by this keyword(s)') 50 | 51 | # following args are just for adb 52 | parser.add_argument('-w', '--tag-width', metavar='N', dest='tag_width', type=int, default=23, 53 | help='Width of log tag') 54 | parser.add_argument('-l', '--min-level', dest='min_level', type=str, choices=LOG_LEVELS + LOG_LEVELS.lower(), 55 | default='V', help='Minimum level to be displayed') 56 | parser.add_argument('--color-gc', dest='color_gc', action='store_true', help='Color garbage collection') 57 | parser.add_argument('--current', dest='current_app', action='store_true', 58 | help='Filter logcat by current running app') 59 | parser.add_argument('-s', '--serial', dest='device_serial', help='Device serial number (adb -s option)') 60 | parser.add_argument('-d', '--device', dest='use_device', action='store_true', 61 | help='Use first device for log input (adb -d option)') 62 | parser.add_argument('-e', '--emulator', dest='use_emulator', action='store_true', 63 | help='Use first emulator for log input (adb -e option)') 64 | parser.add_argument('-c', '--clear', dest='clear_logcat', action='store_true', 65 | help='Clear the entire log before running') 66 | parser.add_argument('-t', '--tag', dest='tag', action='append', help='Filter output by specified tag(s)') 67 | parser.add_argument('-tk', '--tag_keywords', dest='tag_keywords', action='append', 68 | help='Filter output by specified tag keyword(s)') 69 | parser.add_argument('-i', '--ignore-tag', dest='ignored_tag', action='append', 70 | help='Filter output by ignoring specified tag(s)') 71 | parser.add_argument('-a', '--all', dest='all', action='store_true', default=False, 72 | help='Print all log messages') 73 | 74 | # help 75 | if len(argv) == 2 and argv[1] == 'help': 76 | exit() 77 | 78 | args = parser.parse_args() 79 | 80 | file_paths = [] 81 | candidate_path = args.package_or_path 82 | for path in candidate_path: 83 | if is_path(path): 84 | file_paths.append(path) 85 | 86 | if file_paths: 87 | if args.yml is None: 88 | print("") 89 | print_exit("Please using '-y=conf-name' to provide config file to parse this log file.") 90 | print("The config file is very very simple! More detail about config file please move to : https://github.com/Jacksgong/okcat") 91 | print("") 92 | print("-------------------------------------------------------") 93 | exit() 94 | 95 | parser = LogFileParser(file_paths, args.hide_same_tags) 96 | parser.setup(args.yml) 97 | parser.process() 98 | else: 99 | is_interrupt_by_user = False 100 | 101 | _adb = Adb() 102 | _adb.setup(args) 103 | try: 104 | _adb.loop() 105 | except KeyboardInterrupt: 106 | is_interrupt_by_user = True 107 | 108 | if not is_interrupt_by_user: 109 | print_warn('ADB CONNECTION IS LOST.') 110 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # OkCat 2 | 3 | ![](https://img.shields.io/badge/log-any%20format-orange.svg) 4 | ![](https://img.shields.io/badge/log-android-orange.svg) 5 | ![](https://img.shields.io/badge/log-ios-orange.svg) 6 | ![](https://img.shields.io/badge/log-backend-orange.svg) 7 | ![](https://img.shields.io/badge/license-Apache2-blue.svg) 8 | [![](https://img.shields.io/badge/readme-English-blue.svg)](https://github.com/Jacksgong/okcat) 9 | [![](https://img.shields.io/badge/readme-中文-blue.svg)](https://github.com/Jacksgong/okcat/blob/master/README-zh.md) 10 | [![](https://img.shields.io/badge/pip-v1.4.0%20okcat-yellow.svg)](https://pypi.org/project/OkCat/1.4.0/) 11 | [![Build Status](https://travis-ci.org/Jacksgong/okcat.svg?branch=master)](https://travis-ci.org/Jacksgong/okcat) 12 | 13 | 强大的日志处理组件。 14 | 15 | [English Doc](https://github.com/Jacksgong/okcat) 16 | 17 | - 你可以定义任意的日志正则表达式,来适配任意格式的日志,可以将其用于iOS、Android、后端等等。 18 | - ADB Logcat部分是基于JakeWharton的PID Cat,并且适配了各类OkCat的新特性 。 19 | 20 | Andrdoid工程师查看ADB Logcat最简单的使用: 21 | 22 | ```shell 23 | okcat 包名 24 | ``` 25 | 26 | ## 特性 27 | 28 | > 最主要的特性是:你可以为不同的日志定义自己的正则表达式,以此适配各种类型的日志处理。 29 | 30 | - 高亮一些关键字 31 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/highlight-demo.png) 32 | - 转译日志内容 33 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/trans-msg-demo.png) 34 | - 转译Tag 35 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/trans-tag-demo.png) 36 | - 隐藏一些日志 37 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/hide-msg-demo.png) 38 | - 对连续的日志进行分割 39 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/separate-demo.png) 40 | - 忽略符合规则日志 41 | `以提供的字段开头的日志将会被直接忽略` 或者 `满足以提供的TAG的日志将会被直接忽略` 42 | 43 | ## 如何安装 44 | 45 | ```shell 46 | sudo pip install okcat 47 | ``` 48 | 49 | 如果你还没有安装`pip`,你需要先安装`pip`: 50 | 51 | 1. `brew install python` 52 | 2. `sudo easy_install pip` 53 | 54 | 如果你想要升级: 55 | 56 | ``` 57 | sudo pip install okcat --upgrade 58 | ``` 59 | 60 | ## 如何使用 61 | 62 | --- 63 | 64 | #### 最简单的测试 65 | 66 | 1. 下载[filedownloader.yml](https://github.com/Jacksgong/okcat/raw/master/demo-conf/filedownloader.yml)在当前目录,或者移动到`~/.okcat/`目录中 67 | 3. 运行这个[Filedownloader-Demo](https://github.com/lingochamp/FileDownloader)项目中的demo项目,并运行到你的Android手机上,然后将手机连接电脑 68 | 4. 执行: `okcat -y=filedownloader` 69 | 5. 此时日志就会根据[filedownloader.yml](https://github.com/Jacksgong/okcat/raw/master/demo-conf/filedownloader.yml)的配置输出了 70 | 71 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/demo.png) 72 | 73 | --- 74 | 75 | #### 1. 定义你的配置文件(`.yml`) 76 | 77 | 你可以在`~/.okcat/`目录下创建你的yaml格式的配置文件,如果`~/.okcat`文件夹不存在先创建该文件夹;当然也可以直接在执行命令的当前目录创建yaml格式的配置文件。 78 | 文件名字可以是任何你想要的名字,在执行`okcat`的时候可以通过`-y=文件名`的形式,告知okcat想要应用的是哪个文件名的配置文件,okcat会默认在当前目录找,找不到会在`~/.okcat`目录下进行查找。 79 | 80 | 下面是配置文件的案例,里面列出了目前支持的所有的配置,当然你不需要配置所有的特性,只需要配置你需要的即可。 81 | 82 | ```yml 83 | # 继承存在的其他yml的配置(不需要`.yml`后缀) 84 | from: exist-yml-file-name 85 | 86 | # 定义连线手机进行ADB处理时,需要过滤的包名; 87 | # 如果不使用Android的ADB功能,便不需要配置 88 | package: com.liulishuo.filedownloader.demo 89 | 90 | # 配置对于一行日志的正则表达式,目前支持正则出date、time、level、tag、process、thread、message 91 | # 不过不一定要全部提供,至少需要提供一个message 92 | # 如log-line-regex: 'message="(.\S*)"' 93 | log-line-regex: 'date,time,process,thread,level,tag,message = "(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 94 | 95 | # 在Android的ADB的情况下,我们是使用adb logcat -v brief -v threadtime 96 | # 一般情况下不需要adb-log-line-regex配置,我们已经有很完善的这块的正则,但是如果对这个需要特别定制便可以使用以下定制 97 | # adb-log-line-regex: 'date,time,process,thread,level,tag,message="(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 98 | 99 | # 分割正则列表 100 | # 可以提供多个正则表达式,对日志进行分割 101 | separator-regex-list: 102 | # 对满足以下正则的那行日志开始进行分割,并且以([^\]]*)的内容作为分割的标题 103 | - 'call start Url\[([^\]]*)\]' 104 | 105 | # 标签关键字 106 | # 如果不提供tag-keyword-list将会显示所有日志 107 | # 如果如下提供了tag-keyword-list将会过滤日志,只显示tag中包含了这里列出关键字的日志 108 | tag-keyword-list: 109 | - 'FileDownloader' 110 | 111 | # 内容转译表 112 | # 如果日志message中由表中key开头,将会使用彩色的文字在该message开头加上表中的value 113 | trans-msg-map: 114 | # 如这个例子: 115 | # 原message: 'filedownloader:lifecycle:over xxx' 116 | # 转译后: '| 任务结束 | filedownloader:lifecycle:over xxx' 其中的'任务结束'会使用彩色的文字显示 117 | 'filedownloader:lifecycle:over': '任务结束' 118 | 'fetch date with': '开始拉取' 119 | 120 | # 标签转译表 121 | # 如果日志tag中包含表中key开头,将会使用彩色背景的文字在该message开头加上表中的value 122 | trans-tag-map: 123 | # 如这个例子: 124 | # 原输出: 'FileDownloader.DownloadTaskHunter xxx' 125 | # 转译后: 'FileDownloader.DownloadTaskHunter [状态切换] xxx' 其中'[状态切换]'会使用彩色背景 126 | 'DownloadTaskHunter': '[状态切换]' 127 | 'ConnectTask': '[请求]' 128 | 129 | # 隐藏消息列表 130 | # 对以以下内容开头并且message长度小于100的内功进行灰色显示处理,在视觉上进行隐藏 131 | hide-msg-list: 132 | # 这里案例因为心跳日志是非常频繁的日志,通常没有什么问题,因此将其着灰色 133 | - 'notify progress' 134 | - '~~~callback' 135 | 136 | # 高亮列表 137 | # 对message中的以下内容,背景进行彩色处理使其高亮 138 | highlight-list: 139 | - 'Path[' 140 | - 'Url[' 141 | - 'Tag[' 142 | - 'range[' 143 | 144 | # 忽略日志列表 145 | # 以提供的字段开头的日志将会被直接忽略 146 | ignore-msg-list: 147 | - 'log start with this will be ignored' 148 | 149 | # 忽略日志TAG列表 150 | # 当所在日志的 TAG 在该列表中时会被直接忽略 151 | ignore-tag-list: 152 | - 'tagToBeIgnored' 153 | ``` 154 | 155 | #### 2. 执行 156 | 157 | > okcat的使用非常的简单。 158 | 159 | 如果你需要处理运行中App在Logcat的输出,只需要执行: 160 | 161 | ```shell 162 | okcat -y=your-conf-name 163 | ``` 164 | 165 | 如果你需要解析任意格式的日志,只需要执行: 166 | 167 | ```shell 168 | okcat -y=your-conf-name your-log-path1 your-log-path2 your-log-path3 ... 169 | ``` 170 | 171 | > 小技巧: 你在终端中使用`Command + K`来刷新当前回话中的所有内容,以此快速启动新的okcat解析,而不用再另外创建一个新的会话。 172 | 173 | ## 我的终端的风格配置 174 | 175 | 如果你想要适配和上面截图一样的终端风格,非常简单: 176 | 177 | - 首先,请使用[powerlevel9k](https://github.com/bhilburn/powerlevel9k)主题(正如Powerlevel9k文档提到的安装Powerlevel9k主题,并且安装Powerline字体). 178 | - 其次,请配置[iTerm2-Neutron](https://github.com/Ch4s3/iTerm2-Neutron)色系. 179 | - 最后, 请配置ini的shell(如果你使用的是zsh,只需要添加下列代码到`~/.zshrc`文件中): 180 | ``` 181 | POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(dir vcs) 182 | POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status time) 183 | POWERLEVEL9K_TIME_FORMAT="%D{%H:%M:%S}" 184 | POWERLEVEL9K_NODE_VERSION_BACKGROUND='022' 185 | POWERLEVEL9K_SHORTEN_DIR_LENGTH=2 186 | ``` 187 | 188 | ## LICENSE 189 | 190 | ``` 191 | Copyright (C) 2017 Jacksgong(jacksgong.com) 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | ``` 205 | -------------------------------------------------------------------------------- /okcat/logprocessor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | import re 19 | 20 | from okcat.helper import line_rstrip 21 | from okcat.logregex import LogRegex 22 | from okcat.logseparator import LogSeparator 23 | from okcat.terminalcolor import allocate_color, colorize, TAGTYPES, termcolor, BLACK, RESET 24 | from okcat.trans import Trans 25 | 26 | __author__ = 'JacksGong' 27 | 28 | TIME_WIDTH = 12 29 | THREAD_WIDTH = 12 30 | TAG_WIDTH = 23 31 | 32 | width = -1 33 | # noinspection PyBroadException 34 | try: 35 | # Get the current terminal width 36 | import fcntl, termios, struct 37 | 38 | h, width = struct.unpack('hh', fcntl.ioctl(0, termios.TIOCGWINSZ, struct.pack('hh', 0, 0))) 39 | except: 40 | pass 41 | 42 | header_size = TAG_WIDTH + 1 + 3 + 1 # space, level, space 43 | 44 | 45 | def indent_wrap(message): 46 | return message 47 | 48 | 49 | def keywords_regex(content, keywords): 50 | return any(re.match(r'.*' + t + r'.*', content) for t in map(str.strip, keywords)) 51 | 52 | 53 | class LogProcessor: 54 | hide_same_tags = None 55 | trans = None 56 | tag_keywords = None 57 | line_keywords = None 58 | separator = None 59 | regex_parser = None 60 | highlight_list = None 61 | # target_time = None 62 | 63 | ignore_msg_list = None 64 | ignore_tag_list = None 65 | 66 | # tmp 67 | last_msg_key = None 68 | last_tag = None 69 | pre_line_match = True 70 | 71 | def __init__(self, hide_same_tags): 72 | self.hide_same_tags = hide_same_tags 73 | 74 | def setup_trans(self, trans_msg_map, trans_tag_map, hide_msg_list): 75 | self.trans = Trans(trans_msg_map, trans_tag_map, hide_msg_list) 76 | 77 | def setup_ignore(self, ignore_msg_list, ignore_tag_list): 78 | self.ignore_msg_list = ignore_msg_list 79 | self.ignore_tag_list = ignore_tag_list 80 | 81 | def setup_separator(self, separator_rex_list): 82 | if separator_rex_list is not None: 83 | self.separator = LogSeparator(separator_rex_list) 84 | 85 | def setup_highlight(self, highlight_list): 86 | self.highlight_list = highlight_list 87 | 88 | def setup_condition(self, tag_keywords, line_keywords=None): 89 | self.tag_keywords = tag_keywords 90 | self.line_keywords = line_keywords 91 | 92 | def setup_regex_parser(self, regex_exp): 93 | self.regex_parser = LogRegex(regex_exp) 94 | 95 | def process(self, origin_line): 96 | origin_line = line_rstrip(origin_line) 97 | 98 | if len(origin_line.strip()) <= 0: 99 | return None, None, False 100 | 101 | if self.regex_parser is None: 102 | return None, None, False 103 | 104 | date, time, level, tag, process, thread, message = self.regex_parser.parse(origin_line) 105 | if message is None: 106 | message = origin_line 107 | 108 | return self.process_decode_content(origin_line, time, level, tag, process, thread, message) 109 | 110 | # noinspection PyUnusedLocal 111 | def process_decode_content(self, line, time, level, tag, process, thread, message): 112 | 113 | match_condition = True 114 | 115 | # filter 116 | if self.tag_keywords is not None and tag is not None: 117 | if not keywords_regex(tag, self.tag_keywords): 118 | match_condition = False 119 | self.pre_line_match = False 120 | else: 121 | self.pre_line_match = True 122 | 123 | 124 | 125 | if self.line_keywords is not None: 126 | if not keywords_regex(line, self.line_keywords): 127 | match_condition = False 128 | self.pre_line_match = False 129 | else: 130 | self.pre_line_match = True 131 | 132 | if match_condition and tag is None and not self.pre_line_match: 133 | match_condition = False 134 | 135 | # if 'special world' in line: 136 | # match_precondition = True 137 | 138 | if self.ignore_msg_list is not None: 139 | for ignore_msg in self.ignore_msg_list: 140 | if message.startswith(ignore_msg): 141 | match_condition = False 142 | 143 | if self.ignore_tag_list is not None: 144 | if tag in self.ignore_tag_list: 145 | match_condition = False 146 | 147 | if not match_condition: 148 | return None, None, None 149 | 150 | msgkey = None 151 | # the handled current line 152 | linebuf = '' 153 | 154 | # time 155 | if time is not None: 156 | time = time[-TIME_WIDTH:].rjust(TIME_WIDTH) 157 | linebuf += time 158 | linebuf += ' ' 159 | elif self.regex_parser.is_contain_time(): 160 | linebuf += ' ' * TIME_WIDTH 161 | linebuf += ' ' 162 | 163 | # thread 164 | if thread is not None: 165 | thread = thread.strip() 166 | thread = thread[-THREAD_WIDTH:].rjust(THREAD_WIDTH) 167 | linebuf += thread 168 | linebuf += ' ' 169 | elif self.regex_parser.is_contain_thread(): 170 | linebuf += ' ' * THREAD_WIDTH 171 | linebuf += ' ' 172 | 173 | # tag 174 | if tag is not None and (not self.hide_same_tags or tag != self.last_tag): 175 | self.last_tag = tag 176 | tag = tag.strip() 177 | color = allocate_color(tag) 178 | tag = tag.strip() 179 | tag = tag[-TAG_WIDTH:].rjust(TAG_WIDTH) 180 | linebuf += colorize(tag, fg=color) 181 | linebuf += ' ' 182 | elif self.regex_parser.is_contain_tag(): 183 | linebuf += ' ' * TAG_WIDTH 184 | linebuf += ' ' 185 | 186 | # level 187 | if level is not None: 188 | if level in TAGTYPES: 189 | linebuf += TAGTYPES[level] 190 | else: 191 | linebuf += ' ' + level + ' ' 192 | linebuf += ' ' 193 | elif self.regex_parser.is_contain_level(): 194 | linebuf += ' ' 195 | linebuf += ' ' 196 | 197 | # message 198 | # -separator 199 | if self.separator is not None: 200 | msgkey = self.separator.process(message) 201 | 202 | # -trans 203 | if self.trans is not None: 204 | message = self.trans.trans_msg(message) 205 | message = self.trans.hide_msg(message) 206 | message = self.trans.trans_tag(tag, message) 207 | 208 | if self.highlight_list is not None: 209 | for highlight in self.highlight_list: 210 | if highlight in message: 211 | message = message.replace(highlight, 212 | termcolor(fg=BLACK, bg=allocate_color(highlight)) + highlight + RESET) 213 | 214 | linebuf += message 215 | 216 | return msgkey, linebuf, match_condition 217 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OkCat 2 | 3 | ![](https://img.shields.io/badge/log-any%20format-orange.svg) 4 | ![](https://img.shields.io/badge/log-android-orange.svg) 5 | ![](https://img.shields.io/badge/log-ios-orange.svg) 6 | ![](https://img.shields.io/badge/log-backend-orange.svg) 7 | ![](https://img.shields.io/badge/license-Apache2-blue.svg) 8 | [![](https://img.shields.io/badge/readme-English-blue.svg)](https://github.com/Jacksgong/okcat) 9 | [![](https://img.shields.io/badge/readme-中文-blue.svg)](https://github.com/Jacksgong/okcat/blob/master/README-zh.md) 10 | [![](https://img.shields.io/badge/pip-v1.4.0%20okcat-yellow.svg)](https://pypi.org/project/OkCat/1.4.0/) 11 | [![Build Status](https://travis-ci.org/Jacksgong/okcat.svg?branch=master)](https://travis-ci.org/Jacksgong/okcat) 12 | 13 | An powerful log processor. 14 | 15 | [中文文档](https://github.com/Jacksgong/okcat/blob/master/README-zh.md) 16 | 17 | - The adb logcat handler is just update to JakeWharton's nice pidcat and I adapt it for more features. 18 | - You can using this log processor with define you own `log-line-regex` and it can work for any log: iOS, Android, Backend, etc. 19 | 20 | ## Features 21 | 22 | > The most important feature is you can define any regex for any kind of log. 23 | 24 | - highlight some keywords 25 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/highlight-demo.png) 26 | - trans msgs to some words 27 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/trans-msg-demo.png) 28 | - trans tags to some words 29 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/trans-tag-demo.png) 30 | - hide msg on logs 31 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/hide-msg-demo.png) 32 | - separate logs 33 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/separate-demo.png) 34 | - ignore msg on logs: 35 | `when you provide such list, the msg start with provided msg will be ignored to printed` 36 | - ignore tag on logs: 37 | `when you provide such list, the tag in the list will be ignored to printed` 38 | 39 | ## How to Install 40 | 41 | ```shell 42 | sudo pip install okcat 43 | ``` 44 | 45 | If you has not installed `pip` yet, you need to install it first: 46 | 47 | 1. `brew install python` 48 | 2. `sudo easy_install pip` 49 | 50 | If you want to upgrade: 51 | 52 | ```shell 53 | sudo pip install okcat --upgrade 54 | ``` 55 | 56 | ## How to Use 57 | 58 | --- 59 | 60 | #### Simplest test 61 | 62 | 1. Download: download [filedownloader.yml](https://github.com/Jacksgong/okcat/raw/master/demo-conf/filedownloader.yml) to the current folder or move to the `~/.okcat/` folder 63 | 2. Running: run the demo project on [Filedownloader](https://github.com/lingochamp/FileDownloader) repo to your Android phone and connect your Phone to computer 64 | 3. Execute: `okcat -y=filedownloader` 65 | 4. Done: now, you can checkout the colored logs on terminal, enjoy~ 66 | 67 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/demo.png) 68 | 69 | --- 70 | 71 | #### 1. Define your config file(`.yml`) 72 | 73 | You can create your own `.yaml` file as config file on `~/.okcat/` folder or the current folder you will execute `okcat` command, and the filename is free to choose, when you execute the okcat, we will ask you the configure file name you want to apply. 74 | 75 | the following is demo of config file, Of course, you don't have to provide all configs such below, if you think which one is needed, just config that one. 76 | 77 | ```yml 78 | # extends from exist yml file (provide only filename without `.yml` extension) 79 | # from: exist-yml-name 80 | 81 | # we will filter out logs with the provided package (name) 82 | # this 'package' keyword is just using for android adb logcat 83 | package: com.liulishuo.filedownloader.demo 84 | 85 | # this 'log-line-regex' is just a regex for one line log 86 | # now we support keyword: 'date' 'time' 'level' 'tag' 'process' 'thread' 'message' 87 | # you don't have to provide all keyword, but you have to provide at least the 'message' 88 | # such as: 'message="(\S*)"' 89 | log-line-regex: 'date,time,process,thread,level,tag,message = "(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 90 | 91 | # on the case of filter logs from Android adb logcat, we using 'adb logcat -v brief -v threadtime' command to obtain logcat 92 | # in the normal case you don't need ot provide this config, because there is a perfect one on the okcat internal 93 | # but if you want to customize the regex log from adb logcat, it's free to define it such below 94 | # adb-log-line-regex: 'date,time,process,thread,level,tag,message="(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 95 | 96 | # separator regex list 97 | # you can provide multiple regex to separate serial logs 98 | separator-regex-list: 99 | # on this case, if one line log match 'call start Url\[([^\]]*)\]' regex we will separate logs with \n and output a indie line with the '([^\]]*)' value as the title of separate 100 | - 'call start Url\[([^\]]*)\]' 101 | 102 | # tag keyword list 103 | # this list keyword is using for filter out which log need to be output 104 | # all provided keyword will be using for compare with each line tag, if a line with tag not contain any keyword on 'tag-keyword-list' it will be ignore to output 105 | tag-keyword-list: 106 | - 'FileDownloader' 107 | 108 | # translate message map 109 | # if a message on a line start with provide keyword on the 'trans-msg-map' we will add the value of the keyword on the start of the message, and the word of value will be colored to highlight it 110 | trans-msg-map: 111 | # such as this case: 112 | # origin message: 'filedownloader:lifecycle:over xxx' 113 | # after translate: '| Task OVER | filedownloader:lifecycle:over xxx' 114 | 'filedownloader:lifecycle:over': 'Task OVER' 115 | 'fetch data with': 'Start Fetch' 116 | 117 | # translate tag map 118 | # if a tag on a line contain provide keyword on the 'trans-tag-map' we will add the value of the keyword on the start of the message, and the background of the value word will be colored to highlight it 119 | trans-tag-map: 120 | # such as this case: 121 | # origin message: 'FileDownloader.DownloadTaskHunter xxx' 122 | # after translate: 'FileDownloader.DownloadTaskHunter [Status Change] xxx' 123 | 'DownloadTaskHunter': '[Status Change]' 124 | 'ConnectTask': '[Request]' 125 | 126 | # hide message list 127 | # if a message on a line start with provide value on the 'hide-msg-list` and the length of the message is less than 100 word, it would be colored with gray to hide 128 | hide-msg-list: 129 | # here we hide message start with 'notify progress' and '~~~callback' because it is too frequently to output and useless in most case 130 | - 'notify progress' 131 | - '~~~callback' 132 | 133 | # highlight list 134 | # if any value on the 'highlight-list' display on any message, the background of the value word would be colored to highlight it 135 | highlight-list: 136 | - 'Path[' 137 | - 'Url[' 138 | - 'Tag[' 139 | - 'range[' 140 | 141 | # ignore message list 142 | # when you provide such list, the msg start with provided msg will be ignored to printed 143 | ignore-msg-list: 144 | - 'log start with this will be ignored' 145 | 146 | # ignore tag list 147 | # when you provide such list, the tag in the list will be ignored to printed 148 | ignore-tag-list: 149 | - 'tagToBeIgnored' 150 | ``` 151 | 152 | #### 2. Execute 153 | 154 | You can just parse logcat from running adb: 155 | 156 | ```shell 157 | okcat -y=your-conf-name 158 | ``` 159 | 160 | You also can parse your log file through: 161 | 162 | ```shell 163 | okcat -y=your-conf-name your-log-path1 your-log-path2 your-log-path3 ... 164 | ``` 165 | 166 | Simplest case for any developer: 167 | 168 | ```shell 169 | okcat your.package.name 170 | ``` 171 | 172 | > Tips: You can use `command + k` on Terminal to flush all content on the session and start a new okcat parse instead of creating anthor new session. 173 | 174 | ## My Terminal Config 175 | 176 | If you want to adapter the same theme like screenshot above, it's very easy: 177 | 178 | - Firstly, please use [powerlevel9k](https://github.com/bhilburn/powerlevel9k) theme(Install the Powerlevel9k Theme and Powerline Fonts as the powerlevel9k repo readme doc said). 179 | - Secondly, please config the [iTerm2-Neutron](https://github.com/Ch4s3/iTerm2-Neutron) color scheme. 180 | - Thirdly, please config your shell(If you are using zsh, just add following code to the `~/.zshrc` file): 181 | ``` 182 | POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(dir vcs) 183 | POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status time) 184 | POWERLEVEL9K_TIME_FORMAT="%D{%H:%M:%S}" 185 | POWERLEVEL9K_NODE_VERSION_BACKGROUND='022' 186 | POWERLEVEL9K_SHORTEN_DIR_LENGTH=2 187 | ``` 188 | 189 | ## Dev 190 | 191 | Import to PyCharm, and Set the Project Structure: 192 | 193 | ![](https://github.com/Jacksgong/okcat/raw/master/arts/pycharm-build.jpg) 194 | 195 | ## LICENSE 196 | 197 | ``` 198 | Copyright (C) 2017 Jacksgong(jacksgong.com) 199 | 200 | Licensed under the Apache License, Version 2.0 (the "License"); 201 | you may not use this file except in compliance with the License. 202 | You may obtain a copy of the License at 203 | 204 | http://www.apache.org/licenses/LICENSE-2.0 205 | 206 | Unless required by applicable law or agreed to in writing, software 207 | distributed under the License is distributed on an "AS IS" BASIS, 208 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 209 | See the License for the specific language governing permissions and 210 | limitations under the License. 211 | ``` 212 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright (C) 2017 Jacksgong(jacksgong.com) 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /demo-conf/demo-log-file.log: -------------------------------------------------------------------------------- 1 | --------- beginning of system 2 | 06-26 15:18:59.546 1513 1513 I vold : Vold 3.0 (the awakening) firing up 3 | 06-26 15:18:59.546 1513 1513 V vold : Detected support for: ext4 vfat 4 | 06-26 15:18:59.551 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop5: No such device or address 5 | 06-26 15:18:59.581 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop1: No such device or address 6 | 06-26 15:18:59.621 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop6: No such device or address 7 | 06-26 15:18:59.662 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop2: No such device or address 8 | 06-26 15:18:59.700 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop4: No such device or address 9 | 06-26 15:18:59.810 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop0: No such device or address 10 | 06-26 15:18:59.841 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop7: No such device or address 11 | 06-26 15:18:59.880 1513 1513 W vold : Failed to LOOP_GET_STATUS64 /dev/block/loop3: No such device or address 12 | 06-26 15:18:59.915 1513 1513 D vold : VoldNativeService::start() completed OK 13 | 06-26 15:18:59.932 1513 1517 D vold : e4crypt_init_user0 14 | 06-26 15:18:59.933 1513 1517 D vold : e4crypt_prepare_user_storage for volume null, user 0, serial 0, flags 1 15 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/system/users/0 16 | 06-26 15:18:59.933 1513 1516 I vold : Found disk at /devices/pci0000:00/0000:00:08.0/virtio5/block/vdf but delaying scan due to secure keyguard 17 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/misc/profiles/cur/0 18 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/system_de/0 19 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/misc_de/0 20 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/vendor_de/0 21 | 06-26 15:18:59.933 1513 1517 D vold : Preparing: /data/user_de/0 22 | 06-26 15:18:59.934 1513 1517 V vold : /system/bin/vold_prepare_subdirs 23 | 06-26 15:18:59.935 1513 1517 V vold : prepare 24 | 06-26 15:18:59.935 1513 1517 V vold : 0 25 | 06-26 15:18:59.935 1513 1517 V vold : 1 26 | 06-26 15:18:59.949 1513 1517 D vold : e4crypt_unlock_user_key 0 serial=0 token_present=0 27 | 06-26 15:18:59.949 1513 1517 E vold : Failed to chmod /data/system_ce/0: No such file or directory 28 | 06-26 15:18:59.949 1513 1517 E vold : Failed to chmod /data/misc_ce/0: No such file or directory 29 | 06-26 15:18:59.949 1513 1517 E vold : Failed to chmod /data/media/0: No such file or directory 30 | 06-26 15:19:00.225 1513 1590 I Cryptfs : cryptfs_check_passwd 31 | 06-26 15:19:00.227 1513 1590 D Cryptfs : crypt_ftr->fs_size = 1638400 32 | 06-26 15:19:00.227 1513 1590 I Cryptfs : Using scrypt for cryptfs KDF 33 | 06-26 15:19:00.393 1572 1572 I android.hardware.wifi@1.0-service: Wifi Hal is booting up... 34 | 06-26 15:19:00.813 1513 1590 I Cryptfs : Extra parameters for dm_crypt: 1 allow_discards 35 | 06-26 15:19:00.822 1513 1516 D vold : Disk at 252:0 changed 36 | 06-26 15:19:00.978 1513 1590 I Cryptfs : Password matches 37 | 06-26 15:19:00.980 1513 1590 D Cryptfs : test_mount_encrypted_fs(): Master key saved 38 | 06-26 15:19:00.990 1513 1590 I vold : List of Keymaster HALs found: 39 | 06-26 15:19:00.990 1513 1590 I vold : Keymaster HAL #1: SoftwareKeymasterDevice from Google SecurityLevel: SOFTWARE HAL : android.hardware.keymaster@3.0::IKeymasterDevice instance default 40 | 06-26 15:19:00.990 1513 1590 I vold : Using SoftwareKeymasterDevice from Google for encryption. Security level: SOFTWARE, HAL: android.hardware.keymaster@3.0::IKeymasterDevice/default 41 | 06-26 15:19:00.990 1513 1590 D Cryptfs : Password is default - restarting filesystem 42 | 06-26 15:19:01.032 1513 1590 D Cryptfs : unmounting /data succeeded 43 | 06-26 15:19:01.036 1513 1590 I vold : [libfs_mgr]superblock s_max_mnt_count:65535,/dev/block/dm-0 44 | 06-26 15:19:01.037 1513 1590 I vold : [libfs_mgr]Filesystem on /dev/block/dm-0 was not cleanly shutdown; state flags: 0x1, incompat feature flags: 0x46 45 | 06-26 15:19:01.315 1513 1590 I vold : [libfs_mgr]check_fs(): mount(/dev/block/dm-0,/data,ext4)=0: Success 46 | 06-26 15:19:01.354 1513 1590 I vold : [libfs_mgr]check_fs(): unmount(/data) succeeded 47 | 06-26 15:19:01.355 1513 1590 I vold : [libfs_mgr]Running /system/bin/e2fsck on /dev/block/dm-0 48 | 06-26 15:19:01.601 1513 1590 I vold : [libfs_mgr]e2fsck returned status 0x100 49 | 06-26 15:19:01.612 1513 1590 I vold : [libfs_mgr]__mount(source=/dev/block/dm-0,target=/data,type=ext4)=0: Success 50 | 06-26 15:19:01.612 1513 1590 D Cryptfs : Just triggered post_fs_data 51 | 06-26 15:19:01.649 1513 1515 D vold : e4crypt_init_user0 52 | 06-26 15:19:01.649 1513 1515 D vold : e4crypt_prepare_user_storage for volume null, user 0, serial 0, flags 1 53 | 06-26 15:19:01.649 1513 1515 D vold : Preparing: /data/system/users/0 54 | 06-26 15:19:01.649 1513 1515 D vold : Preparing: /data/misc/profiles/cur/0 55 | 06-26 15:19:01.650 1513 1515 D vold : Preparing: /data/system_de/0 56 | 06-26 15:19:01.651 1513 1515 D vold : Preparing: /data/misc_de/0 57 | 06-26 15:19:01.652 1513 1515 D vold : Preparing: /data/vendor_de/0 58 | 06-26 15:19:01.652 1513 1515 D vold : Preparing: /data/user_de/0 59 | 06-26 15:19:01.653 1513 1515 V vold : /system/bin/vold_prepare_subdirs 60 | 06-26 15:19:01.653 1513 1515 V vold : prepare 61 | 06-26 15:19:01.653 1513 1515 V vold : 62 | 06-26 15:19:01.653 1513 1515 V vold : 0 63 | 06-26 15:19:01.653 1513 1515 V vold : 1 64 | 06-26 15:19:01.667 1513 1515 D vold : e4crypt_unlock_user_key 0 serial=0 token_present=0 65 | 06-26 15:19:01.691 1513 1590 D Cryptfs : post_fs_data done 66 | 06-26 15:19:01.692 1513 1590 D Cryptfs : Just triggered restart_framework 67 | 06-26 15:19:01.987 1694 1694 I wificond: wificond is starting up... 68 | 06-26 15:19:02.140 1685 1685 I installd: installd firing up 69 | 06-26 15:19:03.232 1681 1681 D Zygote32Timing: BeginIcuCachePinning took to complete: 46ms 70 | 06-26 15:19:03.721 1681 1681 D Zygote32Timing: PreloadClasses took to complete: 489ms 71 | 06-26 15:19:03.829 1681 1681 D Zygote32Timing: PreloadResources took to complete: 107ms 72 | 06-26 15:19:03.864 1681 1681 D Zygote32Timing: ZygotePreload took to complete: 679ms 73 | 06-26 15:19:03.873 1681 1681 D Zygote32Timing: PostZygoteInitGC took to complete: 9ms 74 | 06-26 15:19:03.873 1681 1681 D Zygote32Timing: ZygoteInit took to complete: 694ms 75 | 06-26 15:19:04.001 1819 1819 I SystemServer: InitBeforeStartServices 76 | 06-26 15:19:04.002 1819 1819 I SystemServer: Entered the Android system server! 77 | 06-26 15:19:04.162 1819 1819 D SystemServerTiming: InitBeforeStartServices took to complete: 161ms 78 | 06-26 15:19:04.162 1819 1819 I SystemServer: StartServices 79 | 06-26 15:19:04.162 1819 1819 I SystemServer: Reading configuration... 80 | 06-26 15:19:04.162 1819 1819 I SystemServer: ReadingSystemConfig 81 | 06-26 15:19:04.164 1819 1819 D SystemServerTiming: ReadingSystemConfig took to complete: 2ms 82 | 06-26 15:19:04.164 1819 1819 I SystemServer: StartInstaller 83 | 06-26 15:19:04.164 1819 1819 I SystemServiceManager: Starting com.android.server.pm.Installer 84 | 06-26 15:19:04.165 1819 1832 D SystemServerInitThreadPool: Started executing ReadingSystemConfig 85 | 06-26 15:19:04.169 1819 1819 D SystemServerTiming: StartInstaller took to complete: 5ms 86 | 06-26 15:19:04.169 1819 1819 I SystemServer: DeviceIdentifiersPolicyService 87 | 06-26 15:19:04.169 1819 1819 I SystemServiceManager: Starting com.android.server.os.DeviceIdentifiersPolicyService 88 | 06-26 15:19:04.171 1819 1819 D SystemServerTiming: DeviceIdentifiersPolicyService took to complete: 2ms 89 | 06-26 15:19:04.171 1819 1819 I SystemServer: StartActivityManager 90 | 06-26 15:19:04.171 1819 1819 I SystemServiceManager: Starting com.android.server.am.ActivityManagerService$Lifecycle 91 | 06-26 15:19:04.197 1819 1819 I ActivityManager: Memory class: 384 92 | 06-26 15:19:04.207 1819 1832 D SystemServerInitThreadPool: Finished executing ReadingSystemConfig 93 | 06-26 15:19:04.216 1819 1819 D BatteryStatsImpl: Reading daily items from /data/system/batterystats-daily.xml 94 | 06-26 15:19:04.230 1819 1840 E BatteryExternalStatsWorker: no controller energy info supplied for telephony 95 | 06-26 15:19:04.233 1819 1840 I KernelUidCpuFreqTimeReader: mPerClusterTimesAvailable=false 96 | 06-26 15:19:04.235 1819 1840 W KernelCpuProcReader: File not exist: /proc/uid_cpupower/time_in_state 97 | 06-26 15:19:04.235 1819 1840 W KernelCpuProcReader: File not exist: /proc/uid_cpupower/concurrent_active_time 98 | 06-26 15:19:04.236 1819 1840 W KernelCpuProcReader: File not exist: /proc/uid_cpupower/concurrent_policy_time 99 | 06-26 15:19:04.236 1819 1840 E KernelCpuSpeedReader: Failed to read cpu-freq: /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state (No such file or directory) 100 | 06-26 15:19:04.236 1819 1840 W KernelMemoryBandwidthStats: No kernel memory bandwidth stats available 101 | 06-26 15:19:04.252 1819 1819 W AppOps : Unknown attribute in 'op' tag: n 102 | 06-26 15:19:04.256 1819 1819 I chatty : uid=1000 system_server identical 163 lines 103 | 06-26 15:19:04.256 1819 1819 W AppOps : Unknown attribute in 'op' tag: n 104 | 06-26 15:19:04.266 1819 1819 I IntentFirewall: Read new rules (A:0 B:0 S:0) 105 | 06-26 15:19:04.277 1819 1819 D AppOps : AppOpsService published 106 | 06-26 15:19:04.277 1819 1819 D SystemServerTiming: StartActivityManager took to complete: 106ms 107 | 06-26 15:19:04.277 1819 1819 I SystemServer: StartPowerManager 108 | 06-26 15:19:04.277 1819 1819 I SystemServiceManager: Starting com.android.server.power.PowerManagerService 109 | 06-26 15:19:04.288 1819 1819 D SystemServerTiming: StartPowerManager took to complete: 11ms 110 | 06-26 15:19:04.288 1819 1819 I SystemServer: InitPowerManagement 111 | 06-26 15:19:04.290 1819 1819 D SystemServerTiming: InitPowerManagement took to complete: 2ms 112 | 06-26 15:19:04.290 1819 1819 I SystemServer: StartRecoverySystemService 113 | 06-26 15:19:04.290 1819 1819 I SystemServiceManager: Starting com.android.server.RecoverySystemService 114 | 06-26 15:19:04.291 1819 1819 D SystemServerTiming: StartRecoverySystemService took to complete: 1ms 115 | 06-26 15:19:04.293 1819 1819 W RescueParty: Failed to determine if device was on USB 116 | 06-26 15:19:04.293 1819 1819 W RescueParty: java.io.FileNotFoundException: /sys/class/android_usb/android0/state (No such file or directory) 117 | 06-26 15:19:04.293 1819 1819 W RescueParty: at java.io.FileInputStream.open0(Native Method) 118 | 06-26 15:19:04.293 1819 1819 W RescueParty: at java.io.FileInputStream.open(FileInputStream.java:231) 119 | 06-26 15:19:04.293 1819 1819 W RescueParty: at java.io.FileInputStream.(FileInputStream.java:165) 120 | 06-26 15:19:04.293 1819 1819 W RescueParty: at android.os.FileUtils.readTextFile(FileUtils.java:514) 121 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.RescueParty.isUsbActive(RescueParty.java:348) 122 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.RescueParty.isDisabled(RescueParty.java:88) 123 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.RescueParty.noteBoot(RescueParty.java:107) 124 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.SystemServer.startBootstrapServices(SystemServer.java:587) 125 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.SystemServer.run(SystemServer.java:429) 126 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.server.SystemServer.main(SystemServer.java:294) 127 | 06-26 15:19:04.293 1819 1819 W RescueParty: at java.lang.reflect.Method.invoke(Native Method) 128 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 129 | 06-26 15:19:04.293 1819 1819 W RescueParty: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838) 130 | 06-26 15:19:04.296 1819 1819 W RescueParty: Noticed 1 events for UID 0 in last 7 sec 131 | 06-26 15:19:04.296 1819 1819 I SystemServer: StartLightsService 132 | 06-26 15:19:04.296 1819 1819 I SystemServiceManager: Starting com.android.server.lights.LightsService 133 | 06-26 15:19:04.297 1819 1819 D SystemServerTiming: StartLightsService took to complete: 1ms 134 | 06-26 15:19:04.297 1819 1819 I SystemServer: StartSidekickService 135 | 06-26 15:19:04.297 1819 1819 D SystemServerTiming: StartSidekickService took to complete: 0ms 136 | 06-26 15:19:04.297 1819 1819 I SystemServer: StartDisplayManager 137 | 06-26 15:19:04.297 1819 1819 I SystemServiceManager: Starting com.android.server.display.DisplayManagerService 138 | 06-26 15:19:04.301 1819 1819 D SystemServerTiming: StartDisplayManager took to complete: 4ms 139 | 06-26 15:19:04.301 1819 1819 I SystemServer: WaitForDisplay 140 | 06-26 15:19:04.301 1819 1819 I SystemServiceManager: Starting phase 100 141 | -------------------------------------------------------------------------------- /okcat/adb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | 3 | """ 4 | Copyright (C) 2017 Jacksgong(jacksgong.com) 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | """ 18 | from os.path import exists 19 | 20 | from okcat.confloader import ConfLoader 21 | from okcat.helper import LOG_LEVELS_MAP, get_conf_path, handle_home_case, print_unicode 22 | from okcat.logprocessor import LogProcessor, indent_wrap 23 | from okcat.logregex import LogRegex 24 | from okcat.terminalcolor import termcolor, RED, RESET, YELLOW, GREEN, colorize, WHITE, allocate_color 25 | 26 | __author__ = 'JacksGong' 27 | 28 | # Script to highlight adb logcat output for console 29 | # Originally written by Jeff Sharkey, http://jsharkey.org/ 30 | # Piping detection and popen() added by other Android team members 31 | # Package filtering and output improvements by Jake Wharton, http://jakewharton.com 32 | # Package adapt for okcat by Jacks Gong, https://jacksgong.com 33 | 34 | import sys 35 | import re 36 | import subprocess 37 | from subprocess import PIPE, STDOUT 38 | 39 | # noinspection Annotator 40 | PID_LINE = re.compile(r'^\w+\s+(\w+)\s+\w+\s+\w+\s+\w+\s+\w+\s+\w+\s+\w\s([\w|\.|\/]+)') 41 | PID_START = re.compile(r'^.*: Start proc ([a-zA-Z0-9._:]+) for ([a-z]+ [^:]+): pid=(\d+) uid=(\d+) gids=(.*)$') 42 | PID_START_5_1 = re.compile(r'^.*: Start proc (\d+):([a-zA-Z0-9._:]+)/[a-z0-9]+ for (.*)$') 43 | PID_START_DALVIK = re.compile( 44 | r'^E/dalvikvm\(\s*(\d+)\): >>>>> ([a-zA-Z0-9._:]+) \[ userId:0 \| appId:(\d+) \]$') 45 | PID_KILL = re.compile(r'^Killing (\d+):([a-zA-Z0-9._:]+)/[^:]+: (.*)$') 46 | PID_LEAVE = re.compile(r'^No longer want ([a-zA-Z0-9._:]+) \(pid (\d+)\): .*$') 47 | PID_DEATH = re.compile(r'^Process ([a-zA-Z0-9._:]+) \(pid (\d+)\) has died.?$') 48 | 49 | ADB_LOG_REGEX_EXP = 'date,time,process,thread,level,tag,message="(.\S*) *(.\S*) *(\d*) *(\d*) *([A-Z]) *([^:]*): *(.*?)$"' 50 | 51 | BUG_LINE = re.compile(r'.*nativeGetEnabledTags.*') 52 | BACKTRACE_LINE = re.compile(r'^#(.*?)pc\s(.*?)$') 53 | RULES = { 54 | # StrictMode policy violation; ~duration=319 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1 55 | re.compile(r'^(StrictMode policy violation)(; ~duration=)(\d+ ms)') 56 | : r'%s\1%s\2%s\3%s' % (termcolor(RED), RESET, termcolor(YELLOW), RESET), 57 | } 58 | 59 | 60 | class Adb: 61 | all = None 62 | min_level = None 63 | package_name = None 64 | tag = None 65 | header_size = None 66 | ignored_tag = None 67 | 68 | log_regex = None 69 | catchall_package = None 70 | named_processes = None 71 | pids = None 72 | 73 | adb = None 74 | processor = None 75 | 76 | def __init__(self): 77 | pass 78 | 79 | def setup(self, args): 80 | self.processor = LogProcessor(args.hide_same_tags) 81 | 82 | self.min_level = LOG_LEVELS_MAP[args.min_level.upper()] 83 | self.all = args.all 84 | self.ignored_tag = args.ignored_tag 85 | self.tag = args.tag 86 | 87 | self.package_name = args.package_or_path 88 | self.processor.setup_condition(tag_keywords=args.tag_keywords) 89 | if args.yml is not None: 90 | conf_file_path = get_conf_path(args.yml) 91 | if not exists(handle_home_case(conf_file_path)): 92 | exit('you provide conf file path: ' + conf_file_path + ' is not exist!') 93 | 94 | conf_loader = ConfLoader() 95 | conf_loader.load(conf_file_path) 96 | 97 | yml_package = conf_loader.get_package() 98 | if yml_package is not None: 99 | self.package_name.append(yml_package) 100 | 101 | yml_adb_log_regex = conf_loader.get_adb_log_line_regex() 102 | if yml_adb_log_regex is not None: 103 | self.log_regex = LogRegex(yml_adb_log_regex) 104 | 105 | self.processor.setup_condition(tag_keywords=conf_loader.get_tag_keyword_list()) 106 | self.processor.setup_trans(trans_msg_map=conf_loader.get_trans_msg_map(), 107 | trans_tag_map=conf_loader.get_trans_tag_map(), 108 | hide_msg_list=conf_loader.get_hide_msg_list()) 109 | self.processor.setup_ignore(ignore_msg_list=conf_loader.get_ignore_msg_list(), 110 | ignore_tag_list=conf_loader.get_ignore_tag_list()) 111 | self.processor.setup_highlight(highlight_list=conf_loader.get_highlight_list()) 112 | self.processor.setup_separator(separator_rex_list=conf_loader.get_separator_regex_list()) 113 | 114 | if self.log_regex is None: 115 | self.log_regex = LogRegex(ADB_LOG_REGEX_EXP) 116 | 117 | base_adb_command = ['adb'] 118 | if args.device_serial: 119 | base_adb_command.extend(['-s', args.device_serial]) 120 | if args.use_device: 121 | base_adb_command.append('-d') 122 | if args.use_emulator: 123 | base_adb_command.append('-e') 124 | 125 | if args.current_app: 126 | system_dump_command = base_adb_command + ["shell", "dumpsys", "activity", "activities"] 127 | system_dump = subprocess.Popen(system_dump_command, stdout=PIPE, stderr=PIPE).communicate()[0] 128 | running_package_name = re.search(".*TaskRecord.*A[= ]([^ ^}]*)", system_dump).group(1) 129 | self.package_name.append(running_package_name) 130 | 131 | if len(self.package_name) == 0: 132 | self.all = True 133 | 134 | # Store the names of packages for which to match all processes. 135 | self.catchall_package = list(filter(lambda package: package.find(":") == -1, self.package_name)) 136 | # Store the name of processes to match exactly. 137 | named_processes = filter(lambda package: package.find(":") != -1, self.package_name) 138 | # Convert default process names from : (cli notation) to (android notation) in the exact names match group. 139 | self.named_processes = map(lambda package: package if package.find(":") != len(package) - 1 else package[:-1], 140 | named_processes) 141 | 142 | self.header_size = args.tag_width + 1 + 3 + 1 # space, level, space 143 | 144 | # Only enable GC coloring if the user opted-in 145 | if args.color_gc: 146 | # GC_CONCURRENT freed 3617K, 29% free 20525K/28648K, paused 4ms+5ms, total 85ms 147 | key = re.compile( 148 | r'^(GC_(?:CONCURRENT|FOR_M?ALLOC|EXTERNAL_ALLOC|EXPLICIT) )(freed