3

I have written following code to enable Cloudwatch support.

import logging
from boto3.session import Session
from watchtower import CloudWatchLogHandler

logging.basicConfig(level=logging.INFO,format='[%(asctime)s.%(msecs).03d] [%(name)s,%(funcName)s:%(lineno)s] [%(levelname)s]  %(message)s',datefmt='%d/%b/%Y %H:%M:%S')
log = logging.getLogger('Test')

boto3_session = Session(aws_access_key_id=AWS_ACCESS_KEY_ID,
                aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
                region_name=REGION_NAME)

cw_handler = CloudWatchLogHandler(log_group=CLOUDWATCH_LOG_GROUP_NAME,stream_name=CLOUDWATCH_LOG_STREAM_NAME,boto3_session=boto3_session)
log.addHandler(cw_handler)

Whenever i try to print any logger statement, i am getting different output on my local system and cloudwatch.

Example:

log.info("Hello world")

Output of above logger statement on my local system (terminal) :

[24/Feb/2019 15:25:06.969] [Test,<module>:1] [INFO]  Hello world

Output of above logger statement on cloudwatch (log stream) :

Hello world

Is there something i am missing ?

Pramod Munemanik
  • 281
  • 3
  • 10

3 Answers3

7

In the Lambda execution environment, the root logger is already preconfigured. You'll have to work with it or work around it. You could do some of the following:

You can set the formatting directly on the root logger:

root = logging.getLogger()
root.setLevel(logging.INFO)
root.handlers[0].setFormatter(logging.Formatter(fmt='[%(asctime)s.%(msecs).03d] [%(name)s,%(funcName)s:%(lineno)s] [%(levelname)s]  %(message)s', datefmt='%d/%b/%Y %H:%M:%S'))

You could add the Watchtower handler to it (disclaimer: I have not tried this approach):

root = logging.getLogger()
root.addHandler(cw_handler)

However I'm wondering if you even need to use Watchtower. In Lambda, every line you print to stdout (so even just using print) get logged to Cloudwatch. So using the standard logging interface might be sufficient.

Milan Cermak
  • 7,476
  • 3
  • 44
  • 59
  • How would you simultaneously support local logging (as per OP's attempt) and CloudWatch logging as per your excellent answer? – jtlz2 Dec 15 '22 at 10:25
2

This worked for me

import logging
import watchtower
watch = watchtower.CloudWatchLogHandler()
watch.setFormatter(fmt = logging.Formatter('%(levelname)s - %(module)s - %(message)s'))
logger = logging.getLogger()
logger.addHandler(watch)
DMA2607
  • 29
  • 2
0

As per comment in https://stackoverflow.com/a/45624044/1021819 (and another answer there),

just add force=True to logging.basicConfig(), so in your case you need

logging.basicConfig(level=logging.INFO, force=True, format='[%(asctime)s.%(msecs).03d] [%(name)s,%(funcName)s:%(lineno)s] [%(levelname)s]  %(message)s',datefmt='%d/%b/%Y %H:%M:%S')
log = logging.getLogger('Test')

This function does nothing if the root logger already has handlers configured, unless the keyword argument force is set to True.

(i.e. AWS case)

Re: force:

If this keyword argument is specified as true, any existing handlers attached to the root logger are removed and closed, before carrying out the configuration as specified by the other arguments.

REF: https://docs.python.org/3/library/logging.html#logging.basicConfig

THANKS:

https://stackoverflow.com/a/72054516/1021819

https://stackoverflow.com/a/45624044/1021819

jtlz2
  • 7,700
  • 9
  • 64
  • 114