Package: slack-logger-python
Slack Logger Python - A Slack Logger for Python LoggingA python logging implementation to send messages to Slack.
Design goals:Use Python logging interface such that it can be integrated with standard logging tools.Fully customizable messages with full control over message layout and design.Simple authentication via webhook.Powerful filtering to filter e.g. for environments or particular values only known at runtime.Easy usage to cover most use cases when slack is used for basic alerting and automatic traceback visualization.To achieve aforementioned goals, the library provides logging handler, formatter and filter implementations.Getting StartedInstall withpip install slack-logger-python.Now, you can use theslack_loggermodule in your python code.Basic Example with Plain MessagesThis basic examples shows the usage and the implementation of design goal (1) and (3).fromslack_loggerimportSlackFormatter,SlackHandlerlogger=logging.getLogger(__name__)formatter=SlackFormatter.plain()# plain message, no decorationshandler=SlackHandler.from_webhook(os.environ["SLACK_WEBHOOK"])handler.setFormatter(formatter)handler.setLevel(logging.WARN)logger.addHandler(handler)logger.info("I won't appear.")logger.warning("I will show up.")logger.error("Mee too.")Visually Appealing MessagesYou can use theSlackFormatter.minimal()andSlackFormatter.default()formatter for more visually appealing log messages.
For now, those require a configuration to show e.g. the header.
Extra fields are shown in blocks at the bottom of the message and can be dynamically added at runtime.
Everything else stays the same:fromslack_loggerimportFormatConfig,SlackFormatter,SlackHandlerlogger=logging.getLogger(__name__)format_config=FormatConfig(service="testrunner",environment="test",extra_fields={"foo":"bar"})formatter=SlackFormatter.default(format_config)handler=SlackHandler.from_webhook(os.environ["SLACK_WEBHOOK"])handler.setFormatter(formatter)handler.setLevel(logging.WARN)logger.addHandler(handler)logger.info("I won't appear.")logger.warning("I will show up.")logger.error("Mee too.")Adding extra fields on single log message is achieved by just putting it in the extra fields of the logging interface:logger.warning("I will show up.",extra={"extra_fields":{"foo":"baba"}})CustomizationTo do basic customizations, you can provide a configuration to theSlackFormatter:service:Optional[str]=Noneenvironment:Optional[str]=Nonecontext:List[str]=[]emojis:Dict[int,str]=DEFAULT_EMOJISextra_fields:Dict[str,str]={}Let's look at an example error log from a division by zero error.
Given the following code snippet with a configuration and the default formatter:importosfromslack_loggerimportFormatConfig,SlackFormatter,SlackHandlerformat_config=FormatConfig(service="testrunner",environment="test",extra_fields={"foo":"bar","raven":"caw"})slack_handler=SlackHandler.from_webhook(os.environ["SLACK_WEBHOOK"])formatter=SlackFormatter.default(format_config)slack_handler.setFormatter(formatter)log.addHandler(slack_handler)try:1/0exceptException:log.exception("Something terrible happened!")We will get the following error message in slack:It contains a header, a context, a body and a section containing extra fields.
Let's identify those in the image above.The header is composed of:Log level emoji: ❌Log level name: ERRORService name: testrunnerThe context contains:Environment: testService name: testrunnerThe body includes:The log error message: "Something terrible happened!"The Traceback errorExtra fields:Field "foo" with value "bar"Field "raven" with value "caw"Design Message - Design Principal (3)Messages are fully customizable using slacks block layout, seeCreating rich message layoutsandReference: Layout blocks.
By implementing theMessageDesigninterface, you can fully control in the message design, which requires you to implement a functionformat_blocks(record: LogRecord) -> Sequence[Optional[Block]]to transform aLogRecordinto a sequence of slack message blocks.
Of course, you can add configurations and helper functions as well.Let's create our own warning message.
This demonstrates the usage and the implementation of design goal (2).importattrs# for convenience, but not requiredfromslack_sdk.models.blocksimportBlock,DividerBlock,HeaderBlock,SectionBlockfromslack_loggerimportSlackFormatter@defineclassCustomDesign(MessageDesign):defformat_blocks(self,record:LogRecord)->Sequence[Optional[Block]]:level=record.levelnamemessage=record.getMessage()blocks:Sequence[Block]=[HeaderBlock(text=PlainTextObject(text=f"{level}{message}")),DividerBlock(),SectionBlock(text=MarkdownTextObject(text=message)),]returnblocksformatter=SlackFormatter(design=CustomDesign())Provide your own set of emojisTo the default emoji set is a defined by the following dict:DEFAULT_EMOJIS={logging.CRITICAL:":fire:",# 🔥logging.ERROR:":x:",# ❌logging.FATAL:":x:",# ❌logging.WARNING:":warning:",# ⚠️logging.WARN:":warning:",# ⚠️logging.INFO:":bell:",# 🔔logging.DEBUG:":microscope:",# 🔬logging.NOTSET:":mega:",# 📣}You can import and overwrite it partially - or you can define a complete new set of emoji.
The following example demonstrates how you can add the emoji set to theSlackFormatter:fromslack_loggerimportFormatConfig,SlackFormattermy_emojis={logging.CRITICAL:":x:",# ❌logging.ERROR:":x:",# ❌logging.FATAL:":x:",# ❌logging.WARNING:":bell:",# 🔔logging.WARN:":bell:",# 🔔logging.INFO:":mega:",# 📣logging.DEBUG:":mega:",# 📣logging.NOTSET:":mega:",# 📣}config=FormatConfig(service="testrunner",environment="test",emojis=my_emojis)formatter=SlackFormatter.default(config)Filter MessageFilters implement the logging interface ofFilters.
They are designed to work as a companion to aLogFormatter, as it can filter on the formatters config.
A message is logged if a filter is matched successfully.
Design goal (4) and (5) are partially demonstrated.Here is a quick example:fromslack_loggerimportFilterConfig,SlackFilter,SlackHandlerimportosimportlogginglogger=logging.getLogger("ProdFilter")# Allow only logs from `prod` environmentfilter=SlackFilter(config=FilterConfig(environment="prod"),filterType=FilterType.AnyAllowList)slack_handler.addFilter(filter)logger.addHandler(slack_handler)# When the ENV enviroment variable is set to prod, the message will be send.# Otherwise, the message is filtered out and not send (e.g. if ENV is `dev`)logger.warning(f"{log_msg}in some environment and allow listed prod",extra={"filter":{"environment":os.getenv("ENV","dev")}})# Will be filteredlogger.warning(f"{log_msg}in dev environment and allow listed prod",extra={"filter":{"environment":"dev"}})# Will be sendlogger.warning(f"{log_msg}in dev environment and allow listed prod",extra={"filter":{"environment":"prod"}})Note that we used the"filter"property in theextrafield option here to inject a config, as we don't use aSlackFormatter.
You can think of it as a reserved word.
Thison-the-flyconfigurations allow to specify properties on messages to alter the filter behavior for a single log message.There are 4 different types of filter lists:FilterType.AnyAllowList: Pass filter if any of the provided conditions are met.FilterType.AllAllowList: Pass filter only if all provided conditions are met.FilterType.AnyDenyList: Pass filter if no condition is met and deny if any condition is met.FilterType.AllDenyList: Pass filter if any condition is met and deny if all conditions are met.Combining multiple filters.It is important to note that as soon a message does not meet any filter condition, it is filtered out and won't appear in the logs, as it is simply the overlap of all filter conditions.
Therefore it is not possible to allow a denied message afterwards.
Furthermore, the order of filters do not matter.The composition of configurations, filters and dynamic extra fields allow for a flexible way of specifying your message content and filter unwanted messages.More examples can be found it thetests folder.