Python Azure function logging into Azure Monitor (App Insights)

3.2k Views Asked by At

We are wanting to log custom properties using the Opencensus library in our Azure function. We are able to log custom properties (in our logs) into Azure Monitor via a standalone python code (locally run). We are also able to log custom properties into Azure Monitor when the Azure function is run locally. However, when we deploy the function in Azure, the Azure Function SDK behaves very differently every time.

  1. It doesn't log custom telemetry in some runs
  2. It logs custom telemetry other times, but logs the same log entry multiple times (logging the same line twice sometimes, while thrice other times). Please see the code below.
import logging
import azure.functions as func

from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.ext.azure.log_exporter import AzureLogHandler
from opencensus.trace import config_integration
from opencensus.trace.samplers import ProbabilitySampler, AlwaysOnSampler
from opencensus.trace.tracer import Tracer
from opencensus.trace import execution_context
from opencensus.trace.propagation.trace_context_http_header_format import TraceContextPropagator
        
config_integration.trace_integrations(['logging'])                

        
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    try:
        exporter = AzureExporter(connection_string=<ConnString>)
        logger = logging.getLogger(__name__)
        handler = AzureLogHandler(connection_string=<ConnString>)
        if(logger.hasHandlers()):
            logger.handlers.clear()

        logger.addHandler(handler)
        logger.info('Python HTTP trigger function processed a request.')

        properties = {'custom_dimensions': {'memberId': '220', 'transactionId': '98480dcc-3abc-45a3-9145-f4b97b991f95'}}

        span_context = TraceContextPropagator().from_headers({
            "traceparent": context.trace_context.Traceparent,
            "tracestate": context.trace_context.Tracestate
        })
        tracer = Tracer(
            span_context=span_context,
            exporter=exporter,
            sampler=AlwaysOnSampler()
        )
        execution_context.set_opencensus_tracer(tracer)

        logger.warning('Before the span', extra=properties)
        
        with tracer.span("custom_dimensions_span"):
            # properties = {'custom_dimensions': {'ABCD': 'EFG'}}
            logger.info("This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.", extra=properties)  
            
        logger.warning('After the span', extra=properties)

        name = req.params.get('name')
        if not name:
            try:
                req_body = req.get_json()
            except ValueError:
                pass
            else:
                name = req_body.get('name')
        
        #result = 1 / 0  # generate a ZeroDivisionError
        if name:
            return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
        else:
            return func.HttpResponse(
                "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
                status_code=200
            )
    except Exception as e:
            logger.error('Captured an exception. ' + str(e), extra=properties)

1

There are 1 best solutions below

0
On

Whenever the main is called it adds the handler to the logger. Each time the handler writes the log. For that, it was repeating. Whenever the getLogger can be called it will return the same object. addHandler doesn't check whether the handler is already added or not.

Ways to fix the Repeating log issue:

  1. If you are using parent and child logging you may use the logger.propagate property to avoid duplicates.
logger.propagate = False
  1. Check whether the handler or already added or not
logger = logging.getLoggger('Your_logger')
if not logger.handlers:
    #Create the handlers
    #call the .addHandler('Your_handler')

  1. Use the hasHandlers hasHandlers returns True if any handers are already configured on the logger.
    logger = logging.getLogger(__name__)
    if logger.hasHandlers():
        # Logger is already configured, remove all handlers by passing empty value
        logger.handlers = []

References