{"id":13572,"date":"2025-11-10T06:12:38","date_gmt":"2025-11-10T06:12:38","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=13572"},"modified":"2025-11-12T10:12:13","modified_gmt":"2025-11-12T10:12:13","slug":"watchtower-configuration-for-logging-python-logs-in-cloudwatch","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/cloud\/watchtower-configuration-for-logging-python-logs-in-cloudwatch","title":{"rendered":"Understanding Watchtower configuration for logging Python logs in CloudWatch"},"content":{"rendered":"<p>Here are 4 simple Watchtower demo examples that you can easily copy and use:<\/p>\n<h3>Key Points:<\/h3>\n<ul>\n<li><strong>Basic usage <\/strong>&#8211; Just add the handler to any logger<\/li>\n<li><strong>Web frameworks<\/strong> &#8211; Flask, FastAPI, Django integration<\/li>\n<li><strong>Custom configuration<\/strong> &#8211; Log groups, streams, formatters<\/li>\n<li><strong>Error handling<\/strong> &#8211; Exception logging with stack traces<\/li>\n<\/ul>\n<h2>Simple Watchtower Demo Examples<\/h2>\n<h3>1. Basic Usage<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">import watchtower, logging\r\nlogging.basicConfig(level=logging.INFO)\r\nlogger = logging.getLogger(__name__)\r\nlogger.addHandler(watchtower.CloudWatchLogsHandler())\r\nlogger.info(\"Hi\")\r\nlogger.info(dict(foo=\"bar\", details={}))\r\n<\/pre>\n<h3>2. Flask App with Watchtower<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">import watchtower, flask, logging\r\nlogging.basicConfig(level=logging.INFO)\r\napp = flask.Flask(\"loggable\")\r\nhandler = watchtower.CloudWatchLogsHandler()\r\napp.logger.addHandler(handler)\r\nlogging.getLogger(\"werkzeug\").addHandler(handler)\r\n\r\n@app.route('\/')\r\ndef hello_world():\r\n    return 'Hello World!'\r\n\r\nif __name__ == '__main__':\r\n    app.run()\r\n<\/pre>\n<h3>3. Custom Log Group and Stream<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">import watchtower, logging\r\nlogger = logging.getLogger('my_app')\r\nhandler = watchtower.CloudWatchLogsHandler(\r\n    log_group='my-app-logs',\r\n    stream_name='production'\r\n)\r\nlogger.addHandler(handler)\r\nlogger.setLevel(logging.INFO)\r\nlogger.info(\"App started\")\r\n<\/pre>\n<h3>4. With Formatter<\/h3>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">import watchtower, logging\r\nlogger = logging.getLogger('formatted')\r\nhandler = watchtower.CloudWatchLogsHandler()\r\nformatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')\r\nhandler.setFormatter(formatter)\r\nlogger.addHandler(handler)\r\nlogger.setLevel(logging.INFO)\r\nlogger.info(\"Formatted message\")\r\n<\/pre>\n<p>Also, below are the alternatives of Watchtower in 2025:<\/p>\n<h2>1. Standard Python logging:<\/h2>\n<h3>How It Works<\/h3>\n<p>By default, Lambda automatically captures logs for all function invocations and sends them to CloudWatch Logs, provided your function&#8217;s execution role has the necessary permissions. These logs are, by default, stored in a log group named \/aws\/lambda\/<\/p>\n<p><strong>import logging<\/strong><\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\"># Configure the logger\r\nlogger = logging.getLogger()\r\nlogger.setLevel(logging.INFO)\r\n\r\ndef lambda_handler(event, context):\r\n    logger.info(\"This message will appear in CloudWatch Logs\")\r\n    logger.error(\"This error will also appear in CloudWatch Logs\")\r\n    \r\n    # Even print statements work\r\n    print(\"This print statement also goes to CloudWatch\")\r\n    \r\n    return {\r\n        'statusCode': 200,\r\n        'body': 'Hello World'\r\n    }\r\n<\/pre>\n<h2>2. AWS Lambda Powertools (Recommended):<\/h2>\n<p>Logger provides an opinionated logger with output structured as JSON and efficiently handles logs for AWS Lambda, ensuring logs are formatted correctly and recorded in CloudWatch seamlessly.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">from aws_lambda_powertools import Logger\r\nlogger = Logger()\r\n\r\n@logger.inject_lambda_context\r\ndef lambda_handler(event, context):\r\n    logger.info(\"Processing event\", extra={\"event\": event})\r\n    return {\"statusCode\": 200}\r\n<\/pre>\n<h2>3. Direct Boto3 CloudWatch Logs Client<\/h2>\n<p>You can use Amazon CloudWatch Logs API directly through boto3 to programmatically access and analyze your AWS CloudWatch logs<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"dockerfile\">import boto3\r\nimport json\r\nfrom datetime import datetime\r\n\r\ndef send_log_to_cloudwatch(message, log_group, log_stream):\r\n    client = boto3.client('logs')\r\n    timestamp = int(datetime.now().timestamp() * 1000)\r\n    client.put_log_events(\r\n        logGroupName=log_group,\r\n        logStreamName=log_stream,\r\n        logEvents=[\r\n            {\r\n                'timestamp': timestamp,\r\n                'message': json.dumps(message) if isinstance(message, dict) else str(message)\r\n            }\r\n        ]\r\n    )\r\n<\/pre>\n<div class=\"qanda-read-box\"><div class=\"bg-light read-more-icon\"><img decoding=\"async\" src=\"https:\/\/assets.bacancytechnology.com\/qanda\/wp-content\/uploads\/2025\/04\/24061434\/read-txt.png\" alt=\"Also Read\"><p><\/p><h3>Also Read:<\/h3><a href=\"https:\/\/www.bacancytechnology.com\/blog\/netflix-aws-migration\" target=\"_blank\">Netflix AWS Migration<\/a><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Here are 4 simple Watchtower demo examples that you can easily copy and use: Key Points: Basic usage &#8211; Just add the handler to any logger Web frameworks &#8211; Flask, FastAPI, Django integration Custom configuration &#8211; Log groups, streams, formatters Error handling &#8211; Exception logging with stack traces Simple Watchtower Demo Examples 1. Basic Usage [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":13642,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[17],"tags":[],"class_list":["post-13572","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cloud"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13572"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=13572"}],"version-history":[{"count":3,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13572\/revisions"}],"predecessor-version":[{"id":13643,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13572\/revisions\/13643"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/13642"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=13572"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=13572"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=13572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}