Package: aws-teams-logger
AWS Teams LoggerAWS Teams Logger is a Python library that forwards errors (failures) and log
messages to a MS Teams channel, and an optional list ofDeveloper Emailswho may need to be notified; the emails use HTML formatting which is
originally designed for MS Outlook and Teams. This library is primarily intended
for use in an AWS environment.This package exposes the below decorator classes which handle the posting
ofwarninglevel or above log messages by default (via the builtinloggingmodule)
to a specified Microsoft Teams channel. Log messages with anexc_infoparameter,
as well as any uncaught exceptions, will also be sent to a list of Dev Emails (if provided)
in addition to being logged to the Teams channel.Available Decorators:LambdaLogger- The main use case is to decorate a handler for
an AWS Lambda function in the Python runtime. For decorating multiple lambda handlers,
it is useful to invoke the helper methoddecorate_all_functions.TaskLogger- Intended to be run in ECS environment, such as
functions (or class methods) that are run in an ECS or Fargate task.
However, this can also be used to decorate any generic functions as well.
Note that the task needs to be usingplatform version 1.4(see thenote belowfor more details)BulkLambdaLogger,BulkTaskLogger- The decorator classes that start withBulkare functionally identical to their above counterparts, but prefer to send emails in bulk where
possible. Use this implementation when it is expected that multiple logs will be sent to Teams
or Outlook, as there will be a performance increase with aBulklogger.
See the below section onBulk Loggersfor more info.UsageSimple Usage for a stand-alone AWS Lambda function:fromaws_teams_loggerimportLambdaLoggerfromloggingimportgetLoggerfromtypingimportDict,Anylog=getLogger(__name__)log.setLevel('INFO')# By default, only logs from this library at the 'WARNING' level and above# should show up in CloudWatch. This specifies the minimum log level at which# library logs appear in CloudWatch.# getLogger('aws_teams_logger').setLevel('INFO')# Note: this is a simplified example, and assumes you define the required# environment variables. Otherwise, you'd need to pass the parameters# to the decorator class like `@LambdaLogger(teams_email='my-teams-email')# in this case.@LambdaLoggerdefmy_lambda_handler(event:Dict[str,Any],context:Any):...other_func()...defother_func():# Message is not logged to Teams (default log level is "WARN")log.info('Info level log')# This message will be forwarded to Teamslog.warning('Sample Warn message')# Messages with the `exc_info` parameter will be logged to both# the Teams Channel and any subscribed Dev Emails via Outlooktry:empty_dict={}value=empty_dict['missing key']exceptKeyError:log.exception('Key missing from `empty_dict`')# Uncaught errors will be logged to both Teams and any Dev Emailsfinally:result=1/0... or for a module with multiple handlers, common for serverless projects with
multiple Lambdas:fromaws_teams_loggerimportLambdaLoggerfromloggingimportgetLoggerlog=getLogger()ll=LambdaLogger(enabled_lvl='WARNING')defmy_handler_1(event,context):# This message won't be sent to Teams, as the minimum lvl here is 'WARNING'log.info('Hello world!')# Build a custom Exception object - a `ValueError` in this case -# which will be forwarded to Teams and also any Devs via emailerr=ValueError('My sample error details.')log.error('This is an error log with a sample exception object',exc_info=err)defmy_handler_2(event,context):# This message will be sent to Teams by default, though this behavior# can be overridden via the 'enabled_lvl' parameter above, or alternatively# via the 'TEAMS_LOG_LVL' environment variable defined for the lambda.log.warning('Help! something went wrong')# Now, decorate all the lambda handlers declared abovell.decorate_all_functions(ses_identity='my@identity.org',teams_email='abc123.my.domain@amer.teams.ms',dev_emails='user1@my.domain.org,user2@mydomain.org')... or use it to decorate one or more ECS tasks:importloggingfromaws_teams_loggerimportTaskLoggerlog=logging.getLogger(__name__)# Logs from this library won't show up by default. This will enable reporting# of logs above a specified level. You can alternatively define the 'LOG_CFG'# environment variable, which does the same thing.logging.basicConfig(level='WARNING')classMyTaskClass:@classmethod@TaskLoggerdefmy_task_func(cls,*args,**kwargs):...cls.other_func()...@staticmethoddefother_func():# TODO add logic here; see examples for logging above...... when multiple emails will be sent using the same template,
it might be more performant to use a Bulk Logger as shown below:fromaws_teams_loggerimportBulkLambdaLoggerfromloggingimportgetLoggerlog=getLogger()@BulkLambdaLoggerdefmy_lambda_handler(event,context):log.info("This%smessage shouldn't be sent via email",'Info')foriinrange(5):log.error('Testing%d...',i+1)...Installing Teams Logger and Supported VersionsAWS Teams Logger is available on PyPI:pipinstallaws-teams-loggerThis package officially supportsPython 3.7or higher.ScreenshotsCheck out thedocs/imagesfolder for screenshots
of sample email messages as they show up in MS Teams and Outlook.AboutThis library decorates (overrides) the base logger methods in theloggingmodule to also
post log messages above a set level to a Teams channel.The library also posts to target destinations via email currently; it doesn't currently
support Teams connectors. It usesAmazon SESto  format the error and log messages that will be sent to MS Teams and Outlook.
Therefore, you will first need to set up SES and allow permissions for the
decorated function to send emails via the SES service, as explained below.Conditional DependenciesNote thatboto3is not included in package requirements by default,
since an AWS Lambda environment is assumed.If running on a Docker environment such as ECS or Fargate, you
can use the following to ensure that all dependencies are installed:pipinstallaws-teams-logger[standalone]Initial SetupSESAdd Required TemplatesThe library uses SES templates to format email messages sent to MS Teams and Outlook.Use the below helper function to add the required templates to the desired AWS account:fromaws_teams_loggerimportupload_templatesupload_templates(profile_name='my-aws-profile')Set up an SES IdentityAdd and verify an identity from the Amazon SES console under your
account. For testing purposes, it might be preferable to use a personal email address
as it'll be easier to verify it.Turn off Sandbox ModeEnableProduction Accessfrom the SES console. This will enable
us to send outbound emails without needing to verify each recipient email
separately.TeamsUse the steps below to find the email address for the MS teams channel to stream
the logs to:Click on thethree dotsto the right of the channel nameSelect 'Get email address'Here we only need to use the email address.For example, givenChannel name <abc123.my.domain@amer.teams.ms>we only need the value that is enclosed in brackets.PermissionsThe execution IAM role will need the following minimum permissions:[{"Effect":"Allow","Action":["ses:Send*Email"],"Resource":"*"},{"Effect":"Allow","Action":"iam:ListAccountAliases","Resource":"*"}]Note that if you set the AWS account name (either via theset_account_namefunction or
theAWS_ACCOUNT_NAMEenvironment variable) then you don't need to add the second
permission to retrieve the account alias from IAM.Example of setting the AWS account name:fromaws_teams_loggerimportset_account_nameset_account_name('my-account-alias')Required ValuesThe library requires certain values that need to either be passed in as parameters to the decorators
(or alternatively to thedecorate_all_functionsmethod for lambda functions),
or which need to be present in the environment.The following minimum values are required to use the decorator methods (otherwise
it should log an error):SES Identity- Sender email address that was previously validated with SES.MS Teams Email- The email address (ex:abc123.my.domain@amer.teams.ms) to send
log messages and unhandled errors to.ParametersThe resolution logic for parameters will use the environment variables
first if they are available.The following table lists the parameter names as used with the decorators,
along with the environment variables that will be used if provided.
Note that the resolution order is from left to right.Env VariableParameterExampleDescriptionSES_IDENTITYses_identitysender@my.domain.orgSender or outbound email, must be validated in the SES consoleTEAMS_EMAILteams_emailabc123.my.domain@amer.teams.msThe email to send log messages or failures (e.g. uncaught exceptions) toTEAMS_LOG_LVLenabled_lvlWARNINGThe minimum log level for messages sent to TeamsDEV_EMAILSdev_emailsuser1@my.domain.org,user2@my.domain.orgComma delimited list of dev emails, if provided will send stylized HTML to them when any uncaught exceptions are raisedAWS_ACCOUNT_NAMEset_account_name()my-account-devAWS Account Alias. If defined, will be used instead of making a call toiam:ListAccountAliasesto retrieve the alias of the current AWS account.SOURCE_CODE-https://github.com/abc/repoA link to the source code repo (such as Bitbucket) for the project.AWS_LOG_GROUP-my-cw-log-groupOnly applies when theTaskLoggerdecorator is used. Determines the log group to link to in the AWS console. Generally this is not needed to be specified (see note on 'ECS Tasks' below)AWS_REGION-us-east-1AWS Region, should be automatically set for AWS Lambda functions. Determines the region in which to invoke the SES service, as well as the default region for Lambda and Task contexts.LOG_CFG-INFOIf specified, sets up logging vialogging.basicConfig. Also determines the minimum level at which messages logged by this library show up in CloudWatch. If this is a valid path to a file, the contents will be passed tologging.config.dictConfiginstead.LOCAL_TZ-US/EasternUser's local time zone, passed in topytz.timezone; used when generating the date/time in the subject for a Teams or Devs email message.Advanced ConfigThe decorator parameterslogger_clsandlog_func_nametogether determine the base log
function to decorate.By default, we decorate thelogging.Logger._logmethod, which is the base method used by all logger methods in the Pythonlogginglibrary.Note on ECS TasksThe decorator retrieves ECS metadata on the currently running task
when posting messages to Teams or via email. It will automatically
retrieve data from the Task Metadata V4 endpoint, if this is
available.To enable correct data reporting for the the V4 endpoint,
set thePlatform Versionfor the task to1.4.0or higher as
mentioned in the article below.https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-metadata-endpoint-v4-fargate.htmlBulk LoggersTheBulklogger implementations will send templated emails in bulk,
e.g. via theses:SendBulkTemplatedEmailAPI call. Use this
implementation when it is expected that multiple logs will be sent via
the same SES template, as there will be a performance increase when using aBulklogger. Note that there is a separate template for
sending a message to Teams and Outlook.Please see the tests under theperf_tests/directory for a
performance comparison between theIndividual(default) vsBulkLoggers.ConsiderationsSome important considerations when choosing aBulkLogger:As we want to bulk send emails where possible, emails are sent in batches once each decorated
function returns.Because emails are sent in bulk, it's expected that the messages will be out of order.
For example, a message logged at the end of a decorated function might show up earlier than
a message logged at the start of the function.As a general rule, use the time listed on thesubjectheader in the Teams message; this is the time
at when the message was originally logged. Note that this might not always align with
timestamp that Teams lists for when the message was delivered.Common IssuesThis section describes common errors that might show up, along with the steps to resolve them.Template Not FoundExampleaws_teams_logger - ERROR - Template [send-to-teams | send-to-outlook] does not exist; please call `upload_templates` to upload the required SES templates.ResolutionAs mentioned, you will need to call theupload_templatesfunction to
add the required SES templates to the AWS account.fromaws_teams_loggerimportupload_templatesupload_templates(profile_name='my-aws-profile')Expected output:aws_teams_logger - INFO - [ send-to-teams ]
aws_teams_logger - INFO - Uploading SES template...
aws_teams_logger - DEBUG - send-to-teams: creating template, as it does not exist
aws_teams_logger - INFO -   SUCCESS
aws_teams_logger - INFO - [ send-to-outlook ]
aws_teams_logger - INFO - Uploading SES template...
aws_teams_logger - DEBUG - send-to-outlook: creating template, as it does not exist
aws_teams_logger - INFO -   SUCCESSUnable to Send EmailExampleAn error occurred (AccessDenied) when calling the SendTemplatedEmail operation: User `arn:aws:iam::1234567890:user/my-user' is not authorized to perform `ses:SendTemplatedEmail' on resource `arn:aws:ses:us-east-1:1234567890:identity/sender@my.domain.org'ResolutionUpdate the attached IAM role to allow theses:SendTemplatedEmailaction as discussed above.Unable to Determine Account NameExampleaws_teams_logger - ERROR - Unable to retrieve the account alias, please ensure the attached role has the necessary permissions (iam:ListAccountAliases). Error: An error occurred (AccessDenied) when calling the ListAccountAliases operation: User: arn:aws:iam::1234567890:user/my-user is not authorized to perform: iam:ListAccountAliases on resource: *ResolutionUpdate the attached IAM role to allow theiam:ListAccountAliasesaction as discussed above.You can also set theAWS_ACCOUNT_NAMEenvironment variable or callset_account_name(), and this will eliminate
the need of an API call made to IAM to retrieve the alias of the current account.