Skip to content

Commit 87c7d9a

Browse files
authored
Add logging_format configuration option in api (Azure#40057)
1 parent e16a937 commit 87c7d9a

File tree

8 files changed

+68
-9
lines changed

8 files changed

+68
-9
lines changed

sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Features Added
66

7+
- Add `logging_format` as configuration option in api
8+
([#40057](https://github.com/Azure/azure-sdk-for-python/pull/40057))
79
- Enable Azure AI Agents instrumentation
810
([#40043](https://github.com/Azure/azure-sdk-for-python/pull/40043))
911

sdk/monitor/azure-monitor-opentelemetry/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ You can use `configure_azure_monitor` to set up instrumentation for your app to
6161
|-------------------|----------------------------------------------------|----------------------|
6262
| `connection_string` | The [connection string][connection_string_doc] for your Application Insights resource. The connection string will be automatically populated from the `APPLICATIONINSIGHTS_CONNECTION_STRING` environment variable if not explicitly passed in. | `APPLICATIONINSIGHTS_CONNECTION_STRING` |
6363
| `enable_live_metrics` | Enable [live metrics][application_insights_live_metrics] feature. Defaults to `False`. | `N/A` |
64+
| `logging_formatter` | A Python logging [formatter][python_logging_formatter] that will be used to format collected logs. | `N/A` |
6465
| `logger_name` | The name of the [Python logger][python_logger] under which telemetry is collected. Setting this value is imperative so logs created from the SDK itself are not tracked. | `N/A` |
6566
| `instrumentation_options` | A nested dictionary that determines which instrumentations to enable or disable. Instrumentations are referred to by their [Library Names](#officially-supported-instrumentations). For example, `{"azure_sdk": {"enabled": False}, "flask": {"enabled": False}, "django": {"enabled": True}}` will disable Azure Core Tracing and the Flask instrumentation but leave Django and the other default instrumentations enabled. The `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` environment variable explained below can also be used to disable instrumentations. | `N/A` |
6667
| `resource` | Specifies the OpenTelemetry [Resource][ot_spec_resource] associated with your application. Passed in [Resource Attributes][ot_spec_resource_attributes] take priority over default attributes and those from [Resource Detectors][ot_python_resource_detectors]. | [OTEL_SERVICE_NAME][ot_spec_service_name], [OTEL_RESOURCE_ATTRIBUTES][ot_spec_resource_attributes], [OTEL_EXPERIMENTAL_RESOURCE_DETECTORS][ot_python_resource_detectors] |
@@ -256,6 +257,7 @@ contact [[email protected]](mailto:[email protected]) with any additio
256257
[pypi_urllib3]: https://pypi.org/project/urllib3/
257258
[python]: https://www.python.org/downloads/
258259
[python_logger]: https://docs.python.org/3/library/logging.html#logger-objects
260+
[python_logging_formatter]: https://docs.python.org/3/library/logging.html#formatter-objects
259261
[python_logging_level]: https://docs.python.org/3/library/logging.html#levels
260262
[samples]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples
261263
[samples_manual]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/manually_instrumented.py

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
from functools import cached_property
7-
from logging import getLogger
7+
from logging import getLogger, Formatter
88
from typing import Dict, List, cast
99

1010
from opentelemetry._events import _set_event_logger_provider
@@ -42,6 +42,7 @@
4242
DISABLE_TRACING_ARG,
4343
ENABLE_LIVE_METRICS_ARG,
4444
LOGGER_NAME_ARG,
45+
LOGGING_FORMATTER_ARG,
4546
RESOURCE_ARG,
4647
SAMPLING_RATIO_ARG,
4748
SPAN_PROCESSORS_ARG,
@@ -172,11 +173,20 @@ def _setup_logging(configurations: Dict[str, ConfigurationValue]):
172173
logger_provider.add_log_record_processor(log_record_processor)
173174
set_logger_provider(logger_provider)
174175
logger_name: str = configurations[LOGGER_NAME_ARG] # type: ignore
176+
logging_formatter: Formatter = configurations[LOGGING_FORMATTER_ARG] # type: ignore
175177
logger = getLogger(logger_name)
176178
# Only add OpenTelemetry LoggingHandler if logger does not already have the handler
177179
# This is to prevent most duplicate logging telemetry
178180
if not any(isinstance(handler, LoggingHandler) for handler in logger.handlers):
179181
handler = LoggingHandler(logger_provider=logger_provider)
182+
if logging_formatter:
183+
try:
184+
handler.setFormatter(logging_formatter)
185+
except Exception as ex: # pylint: disable=broad-except
186+
_logger.warning(
187+
"Exception occurred when adding logging Formatter: %s.",
188+
ex,
189+
)
180190
logger.addHandler(handler)
181191

182192
# Setup EventLoggerProvider

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DISABLE_TRACING_ARG = "disable_tracing"
1919
DISTRO_VERSION_ARG = _AZURE_MONITOR_DISTRO_VERSION_ARG
2020
LOGGER_NAME_ARG = "logger_name"
21+
LOGGING_FORMATTER_ARG = "logging_formatter"
2122
INSTRUMENTATION_OPTIONS_ARG = "instrumentation_options"
2223
RESOURCE_ARG = "resource"
2324
SAMPLING_RATIO_ARG = "sampling_ratio"

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_utils/configurations.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# license information.
55
# --------------------------------------------------------------------------
66

7-
from logging import getLogger
7+
from logging import getLogger, Formatter
88
from os import environ
99
from typing import Dict
1010

@@ -34,6 +34,7 @@
3434
ENABLE_LIVE_METRICS_ARG,
3535
INSTRUMENTATION_OPTIONS_ARG,
3636
LOGGER_NAME_ARG,
37+
LOGGING_FORMATTER_ARG,
3738
RESOURCE_ARG,
3839
SAMPLING_RATIO_ARG,
3940
SPAN_PROCESSORS_ARG,
@@ -66,6 +67,7 @@ def _get_configurations(**kwargs) -> Dict[str, ConfigurationValue]:
6667
_default_disable_metrics(configurations)
6768
_default_disable_tracing(configurations)
6869
_default_logger_name(configurations)
70+
_default_logging_formatter(configurations)
6971
_default_resource(configurations)
7072
_default_sampling_ratio(configurations)
7173
_default_instrumentation_options(configurations)
@@ -104,6 +106,12 @@ def _default_logger_name(configurations):
104106
configurations.setdefault(LOGGER_NAME_ARG, "")
105107

106108

109+
def _default_logging_formatter(configurations):
110+
formatter = configurations.get(LOGGING_FORMATTER_ARG)
111+
if not isinstance(formatter, Formatter):
112+
configurations[LOGGING_FORMATTER_ARG] = None
113+
114+
107115
def _default_resource(configurations):
108116
environ.setdefault(OTEL_EXPERIMENTAL_RESOURCE_DETECTORS, ",".join(_SUPPORTED_RESOURCE_DETECTORS))
109117
if RESOURCE_ARG not in configurations:

sdk/monitor/azure-monitor-opentelemetry/samples/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ For guidance on the samples README, visit the [sample guide](https://github.com/
1717

1818
|**File Name**|**Description**|
1919
|----------------|-------------|
20+
|[logging/basic.py][logging_basic] | Produce logs using basic logging configurations |
2021
|[logging/correlated_logs.py][correlated_logs] | Produce logs correlated with spans |
22+
|[logging/custom_event.py][custom_event] | Produce custom events using logs |
2123
|[logging/custom_properties.py][custom_properties] | Add custom propterties to logs |
2224
|[logging/exception_logs.py][exception_logs] | Produce exception logs |
2325
|[logging/logs_with_traces.py][logs_with_traces] | Produce correlated logs inside an instrumented http library's distributed tracing |
24-
|[logging/basic.py][logging_basic] | Produce logs |
2526
|[metrics/attributes.py][attributes] | Add attributes to custom metrics counters |
2627
|[metrics/instruments.py][instruments] | Create observable instruments |
2728
|[metrics/live_metrics.py][live_metrics] | Live metrics feature |
29+
|[tracing/azure_ai_inference.py][azure_ai_inference] | Instrument an app using Azure AI inference SDK |
30+
|[tracing/azure_blob_storage.py][azure_blob_storage] | Instrument an app using Azure Blob storage SDK |
2831
|[tracing/django/sample/manage.py][django] | Instrument a django app |
2932
|[tracing/db_psycopg2.py][db_psycopg2] | Instrument the PsycoPG2 library |
3033
|[tracing/http_fastapi.py][http_fastapi] | Instrument a FastAPI app |
@@ -34,6 +37,7 @@ For guidance on the samples README, visit the [sample guide](https://github.com/
3437
|[tracing/http_urllib3.py][http_urllib3] | Instrument the URLLib library |
3538
|[tracing/instrumentation_options.py][instrumentation_options] | Enable and disable instrumentations |
3639
|[tracing/manually_instrumented.py][manual] | Manually add instrumentation |
40+
|[tracing/modify_spans.py][modify_spans] | Modify spans with span processors |
3741
|[tracing/sampling.py][sampling] | Sample distributed tracing telemetry |
3842
|[tracing/tracing_simple.py][tracing_simple] | Produce manual spans |
3943

@@ -63,13 +67,16 @@ To learn more, see the [Azure Monitor OpenTelemetry Distro documentation][distro
6367
[distro_docs]: https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable?tabs=python
6468
[otel_docs]: https://opentelemetry.io/docs/
6569
[correlated_logs]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/correlated_logs.py
70+
[custom_event]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/custom_event.py
6671
[custom_properties]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/custom_properties.py
6772
[exception_logs]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/exception_logs.py
6873
[logs_with_traces]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/logs_with_traces.py
6974
[logging_basic]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/logging/basic.py
7075
[attributes]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/metrics/attributes.py
7176
[instruments]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/metrics/instruments.py
7277
[instruments]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/metrics/live_metrics.py
78+
[azure_ai_inference]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/azure_ai_inference.py
79+
[azure_blob_storage]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/azure_blob_storage.py
7380
[django]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/django/sample/manage.py
7481
[db_psycopg2]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/db_psycopg2.py
7582
[http_fastapi]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_fastapi.py
@@ -78,6 +85,7 @@ To learn more, see the [Azure Monitor OpenTelemetry Distro documentation][distro
7885
[http_urllib]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_urllib.py
7986
[http_urllib3]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_urllib3.py
8087
[instrumentation_options]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/instrumentation_options.py
88+
[modify_spans]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/modify_spans.py
8189
[manual]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/manually_instrumented.py
8290
[sampling]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/sampling.py
8391
[tracing_simple]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/simple.py

sdk/monitor/azure-monitor-opentelemetry/samples/logging/basic.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,31 @@
44
# license information.
55
# --------------------------------------------------------------------------
66

7-
from logging import INFO, getLogger
7+
from logging import INFO, Formatter, getLogger
88

99
from azure.monitor.opentelemetry import configure_azure_monitor
1010

11+
# logging.basicConfig and logging.config will be overwritten by the SDK
12+
# We recommend using logger specific configuration or the below apis to configure logging
13+
1114
configure_azure_monitor(
1215
# Set logger_name to the name of the logger you want to capture logging telemetry with
1316
# This is imperative so you do not collect logging telemetry from the SDK itself.
14-
logger_name="my_application_logger",
17+
logger_name="my_app_logger",
18+
# You can specify the logging format of your collected logs by passing in a logging.Formatter
19+
logging_formatter=Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
1520
)
1621

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

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

2530
# Logging calls with this logger will not be tracked
26-
logger_not_tracked = getLogger("not_my_application_logger")
31+
logger_not_tracked = getLogger("not_my_app_logger")
2732
logger_not_tracked.setLevel(INFO)
2833

2934
logger.info("info log")

sdk/monitor/azure-monitor-opentelemetry/tests/test_configure.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,13 @@ def test_setup_logging(
354354
logger_mock = Mock()
355355
logger_mock.handlers = []
356356
get_logger_mock.return_value = logger_mock
357+
formatter_init_mock = Mock()
357358

358359
configurations = {
359360
"connection_string": "test_cs",
360361
"logger_name": "test",
361362
"resource": TEST_RESOURCE,
363+
"logging_formatter": formatter_init_mock
362364
}
363365
_setup_logging(configurations)
364366

@@ -370,6 +372,7 @@ def test_setup_logging(
370372
)
371373
lp_init_mock.add_log_record_processor.assert_called_once_with(blrp_init_mock)
372374
logging_handler_mock.assert_called_once_with(logger_provider=lp_init_mock)
375+
logging_handler_init_mock.setFormatter.assert_called_once_with(formatter_init_mock)
373376
get_logger_mock.assert_called_once_with("test")
374377
logger_mock.addHandler.assert_called_once_with(logging_handler_init_mock)
375378
elp_mock.assert_called_once_with(lp_init_mock)
@@ -414,6 +417,7 @@ def test_setup_logging_duplicate_logger(
414417
"connection_string": "test_cs",
415418
"logger_name": "test",
416419
"resource": TEST_RESOURCE,
420+
"logging_formatter": None,
417421
}
418422
_setup_logging(configurations)
419423

@@ -424,7 +428,6 @@ def test_setup_logging_duplicate_logger(
424428
log_exp_init_mock,
425429
)
426430
lp_init_mock.add_log_record_processor.assert_called_once_with(blrp_init_mock)
427-
# logging_handler_mock.assert_not_called()
428431
get_logger_mock.assert_called_once_with("test")
429432
logger_mock.addHandler.assert_not_called()
430433

@@ -660,26 +663,46 @@ def test_setup_instrumentations_disabled(
660663
logger_mock.debug.assert_called_once()
661664

662665
@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
666+
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
663667
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
664668
def test_send_attach_warning_true(
665669
self,
666670
is_attach_enabled_mock,
671+
is_on_functions_mock,
667672
mock_diagnostics,
668673
):
669674
is_attach_enabled_mock.return_value = True
675+
is_on_functions_mock.return_value = False
670676
_send_attach_warning()
671677
mock_diagnostics.warning.assert_called_once_with(
672678
"Distro detected that automatic attach may have occurred. Check your data to ensure that telemetry is not being duplicated. This may impact your cost.",
673679
_DISTRO_DETECTS_ATTACH,
674680
)
675681

676682
@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
683+
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
677684
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
678685
def test_send_attach_warning_false(
679686
self,
680687
is_attach_enabled_mock,
688+
is_on_functions_mock,
681689
mock_diagnostics,
682690
):
683691
is_attach_enabled_mock.return_value = False
692+
is_on_functions_mock.return_value = False
693+
_send_attach_warning()
694+
mock_diagnostics.warning.assert_not_called()
695+
696+
@patch("azure.monitor.opentelemetry._configure.AzureDiagnosticLogging")
697+
@patch("azure.monitor.opentelemetry._configure._is_on_functions")
698+
@patch("azure.monitor.opentelemetry._configure._is_attach_enabled")
699+
def test_send_attach_warning_false_on_functions(
700+
self,
701+
is_attach_enabled_mock,
702+
is_on_functions_mock,
703+
mock_diagnostics,
704+
):
705+
is_attach_enabled_mock.return_value = True
706+
is_on_functions_mock.return_value = True
684707
_send_attach_warning()
685708
mock_diagnostics.warning.assert_not_called()

0 commit comments

Comments
 (0)