Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features Added

- Add `logging_format` as configuration option in api
([#40057](https://github.com/Azure/azure-sdk-for-python/pull/40057))
Comment thread
lzchen marked this conversation as resolved.
- Enable Azure AI Agents instrumentation
([#40043](https://github.com/Azure/azure-sdk-for-python/pull/40043))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# license information.
# --------------------------------------------------------------------------
from functools import cached_property
from logging import getLogger
from logging import getLogger, Formatter
from typing import Dict, List, cast

from opentelemetry._events import _set_event_logger_provider
Expand Down Expand Up @@ -42,6 +42,7 @@
DISABLE_TRACING_ARG,
ENABLE_LIVE_METRICS_ARG,
LOGGER_NAME_ARG,
LOGGING_FORMAT_ARG,
RESOURCE_ARG,
SAMPLING_RATIO_ARG,
SPAN_PROCESSORS_ARG,
Expand Down Expand Up @@ -172,11 +173,23 @@ def _setup_logging(configurations: Dict[str, ConfigurationValue]):
logger_provider.add_log_record_processor(log_record_processor)
set_logger_provider(logger_provider)
logger_name: str = configurations[LOGGER_NAME_ARG] # type: ignore
logging_format: str = configurations[LOGGING_FORMAT_ARG] # type: ignore
logger = getLogger(logger_name)
# Only add OpenTelemetry LoggingHandler if logger does not already have the handler
# This is to prevent most duplicate logging telemetry
if not any(isinstance(handler, LoggingHandler) for handler in logger.handlers):
handler = LoggingHandler(logger_provider=logger_provider)
if logging_format:
formatter = None
try:
formatter = Formatter(logging_format)
except Exception as ex: # pylint: disable=broad-except
_logger.warning(
"Exception occurred when constructing logging Formatter: %s.",
ex,
)
if formatter:
handler.setFormatter(formatter)
logger.addHandler(handler)

# Setup EventLoggerProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
DISABLE_TRACING_ARG = "disable_tracing"
DISTRO_VERSION_ARG = _AZURE_MONITOR_DISTRO_VERSION_ARG
LOGGER_NAME_ARG = "logger_name"
LOGGING_FORMAT_ARG = "logging_format"
INSTRUMENTATION_OPTIONS_ARG = "instrumentation_options"
RESOURCE_ARG = "resource"
SAMPLING_RATIO_ARG = "sampling_ratio"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
ENABLE_LIVE_METRICS_ARG,
INSTRUMENTATION_OPTIONS_ARG,
LOGGER_NAME_ARG,
LOGGING_FORMAT_ARG,
RESOURCE_ARG,
SAMPLING_RATIO_ARG,
SPAN_PROCESSORS_ARG,
Expand Down Expand Up @@ -66,6 +67,7 @@ def _get_configurations(**kwargs) -> Dict[str, ConfigurationValue]:
_default_disable_metrics(configurations)
_default_disable_tracing(configurations)
_default_logger_name(configurations)
_default_logging_format(configurations)
_default_resource(configurations)
_default_sampling_ratio(configurations)
_default_instrumentation_options(configurations)
Expand Down Expand Up @@ -104,6 +106,10 @@ def _default_logger_name(configurations):
configurations.setdefault(LOGGER_NAME_ARG, "")


def _default_logging_format(configurations):
configurations.setdefault(LOGGING_FORMAT_ARG, "")
Comment thread
lzchen marked this conversation as resolved.
Outdated


def _default_resource(configurations):
environ.setdefault(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, ",".join(_SUPPORTED_RESOURCE_DETECTORS))
if RESOURCE_ARG not in configurations:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,29 @@

from azure.monitor.opentelemetry import configure_azure_monitor

# logging.basicConfig and logging.config will be overwritten by the SDK
# We recommend using logger specific configuration or the below apis to configure logging

configure_azure_monitor(
# Set logger_name to the name of the logger you want to capture logging telemetry with
# This is imperative so you do not collect logging telemetry from the SDK itself.
logger_name="my_application_logger",
logger_name="my_app_logger",
# You can specify the logging format of your collected logs
logging_format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)

# formatter = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# Logging telemetry will be collected from logging calls made with this logger and all of it's children loggers.
logger = getLogger("my_application_logger")
logger = getLogger("my_app_logger")
logger.setLevel(INFO)

# Logging calls with any logger that is a child logger will also be tracked
logger_child = getLogger("my-application_logger.module")
logger_child = getLogger("my_app_logger.module")
logger_child.setLevel(INFO)

# Logging calls with this logger will not be tracked
logger_not_tracked = getLogger("not_my_application_logger")
logger_not_tracked = getLogger("not_my_app_logger")
logger_not_tracked.setLevel(INFO)

logger.info("info log")
Expand Down
29 changes: 29 additions & 0 deletions sdk/monitor/azure-monitor-opentelemetry/tests/test_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ def test_setup_tracing(
"azure.monitor.opentelemetry._configure.EventLoggerProvider",
autospec=True,
)
@patch(
"azure.monitor.opentelemetry._configure.Formatter",
)
@patch(
"azure.monitor.opentelemetry._configure.getLogger",
)
Expand Down Expand Up @@ -338,6 +341,7 @@ def test_setup_logging(
blrp_mock,
logging_handler_mock,
get_logger_mock,
formatter_mock,
elp_mock,
set_elp_mock,
):
Expand All @@ -354,11 +358,14 @@ def test_setup_logging(
logger_mock = Mock()
logger_mock.handlers = []
get_logger_mock.return_value = logger_mock
formatter_init_mock = Mock()
formatter_mock.return_value = formatter_init_mock

configurations = {
"connection_string": "test_cs",
"logger_name": "test",
"resource": TEST_RESOURCE,
"logging_format": "test_format"
}
_setup_logging(configurations)

Expand All @@ -370,6 +377,7 @@ def test_setup_logging(
)
lp_init_mock.add_log_record_processor.assert_called_once_with(blrp_init_mock)
logging_handler_mock.assert_called_once_with(logger_provider=lp_init_mock)
logging_handler_init_mock.setFormatter.assert_called_once_with(formatter_init_mock)
get_logger_mock.assert_called_once_with("test")
logger_mock.addHandler.assert_called_once_with(logging_handler_init_mock)
elp_mock.assert_called_once_with(lp_init_mock)
Expand Down Expand Up @@ -414,6 +422,7 @@ def test_setup_logging_duplicate_logger(
"connection_string": "test_cs",
"logger_name": "test",
"resource": TEST_RESOURCE,
"logging_format": ""
}
_setup_logging(configurations)

Expand Down Expand Up @@ -660,26 +669,46 @@ def test_setup_instrumentations_disabled(
logger_mock.debug.assert_called_once()

@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
def test_send_attach_warning_true(
self,
is_attach_enabled_mock,
is_on_functions_mock,
mock_diagnostics,
):
is_attach_enabled_mock.return_value = True
is_on_functions_mock.return_value = False
_send_attach_warning()
mock_diagnostics.warning.assert_called_once_with(
"Distro detected that automatic attach may have occurred. Check your data to ensure that telemetry is not being duplicated. This may impact your cost.",
_DISTRO_DETECTS_ATTACH,
)

@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
def test_send_attach_warning_false(
self,
is_attach_enabled_mock,
is_on_functions_mock,
mock_diagnostics,
):
is_attach_enabled_mock.return_value = False
is_on_functions_mock.return_value = False
_send_attach_warning()
mock_diagnostics.warning.assert_not_called()

@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
def test_send_attach_warning_false_on_functions(
self,
is_attach_enabled_mock,
is_on_functions_mock,
mock_diagnostics,
):
is_attach_enabled_mock.return_value = True
is_on_functions_mock.return_value = True
_send_attach_warning()
mock_diagnostics.warning.assert_not_called()