From 71998fbf0b419eab044560d958c7fd5783edad49 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 25 Jul 2023 09:47:56 -0700 Subject: [PATCH] Update docs to include flush/shutdown behavior, add fastapi example (#31283) --- .../README.md | 19 ++++++++- .../samples/logs/sample_log.py | 9 +++-- .../samples/metrics/sample_instruments.py | 12 ++++-- .../samples/traces/sample_fastapi.py | 40 +++++++++++++++++++ .../samples/traces/sample_trace.py | 10 +++-- 5 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_fastapi.py diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/README.md b/sdk/monitor/azure-monitor-opentelemetry-exporter/README.md index 63c9daa83c6d..612598e5ae4f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/README.md +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/README.md @@ -188,6 +188,10 @@ logging.getLogger().setLevel(logging.NOTSET) logger = logging.getLogger(__name__) logger.warning("Hello World!") + +# Telemetry records are flushed automatically upon application exit +# If you would like to flush records manually yourself, you can call force_flush() +logger_provider.force_flush() ``` #### Export Correlated Log @@ -397,6 +401,10 @@ histogram.record(99.9) # Async Gauge gauge = meter.create_observable_gauge("gauge", [observable_gauge_func]) +# Upon application exit, one last collection is made and telemetry records are +# flushed automatically. # If you would like to flush records manually yourself, +# you can call force_flush() +meter_provider.force_flush() ``` #### Metric custom views @@ -521,7 +529,8 @@ from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter -trace.set_tracer_provider(TracerProvider()) +tracer_provider = TracerProvider() +trace.set_tracer_provider(tracer_provider) tracer = trace.get_tracer(__name__) # This is the exporter that sends data to Application Insights exporter = AzureMonitorTraceExporter( @@ -532,6 +541,10 @@ trace.get_tracer_provider().add_span_processor(span_processor) with tracer.start_as_current_span("hello"): print("Hello, World!") + +# Telemetry records are flushed automatically upon application exit +# If you would like to flush records manually yourself, you can call force_flush() +tracer_provider.force_flush() ``` #### Instrumentation with requests library @@ -611,6 +624,10 @@ for i in range(100): print("Hello, World!") ``` +## Flush/shutdown behavior + +For all applications set up with OpenTelemetry SDK and Azure Monitor exporters, telemetry is flushed automatically upon application exit. Note that this does not include when application ends abruptly or crashes due to uncaught exception. + ## Troubleshooting The exporter raises exceptions defined in [Azure Core](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/README.md#azure-core-library-exceptions). diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/logs/sample_log.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/logs/sample_log.py index 1f917b1a80c6..08a2090c54c2 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/logs/sample_log.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/logs/sample_log.py @@ -19,11 +19,12 @@ from azure.monitor.opentelemetry.exporter import AzureMonitorLogExporter -set_logger_provider(LoggerProvider()) +logger_provider = LoggerProvider() +set_logger_provider(logger_provider) exporter = AzureMonitorLogExporter.from_connection_string( os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"] ) -get_logger_provider().add_log_record_processor(BatchLogRecordProcessor(exporter)) +get_logger_provider().add_log_record_processor(BatchLogRecordProcessor(exporter, schedule_delay_millis=60000)) # Attach LoggingHandler to namespaced logger handler = LoggingHandler() @@ -33,4 +34,6 @@ logger.info("Hello World!") -input() +# Telemetry records are flushed automatically upon application exit +# If you would like to flush records manually yourself, you can call force_flush() +logger_provider.force_flush() diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_instruments.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_instruments.py index 6f4c9bfa3053..dd956ff15198 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_instruments.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/metrics/sample_instruments.py @@ -20,8 +20,9 @@ os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"] ) # Metrics are reported every 1 minute -reader = PeriodicExportingMetricReader(exporter) -metrics.set_meter_provider(MeterProvider(metric_readers=[reader])) +reader = PeriodicExportingMetricReader(exporter,export_interval_millis=60000) +meter_provider = MeterProvider(metric_readers=[reader]) +metrics.set_meter_provider(meter_provider) # Create a namespaced meter meter = metrics.get_meter_provider().get_meter("sample") @@ -66,4 +67,9 @@ def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]: # Async Gauge gauge = meter.create_observable_gauge("gauge", [observable_gauge_func]) -# cSpell:disable \ No newline at end of file +# Upon application exit, one last collection is made and telemetry records are +# flushed automatically. # If you would like to flush records manually yourself, +# you can call force_flush() +meter_provider.force_flush() + +# cSpell:disable diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_fastapi.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_fastapi.py new file mode 100644 index 000000000000..66152ea8c455 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_fastapi.py @@ -0,0 +1,40 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +""" +An example to show an application instrumented with the OpenTelemetry flask instrumentation. +Calls made with the flask library will be automatically tracked and telemetry is exported to +application insights with the AzureMonitorTraceExporter. +See more info on the flask instrumentation here: +https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-flask +""" +from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter +from fastapi import Depends, FastAPI +from httpx import AsyncClient +from opentelemetry import trace +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor + +app = FastAPI() + +tracer = trace.get_tracer(__name__) + +async def get_client(): + async with AsyncClient() as client: + yield client + +@app.get("/") +async def read_root(client=Depends(get_client)): + with tracer.start_as_current_span("read_root"): + response = await client.get("https://httpbin.org/get") + return response.json() + +if __name__ == "__main__": + # cSpell:disable + import uvicorn + trace.set_tracer_provider(TracerProvider()) + exporter = AzureMonitorTraceExporter() + span_processor = BatchSpanProcessor(exporter) + trace.get_tracer_provider().add_span_processor(span_processor) + uvicorn.run("sample_fastapi:app", port=8008, reload=True) + # cSpell:disable + diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_trace.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_trace.py index 56ea5d24129b..a639a23a1fff 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_trace.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/samples/traces/sample_trace.py @@ -11,15 +11,19 @@ from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter - exporter = AzureMonitorTraceExporter.from_connection_string( os.environ["APPLICATIONINSIGHTS_CONNECTION_STRING"] ) -trace.set_tracer_provider(TracerProvider()) +tracer_provider = TracerProvider() +trace.set_tracer_provider(tracer_provider) tracer = trace.get_tracer(__name__) -span_processor = BatchSpanProcessor(exporter) +span_processor = BatchSpanProcessor(exporter, schedule_delay_millis=60000) trace.get_tracer_provider().add_span_processor(span_processor) with tracer.start_as_current_span("hello"): print("Hello, World!") + +# Telemetry records are flushed automatically upon application exit +# If you would like to flush records manually yourself, you can call force_flush() +tracer_provider.force_flush()