Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
([#227](https://github.com/microsoft/ApplicationInsights-Python/pull/227))
- Add metric configuration to distro api
([#232](https://github.com/microsoft/ApplicationInsights-Python/pull/232))
- Add ability to pass custom configuration into instrumentations
([#235](https://github.com/microsoft/ApplicationInsights-Python/pull/235))

## [1.0.0b8](https://github.com/microsoft/ApplicationInsights-Python/releases/tag/v1.0.0b8) - 2022-09-26

Expand Down
32 changes: 30 additions & 2 deletions azure-monitor-opentelemetry-distro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,37 @@ You can use `configure_azure_monitor` to set up instrumentation for your app to
* sampling_ratio - Specifies the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling]. Accepted values are in the range [0,1]. Defaults to 1.0, meaning no telemetry is sampled out.
* tracing_export_interval_millis - Specifies the distributed tracing export interval in milliseconds. Defaults to 30,000.

See additional [configuration related to exporting here][exporter_configuration_docs].
#### Exporter configurations

### Example code
You can pass exporter configuration parameters directly into `configure_azure_monitor`. See additional [configuration related to exporting here][exporter_configuration_docs].

```python
...
configure_azure_monitor(
connection_string="<your-connection-string>",
disable_offline_storage=True,
)
...
```

#### Instrumentation configurations

You can pass in instrumentation specific configuration into `configure_azure_monitor` with the key `<instrumented-library-name>_config` and value as a dictionary representing `kwargs` for the corresponding instrumentation. Note the instrumented library must also be enabled through the `instrumentations` configuration.

```python
...
configure_azure_monitor(
connection_string="<your-connection-string>",
instrumentations=["flask", "requests"],
flask_config={"excluded_urls": "http://localhost:8080/ignore"},
requests_config={"excluded_urls": "http://example.com"},
)
...
```

Take a look at the specific [instrumenation][ot_instrumentations] documentation for available configurations.

### Samples

Samples are available [here][samples] to demonstrate how to utilize the above configuration options.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@
_logger = getLogger(__name__)


_SUPPORTED_INSTRUMENTED_LIBRARIES = {
_INSTRUMENTATION_CONFIG_SUFFIX = "_config"
_SUPPORTED_INSTRUMENTED_LIBRARIES = (
"django",
"flask",
"psycopg2",
"requests",
}
)


def configure_azure_monitor(**kwargs):
Expand Down Expand Up @@ -141,6 +142,15 @@ def _setup_metrics(resource: Resource, configurations: Dict[str, Any]):

def _setup_instrumentations(configurations: Dict[str, Any]):
instrumentations = configurations.get("instrumentations", [])
instrumentation_configs = {}

# Instrumentation specific configs
# Format is {"<library_name>": {"<config>":<value>}}
for k, v in configurations.items():
if k.endswith(_INSTRUMENTATION_CONFIG_SUFFIX):
lib_name = k.partition(_INSTRUMENTATION_CONFIG_SUFFIX)[0]
instrumentation_configs[lib_name] = v

for lib_name in instrumentations:
if lib_name in _SUPPORTED_INSTRUMENTED_LIBRARIES:
try:
Expand All @@ -158,7 +168,8 @@ def _setup_instrumentations(configurations: Dict[str, Any]):
lib_name.capitalize()
)
class_ = getattr(module, instrumentor_name)
class_().instrument()
config = instrumentation_configs.get(lib_name, {})
class_().instrument(**config)
except ImportError:
_logger.warning(
"Unable to import %s. Please make sure it is installed.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
disable_tracing=True,
)

# Create a namespaced meter
meter = metrics.get_meter_provider().get_meter("sample")

# Callback functions for observable instruments
def observable_counter_func(options: CallbackOptions) -> Iterable[Observation]:
Expand All @@ -32,6 +30,9 @@ def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]:
yield Observation(9, {})


# Create a namespaced meter
meter = metrics.get_meter_provider().get_meter("sample")

# Counter
counter = meter.create_counter("counter")
counter.add(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
disable_logging=True,
disable_metrics=True,
instrumentations=["requests"],
requests_config={"excluded_urls": "http://example.com"},
tracing_export_interval_millis=15000,
)

Expand All @@ -26,6 +27,8 @@
try:
# Requests made using the requests library will be automatically captured
response = requests.get("https://azure.microsoft.com/", timeout=5)
# This request will not be tracked due to the excluded_urls configuration
response = requests.get("http://example.com", timeout=5)
logger.warning("Request sent")
except Exception as ex:
# If an exception occurs, this can be manually recorded on the parent span
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

# Configure Azure monitor collection telemetry pipeline
configure_azure_monitor(
# connection_string="<your-connection-string>",
connection_string="<your-connection-string>",
service_name="django_service_name",
instrumentations=["django"],
disable_logging=True,
disable_metrics=True,
tracing_export_interval_millis=15000,
)


# Requests sent to the django application will be automatically captured
def index(request):
return HttpResponse("Hello, world.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
disable_logging=True,
disable_metrics=True,
instrumentations=["flask"],
flask_config={"excluded_urls": "http://localhost:8080/ignore"},
tracing_export_interval_millis=15000,
)

app = flask.Flask(__name__)


# Requests sent to the flask application will be automatically captured
@app.route("/")
def test():
Expand All @@ -30,5 +32,12 @@ def exception():
raise Exception("Hit an exception")


# Requests sent to this endpoint will not be tracked due to
# flask_config configuration
@app.route("/ignore")
def ignore():
return "Request received but not tracked."


if __name__ == "__main__":
app.run(host="localhost", port=8080)
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,73 @@ def test_setup_instrumentations_failed_general(
)
instrumentor_mock.assert_not_called()
instrument_mock.instrument.assert_not_called()

@patch("azure.monitor.opentelemetry.distro.getattr")
def test_setup_instrumentations_custom_configuration(
self,
getattr_mock,
):
for lib_name in _SUPPORTED_INSTRUMENTED_LIBRARIES:
with patch("importlib.import_module") as import_module_mock:
configurations = {
"instrumentations": [lib_name],
lib_name + "_config": {"test_key": "test_value"},
}
instrument_mock = Mock()
instrumentor_mock = Mock()
instrumentor_mock.return_value = instrument_mock
getattr_mock.return_value = instrumentor_mock
_setup_instrumentations(configurations)
self.assertEqual(import_module_mock.call_count, 2)
instr_lib_name = "opentelemetry.instrumentation." + lib_name
import_module_mock.assert_has_calls(
[call(lib_name), call(instr_lib_name)]
)
instrumentor_mock.assert_called_once()
instrument_mock.instrument.assert_called_once_with(
**{"test_key": "test_value"}
)

@patch("azure.monitor.opentelemetry.distro.getattr")
def test_setup_instrumentations_custom_configuration_not_enabled(
self,
getattr_mock,
):
with patch("importlib.import_module") as import_module_mock:
lib_name = list(_SUPPORTED_INSTRUMENTED_LIBRARIES)[0]
configurations = {
"instrumentations": [],
lib_name + "_config": {"test_key": "test_value"},
}
instrument_mock = Mock()
instrumentor_mock = Mock()
instrumentor_mock.return_value = instrument_mock
getattr_mock.return_value = instrumentor_mock
_setup_instrumentations(configurations)
import_module_mock.assert_not_called()
instrumentor_mock.assert_not_called()
instrument_mock.instrument.assert_not_called()

@patch("azure.monitor.opentelemetry.distro.getattr")
def test_setup_instrumentations_custom_configuration_incorrect(
self,
getattr_mock,
):
with patch("importlib.import_module") as import_module_mock:
lib_name = list(_SUPPORTED_INSTRUMENTED_LIBRARIES)[0]
configurations = {
"instrumentations": [lib_name],
lib_name + "error_config": {"test_key": "test_value"},
}
instrument_mock = Mock()
instrumentor_mock = Mock()
instrumentor_mock.return_value = instrument_mock
getattr_mock.return_value = instrumentor_mock
_setup_instrumentations(configurations)
self.assertEqual(import_module_mock.call_count, 2)
instr_lib_name = "opentelemetry.instrumentation." + lib_name
import_module_mock.assert_has_calls(
[call(lib_name), call(instr_lib_name)]
)
instrumentor_mock.assert_called_once()
instrument_mock.instrument.assert_called_once_with(**{})
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_get_configurations(self):
connection_string="test_cs",
disable_logging="test_disable_logging",
disable_tracing="test_disable_tracing",
instrumentations=["test_instrumentation"],
logging_level="test_logging_level",
logger_name="test_logger_name",
service_name="test_service_name",
Expand All @@ -31,6 +32,7 @@ def test_get_configurations(self):
sampling_ratio="test_sample_ratio",
tracing_export_interval="test_tracing_interval",
logging_export_interval="test_logging_interval",
views=("test_view"),
)

self.assertEqual(configurations["connection_string"], "test_cs")
Expand All @@ -40,6 +42,9 @@ def test_get_configurations(self):
self.assertEqual(
configurations["disable_tracing"], "test_disable_tracing"
)
self.assertEqual(
configurations["instrumentations"], ["test_instrumentation"]
)
self.assertEqual(configurations["logging_level"], "test_logging_level")
self.assertEqual(configurations["logger_name"], "test_logger_name")
self.assertEqual(configurations["service_name"], "test_service_name")
Expand All @@ -52,3 +57,4 @@ def test_get_configurations(self):
self.assertEqual(
configurations["logging_export_interval"], "test_logging_interval"
)
self.assertEqual(configurations["views"], ("test_view"))