├── .editorconfig ├── .gitignore ├── HOWTO_PIP.md ├── LICENSE ├── MANIFEST.in ├── README.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── slacker_log_handler └── __init__.py └── test.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 2 10 | end_of_line = lf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [*.py] 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | venv/ 57 | 58 | # IDEA 59 | .idea/ 60 | *.iml -------------------------------------------------------------------------------- /HOWTO_PIP.md: -------------------------------------------------------------------------------- 1 | How to put a new version on pypi.python.org 2 | =========================================== 3 | 4 | So that the package maintainer won't have to google it every time. 5 | 6 | * http://peterdowns.com/posts/first-time-with-pypi.html 7 | 8 | 1. git checkout master 9 | 2. git pull 10 | 3. Update version number in setup.py 11 | 4. git add setup.py 12 | 5. git commit 13 | 6. git push 14 | 7. git tag X.X.X -m "vX.X.X" 15 | 8. git push --tags 16 | 9. python setup.py sdist upload -r pypitest 17 | 10. Check that things look right on https://test.pypi.org/project/slacker_log_handler/ 18 | 11. python setup.py sdist upload -r pypi 19 | 12. Check that things look right on https://pypi.org/project/slacker-log-handler/ 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the 13 | copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other 16 | entities that control, are controlled by, or are under common control with 17 | that entity. For the purposes of this definition, "control" means (i) the 18 | power, direct or indirect, to cause the direction or management of such 19 | entity, whether by contract or otherwise, or (ii) ownership of 20 | fifty percent (50%) or more of the outstanding shares, or (iii) beneficial 21 | ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity exercising 24 | permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation source, 28 | and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical transformation 31 | or translation of a Source form, including but not limited to compiled 32 | object code, generated documentation, and conversions to 33 | other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or Object 36 | form, made available under the License, as indicated by a copyright notice 37 | that is included in or attached to the work (an example is provided in the 38 | Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object form, 41 | that is based on (or derived from) the Work and for which the editorial 42 | revisions, annotations, elaborations, or other modifications represent, 43 | as a whole, an original work of authorship. For the purposes of this 44 | License, Derivative Works shall not include works that remain separable 45 | from, or merely link (or bind by name) to the interfaces of, the Work and 46 | Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including the original 49 | version of the Work and any modifications or additions to that Work or 50 | Derivative Works thereof, that is intentionally submitted to Licensor for 51 | inclusion in the Work by the copyright owner or by an individual or 52 | Legal Entity authorized to submit on behalf of the copyright owner. 53 | For the purposes of this definition, "submitted" means any form of 54 | electronic, verbal, or written communication sent to the Licensor or its 55 | representatives, including but not limited to communication on electronic 56 | mailing lists, source code control systems, and issue tracking systems 57 | that are managed by, or on behalf of, the Licensor for the purpose of 58 | discussing and improving the Work, but excluding communication that is 59 | conspicuously marked or otherwise designated in writing by the copyright 60 | owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity on 63 | behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. 67 | 68 | Subject to the terms and conditions of this License, each Contributor 69 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 70 | royalty-free, irrevocable copyright license to reproduce, prepare 71 | Derivative Works of, publicly display, publicly perform, sublicense, 72 | and distribute the Work and such Derivative Works in 73 | Source or Object form. 74 | 75 | 3. Grant of Patent License. 76 | 77 | Subject to the terms and conditions of this License, each Contributor 78 | hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, 79 | royalty-free, irrevocable (except as stated in this section) patent 80 | license to make, have made, use, offer to sell, sell, import, and 81 | otherwise transfer the Work, where such license applies only to those 82 | patent claims licensable by such Contributor that are necessarily 83 | infringed by their Contribution(s) alone or by combination of their 84 | Contribution(s) with the Work to which such Contribution(s) was submitted. 85 | If You institute patent litigation against any entity (including a 86 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 87 | Contribution incorporated within the Work constitutes direct or 88 | contributory patent infringement, then any patent licenses granted to 89 | You under this License for that Work shall terminate as of the date such 90 | litigation is filed. 91 | 92 | 4. Redistribution. 93 | 94 | You may reproduce and distribute copies of the Work or Derivative Works 95 | thereof in any medium, with or without modifications, and in Source or 96 | Object form, provided that You meet the following conditions: 97 | 98 | 1. You must give any other recipients of the Work or Derivative Works a 99 | copy of this License; and 100 | 101 | 2. You must cause any modified files to carry prominent notices stating 102 | that You changed the files; and 103 | 104 | 3. You must retain, in the Source form of any Derivative Works that You 105 | distribute, all copyright, patent, trademark, and attribution notices from 106 | the Source form of the Work, excluding those notices that do not pertain 107 | to any part of the Derivative Works; and 108 | 109 | 4. If the Work includes a "NOTICE" text file as part of its distribution, 110 | then any Derivative Works that You distribute must include a readable copy 111 | of the attribution notices contained within such NOTICE file, excluding 112 | those notices that do not pertain to any part of the Derivative Works, 113 | in at least one of the following places: within a NOTICE text file 114 | distributed as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, within a 116 | display generated by the Derivative Works, if and wherever such 117 | third-party notices normally appear. The contents of the NOTICE file are 118 | for informational purposes only and do not modify the License. 119 | You may add Your own attribution notices within Derivative Works that You 120 | distribute, alongside or as an addendum to the NOTICE text from the Work, 121 | provided 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 may 125 | provide additional or different license terms and conditions for use, 126 | reproduction, or distribution of Your modifications, or for any such 127 | Derivative Works as a whole, provided Your use, reproduction, and 128 | distribution of the Work otherwise complies with the conditions 129 | stated in this License. 130 | 131 | 5. Submission of Contributions. 132 | 133 | Unless You explicitly state otherwise, any Contribution intentionally 134 | submitted for inclusion in the Work by You to the Licensor shall be under 135 | the terms and conditions of this License, without any additional 136 | terms or conditions. Notwithstanding the above, nothing herein shall 137 | supersede or modify the terms of any separate license agreement you may 138 | have executed with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. 141 | 142 | This License does not grant permission to use the trade names, trademarks, 143 | service marks, or product names of the Licensor, except as required for 144 | reasonable and customary use in describing the origin of the Work and 145 | reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. 148 | 149 | Unless required by applicable law or agreed to in writing, Licensor 150 | provides the Work (and each Contributor provides its Contributions) 151 | on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 152 | either express or implied, including, without limitation, any warranties 153 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS 154 | FOR A PARTICULAR PURPOSE. You are solely responsible for determining the 155 | appropriateness of using or redistributing the Work and assume any risks 156 | associated with Your exercise of permissions under this License. 157 | 158 | 8. Limitation of Liability. 159 | 160 | In no event and under no legal theory, whether in tort 161 | (including negligence), contract, or otherwise, unless required by 162 | applicable law (such as deliberate and grossly negligent acts) or agreed 163 | to in writing, shall any Contributor be liable to You for damages, 164 | including any direct, indirect, special, incidental, or consequential 165 | damages of any character arising as a result of this License or out of 166 | the use or inability to use the Work (including but not limited to damages 167 | for loss of goodwill, work stoppage, computer failure or malfunction, 168 | or any and all other commercial damages or losses), even if such 169 | Contributor has been advised of the possibility of such damages. 170 | 171 | 9. Accepting Warranty or Additional Liability. 172 | 173 | While redistributing the Work or Derivative Works thereof, You may choose 174 | to offer, and charge a fee for, acceptance of support, warranty, 175 | indemnity, or other liability obligations and/or rights consistent with 176 | this License. However, in accepting such obligations, You may act only 177 | on Your own behalf and on Your sole responsibility, not on behalf of any 178 | other Contributor, and only if You agree to indemnify, defend, and hold 179 | each Contributor harmless for any liability incurred by, or claims 180 | asserted against, such Contributor by reason of your accepting any such 181 | warranty or additional liability. 182 | 183 | END OF TERMS AND CONDITIONS 184 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.py *.txt *.md *.rst LICENSE 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | slacker_log_handler 2 | =================== 3 | 4 | .. image:: https://img.shields.io/pypi/v/slacker_log_handler.svg?style=flat-square 5 | :target: https://pypi.python.org/pypi/slacker_log_handler 6 | 7 | .. image:: https://img.shields.io/pypi/wheel/slacker_log_handler.svg?style=flat-square 8 | :target: https://pypi.python.org/pypi/slacker_log_handler 9 | 10 | .. image:: https://img.shields.io/pypi/format/slacker_log_handler.svg?style=flat-square 11 | :target: https://pypi.python.org/pypi/slacker_log_handler 12 | 13 | .. image:: https://img.shields.io/pypi/pyversions/slacker_log_handler.svg?style=flat-square 14 | :target: https://pypi.python.org/pypi/slacker_log_handler 15 | 16 | .. image:: https://img.shields.io/pypi/status/slacker_log_handler.svg?style=flat-square 17 | :target: https://pypi.python.org/pypi/slacker_log_handler 18 | 19 | Python log handler that posts to a Slack channel. Posts to the Slack API 20 | using https://github.com/os/slacker. 21 | 22 | For a different implementation using webhooks instead of Slacker, see 23 | https://github.com/claudetech/python-slack-log or read 24 | http://www.pythian.com/blog/logging-for-slackers/ 25 | 26 | Created with the intention of using for a Django project, but some 27 | effort has been made to make it generic enough that any Python project 28 | could use it. 29 | 30 | Installation 31 | ------------ 32 | 33 | .. code-block:: bash 34 | 35 | pip install slacker-log-handler 36 | 37 | Options 38 | ------- 39 | 40 | api_key (required) 41 | ~~~~~~~~~~~~~~~~~~ 42 | 43 | Generate a key at https://api.slack.com/ 44 | 45 | channel (required) 46 | ~~~~~~~~~~~~~~~~~~ 47 | 48 | Set which channel you want to post to, e.g. "#general". 49 | 50 | username 51 | ~~~~~~~~ 52 | 53 | The username that will post to Slack. Defaults to "Python logger". 54 | 55 | icon_url 56 | ~~~~~~~~ 57 | 58 | URL to an image to use as the icon for the logger user 59 | 60 | icon_emoji 61 | ~~~~~~~~~~ 62 | 63 | emoji to use as the icon. Overrides icon_url. If neither icon_url nor 64 | icon_emoji is set, :heavy_exclamation_mark: will be used. 65 | 66 | fail_silent 67 | ~~~~~~~~~~~ 68 | Defaults to False. 69 | If your API key is invalid or for some other reason the API call returns an error, 70 | this option will silently ignore the API error. 71 | If you enable this setting, **make sure you have another log handler** that will also handle the same log events, 72 | or they may be lost entirely. 73 | 74 | 75 | Django configuration 76 | -------------------- 77 | Logging reference: https://docs.djangoproject.com/en/stable/topics/logging/ 78 | 79 | This example will send INFO and ERRORS to Slack, as well as errors to admin emails. 80 | 81 | - Set ``SLACK_API_KEY`` in your settings module. 82 | 83 | .. code-block:: python 84 | 85 | LOGGING = { 86 | 'version': 1, 87 | 'disable_existing_loggers': False, 88 | 'filters': { 89 | 'require_debug_false': { 90 | '()': 'django.utils.log.RequireDebugFalse' 91 | } 92 | }, 93 | 'handlers': { 94 | 'mail_admins': { 95 | 'level': 'ERROR', 96 | 'filters': ['require_debug_false'], 97 | 'class': 'django.utils.log.AdminEmailHandler' 98 | }, 99 | 'slack-error': { 100 | 'level': 'ERROR', 101 | 'api_key': SLACK_API_KEY, 102 | 'class': 'slacker_log_handler.SlackerLogHandler', 103 | 'channel': '#general' 104 | }, 105 | 'slack-info': { 106 | 'level': 'INFO', 107 | 'api_key': SLACK_API_KEY, 108 | 'class': 'slacker_log_handler.SlackerLogHandler', 109 | 'channel': '#general' 110 | }, 111 | 'loggers': { 112 | 'django.request': { 113 | 'handlers': ['mail_admins', 'slack-error', 'slack-info'], 114 | 'level': 'ERROR', 115 | 'propagate': True, 116 | }, 117 | } 118 | } 119 | } 120 | 121 | Example Python logging handler 122 | ------------------------------ 123 | 124 | This is how you use `slacker_log_handler` as a regular Python logging handler. 125 | This example will send a error message to a slack channel. 126 | 127 | .. code-block:: python 128 | 129 | import logging 130 | from slacker_log_handler import SlackerLogHandler, NoStacktraceFormatter 131 | 132 | # Create slack handler 133 | slack_handler = SlackerLogHandler('my-channel-token', 'my-channel-name', stack_trace=True) 134 | 135 | # Create logger 136 | logger = logging.getLogger('debug_application') 137 | logger.addHandler(slack_handler) 138 | 139 | # OPTIONAL: Define a log message formatter. 140 | # If you have set stack_trace=True, any exception stack traces will be included as Slack message attachments. 141 | # You therefore need to use NoStacktraceFormatter as a base to exclude the trace from the main message text. 142 | formatter = NoStacktraceFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 143 | slack_handler.setFormatter(formatter) 144 | 145 | # Define the minimum level of log messages you want to send to Slack 146 | slack_handler.setLevel(logging.DEBUG) 147 | 148 | # Test logging 149 | logger.error("Debug message from slack!") 150 | 151 | Slack message formatting 152 | ------------------------ 153 | 154 | This example use a subclass that will send a formatted message to a slack channel. 155 | Reference: https://api.slack.com/docs/message-formatting 156 | 157 | .. code-block:: python 158 | 159 | class CustomLogHandler(SlackerLogHandler): 160 | def build_msg(self, record): 161 | message = "> New message :\n" + record.getMessage() 162 | return message 163 | 164 | License 165 | ------- 166 | 167 | Apache 2.0 168 | 169 | Slacker is also under Apache 2.0. 170 | 171 | See also: https://api.slack.com/terms-of-service 172 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | slacker>=0.7.3,<=0.9.65 2 | six>=1.11.0 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | from setuptools import setup 5 | 6 | VERSION = '1.8.0' 7 | 8 | 9 | def readme(*paths): 10 | with open(os.path.join(*paths), 'r') as f: 11 | return f.read() 12 | 13 | 14 | def requirements(*paths): 15 | with open(os.path.join(*paths), 'r') as f: 16 | return list(line.strip() for line in f.readlines() if line.strip() != '') 17 | 18 | 19 | setup( 20 | name='slacker_log_handler', 21 | packages=['slacker_log_handler'], 22 | version=VERSION, 23 | description='Posts log events to Slack via API', 24 | long_description=readme('README.rst'), 25 | classifiers=[ 26 | 'Development Status :: 5 - Production/Stable', 27 | 'Environment :: Web Environment', 28 | 'Framework :: Django', 29 | 'Intended Audience :: Developers', 30 | 'Intended Audience :: System Administrators', 31 | 'License :: OSI Approved :: Apache Software License', 32 | 'Natural Language :: English', 33 | 'Operating System :: OS Independent', 34 | 'Programming Language :: Python', 35 | 'Programming Language :: Python :: 2', 36 | 'Programming Language :: Python :: 3', 37 | 'Topic :: Communications :: Chat', 38 | 'Topic :: Office/Business :: Groupware', 39 | ], 40 | url='https://github.com/mathiasose/slacker_log_handler', 41 | download_url='https://github.com/mathiasose/slacker_log_handler/archive/{v}.tar.gz'.format(v=VERSION), 42 | author='Mathias Ose', 43 | author_email='mathias.ose@gmail.com', 44 | keywords=['slack', 'logging'], 45 | install_requires=requirements('requirements.txt'), 46 | include_package_data=True, 47 | zip_safe=False 48 | ) 49 | -------------------------------------------------------------------------------- /slacker_log_handler/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | import traceback 3 | from logging import Handler, CRITICAL, ERROR, WARNING, INFO, FATAL, DEBUG, NOTSET, Formatter 4 | 5 | import six 6 | import slacker 7 | 8 | ERROR_COLOR = 'danger' # color name is built in to Slack API 9 | WARNING_COLOR = 'warning' # color name is built in to Slack API 10 | INFO_COLOR = '#439FE0' 11 | 12 | COLORS = { 13 | CRITICAL: ERROR_COLOR, 14 | FATAL: ERROR_COLOR, 15 | ERROR: ERROR_COLOR, 16 | WARNING: WARNING_COLOR, 17 | INFO: INFO_COLOR, 18 | DEBUG: INFO_COLOR, 19 | NOTSET: INFO_COLOR, 20 | } 21 | 22 | DEFAULT_EMOJI = ':heavy_exclamation_mark:' 23 | 24 | 25 | class NoStacktraceFormatter(Formatter): 26 | """ 27 | By default the stacktrace will be formatted as part of the message. 28 | Since we want the stacktrace to be in the attachment of the Slack message, 29 | we need a custom formatter to leave it out of the message 30 | """ 31 | 32 | def formatException(self, ei): 33 | return None 34 | 35 | def format(self, record): 36 | # Work-around for https://bugs.python.org/issue29056 37 | saved_exc_text = record.exc_text 38 | record.exc_text = None 39 | try: 40 | return super(NoStacktraceFormatter, self).format(record) 41 | finally: 42 | record.exc_text = saved_exc_text 43 | 44 | 45 | class SlackerLogHandler(Handler): 46 | def __init__(self, api_key, channel, stack_trace=True, username='Python logger', icon_url=None, icon_emoji=None, 47 | fail_silent=False, ping_users=None, ping_level=None): 48 | Handler.__init__(self) 49 | self.formatter = NoStacktraceFormatter() 50 | 51 | self.stack_trace = stack_trace 52 | self.fail_silent = fail_silent 53 | 54 | self.slacker = slacker.Slacker(api_key) 55 | 56 | self.username = username 57 | self.icon_url = icon_url 58 | self.icon_emoji = icon_emoji if (icon_emoji or icon_url) else DEFAULT_EMOJI 59 | self.channel = channel 60 | if not self.channel.startswith('#') and not self.channel.startswith('@'): 61 | self.channel = '#' + self.channel 62 | 63 | self.ping_level = ping_level 64 | self.ping_users = [] 65 | 66 | if ping_users: 67 | user_list = self.slacker.users.list().body['members'] 68 | 69 | for ping_user in ping_users: 70 | ping_user = ping_user.lstrip('@') 71 | 72 | for user in user_list: 73 | if user['name'] == ping_user: 74 | self.ping_users.append(user['id']) 75 | break 76 | else: 77 | raise RuntimeError('User not found in Slack users list: %s' % ping_user) 78 | 79 | 80 | 81 | def build_msg(self, record): 82 | return six.text_type(self.format(record)) 83 | 84 | def build_trace(self, record, fallback): 85 | trace = { 86 | 'fallback': fallback, 87 | 'color': COLORS.get(self.level, INFO_COLOR) 88 | } 89 | 90 | if record.exc_info: 91 | trace['text'] = '\n'.join(traceback.format_exception(*record.exc_info)) 92 | 93 | return trace 94 | 95 | def emit(self, record): 96 | message = self.build_msg(record) 97 | 98 | if self.ping_users and record.levelno >= self.ping_level: 99 | for user in self.ping_users: 100 | message = '<@%s> %s' % (user, message) 101 | 102 | if self.stack_trace: 103 | trace = self.build_trace(record, fallback=message) 104 | attachments = json.dumps([trace]) 105 | else: 106 | attachments = None 107 | 108 | try: 109 | self.slacker.chat.post_message( 110 | text=message, 111 | channel=self.channel, 112 | username=self.username, 113 | icon_url=self.icon_url, 114 | icon_emoji=self.icon_emoji, 115 | attachments=attachments, 116 | ) 117 | except slacker.Error as e: 118 | if self.fail_silent: 119 | pass 120 | else: 121 | raise e 122 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from platform import python_version 4 | 5 | from slacker_log_handler import SlackerLogHandler, NoStacktraceFormatter 6 | 7 | SLACK_API_TOKEN = os.getenv('SLACK_API_TOKEN') 8 | SLACK_CHANNEL = os.getenv('SLACK_CHANNEL') 9 | 10 | slack_handler = SlackerLogHandler(SLACK_API_TOKEN, SLACK_CHANNEL, stack_trace=True, ping_users=["@ose", "slackbot"], ping_level=logging.ERROR) 11 | 12 | logger = logging.getLogger('debug_application') 13 | logger.addHandler(slack_handler) 14 | logger.setLevel(logging.DEBUG) 15 | 16 | formatter = NoStacktraceFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 17 | slack_handler.setFormatter(formatter) 18 | 19 | logger.info('Python version is {}'.format(python_version())) 20 | 21 | logger.debug('Test DEBUG') 22 | logger.info('Test INFO') 23 | logger.warning('Test WARNING') 24 | logger.error('Test ERROR') 25 | logger.fatal('Test FATAL') 26 | logger.critical('Test CRITICAL') 27 | 28 | try: 29 | raise Exception('Test exception') 30 | except Exception as e: 31 | logger.exception(e) 32 | --------------------------------------------------------------------------------