diff --git a/CHANGELOG.md b/CHANGELOG.md index d9e89b18034..256312c8c44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix serialization of extended attributes for logs signal ([#4342](https://github.com/open-telemetry/opentelemetry-python/pull/4342)) +- docs: updated and added to the metrics and log examples + ([#4559](https://github.com/open-telemetry/opentelemetry-python/pull/4559)) ## Version 1.32.0/0.53b0 (2025-04-10) diff --git a/docs/examples/logs/README.rst b/docs/examples/logs/README.rst index b61114733ec..e3cd86362b2 100644 --- a/docs/examples/logs/README.rst +++ b/docs/examples/logs/README.rst @@ -52,37 +52,70 @@ The resulting logs will appear in the output from the collector and look similar .. code-block:: sh - Resource SchemaURL: - Resource labels: - -> telemetry.sdk.language: STRING(python) - -> telemetry.sdk.name: STRING(opentelemetry) - -> telemetry.sdk.version: STRING(1.8.0) - -> service.name: STRING(shoppingcart) - -> service.instance.id: STRING(instance-12) - InstrumentationLibraryLogs #0 - InstrumentationLibraryMetrics SchemaURL: - InstrumentationLibrary __main__ 0.1 - LogRecord #0 - Timestamp: 2022-01-13 20:37:03.998733056 +0000 UTC - Severity: WARNING - ShortName: - Body: Jail zesty vixen who grabbed pay from quack. - Trace ID: - Span ID: - Flags: 0 - LogRecord #1 - Timestamp: 2022-01-13 20:37:04.082757888 +0000 UTC - Severity: ERROR - ShortName: - Body: The five boxing wizards jump quickly. - Trace ID: - Span ID: - Flags: 0 - LogRecord #2 - Timestamp: 2022-01-13 20:37:04.082979072 +0000 UTC - Severity: ERROR - ShortName: - Body: Hyderabad, we have a major problem. - Trace ID: 63491217958f126f727622e41d4460f3 - Span ID: d90c57d6e1ca4f6c - Flags: 1 + ResourceLog #0 + Resource SchemaURL: + Resource attributes: + -> telemetry.sdk.language: Str(python) + -> telemetry.sdk.name: Str(opentelemetry) + -> telemetry.sdk.version: Str(1.33.0.dev0) + -> service.name: Str(shoppingcart) + -> service.instance.id: Str(instance-12) + ScopeLogs #0 + ScopeLogs SchemaURL: + InstrumentationScope myapp.area2 + LogRecord #0 + ObservedTimestamp: 2025-04-22 12:16:57.315179 +0000 UTC + Timestamp: 2025-04-22 12:16:57.315152896 +0000 UTC + SeverityText: WARN + SeverityNumber: Warn(13) + Body: Str(Jail zesty vixen who grabbed pay from quack.) + Attributes: + -> code.filepath: Str(/Users/jayclifford/Repos/opentelemetry-python/docs/examples/logs/example.py) + -> code.function: Str() + -> code.lineno: Int(47) + Trace ID: + Span ID: + Flags: 0 + LogRecord #1 + ObservedTimestamp: 2025-04-22 12:16:57.31522 +0000 UTC + Timestamp: 2025-04-22 12:16:57.315213056 +0000 UTC + SeverityText: ERROR + SeverityNumber: Error(17) + Body: Str(The five boxing wizards jump quickly.) + Attributes: + -> code.filepath: Str(/Users/jayclifford/Repos/opentelemetry-python/docs/examples/logs/example.py) + -> code.function: Str() + -> code.lineno: Int(48) + Trace ID: + Span ID: + Flags: 0 + LogRecord #2 + ObservedTimestamp: 2025-04-22 12:16:57.315445 +0000 UTC + Timestamp: 2025-04-22 12:16:57.31543808 +0000 UTC + SeverityText: ERROR + SeverityNumber: Error(17) + Body: Str(Hyderabad, we have a major problem.) + Attributes: + -> code.filepath: Str(/Users/jayclifford/Repos/opentelemetry-python/docs/examples/logs/example.py) + -> code.function: Str() + -> code.lineno: Int(61) + Trace ID: 8a6739fffce895e694700944e2faf23e + Span ID: a45337020100cb63 + Flags: 1 + ScopeLogs #1 + ScopeLogs SchemaURL: + InstrumentationScope myapp.area1 + LogRecord #0 + ObservedTimestamp: 2025-04-22 12:16:57.315242 +0000 UTC + Timestamp: 2025-04-22 12:16:57.315234048 +0000 UTC + SeverityText: ERROR + SeverityNumber: Error(17) + Body: Str(I have custom attributes.) + Attributes: + -> user_id: Str(user-123) + -> code.filepath: Str(/Users/jayclifford/Repos/opentelemetry-python/docs/examples/logs/example.py) + -> code.function: Str() + -> code.lineno: Int(53) + Trace ID: + Span ID: + Flags: 0 diff --git a/docs/examples/logs/example.py b/docs/examples/logs/example.py index ba471ea7e69..c782d457533 100644 --- a/docs/examples/logs/example.py +++ b/docs/examples/logs/example.py @@ -47,6 +47,10 @@ logger2.warning("Jail zesty vixen who grabbed pay from quack.") logger2.error("The five boxing wizards jump quickly.") +# Log custom attributes +# Custom attributes are added on a per event basis +user_id = "user-123" +logger1.error("I have custom attributes.", extra={"user_id": user_id}) # Trace context correlation tracer = trace.get_tracer(__name__) diff --git a/docs/examples/logs/otel-collector-config.yaml b/docs/examples/logs/otel-collector-config.yaml index 50d29086415..64495c75091 100644 --- a/docs/examples/logs/otel-collector-config.yaml +++ b/docs/examples/logs/otel-collector-config.yaml @@ -6,7 +6,7 @@ receivers: exporters: debug: - verbosity: debug + verbosity: detailed processors: batch: diff --git a/docs/examples/metrics/reader/README.rst b/docs/examples/metrics/reader/README.rst index 4822fe77669..01a913f22a3 100644 --- a/docs/examples/metrics/reader/README.rst +++ b/docs/examples/metrics/reader/README.rst @@ -6,6 +6,7 @@ These examples show how to customize the metrics that are output by the SDK usin * preferred_aggregation.py: Shows how to configure the preferred aggregation for metric instrument types. * preferred_temporality.py: Shows how to configure the preferred temporality for metric instrument types. * preferred_exemplarfilter.py: Shows how to configure the exemplar filter. +* synchronous_gauge_read.py: Shows how to use `PeriodicExportingMetricReader` in a synchronous manner to explicitly control the collection of metrics. The source files of these examples are available :scm_web:`here `. diff --git a/docs/examples/metrics/reader/synchronous_gauge_read.py b/docs/examples/metrics/reader/synchronous_gauge_read.py new file mode 100644 index 00000000000..d45f7ff00da --- /dev/null +++ b/docs/examples/metrics/reader/synchronous_gauge_read.py @@ -0,0 +1,88 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math +from typing import Iterable + +from opentelemetry.metrics import ( + CallbackOptions, + Observation, + get_meter_provider, + set_meter_provider, +) +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import ( + ConsoleMetricExporter, + PeriodicExportingMetricReader, +) + +temperature = 0.0 +humidity = 0.0 + + +# Function called by the gauge to read the temperature +def read_temperature(options: CallbackOptions) -> Iterable[Observation]: + global temperature + yield Observation(value=temperature, attributes={"room": "living-room"}) + + +# Function called by the gauge to read the humidity +def read_humidity(options: CallbackOptions) -> Iterable[Observation]: + global humidity + yield Observation(value=humidity, attributes={"room": "living-room"}) + + +# Use console exporter for the example +exporter = ConsoleMetricExporter() + +# The PeriodicExportingMetricReader If the time interval is set to math.inf +# the reader will not invoke periodic collection +reader = PeriodicExportingMetricReader( + exporter, + export_interval_millis=math.inf, +) + +provider = MeterProvider(metric_readers=[reader]) +set_meter_provider(provider) + +meter = get_meter_provider().get_meter("synchronous_read", "0.1.2") + +gauge = meter.create_observable_gauge( + name="synchronous_gauge_temperature", + description="Gauge value captured synchronously", + callbacks=[read_temperature], +) + +# Simulate synchronous reading of temperature +print("--- Simulating synchronous reading of temperature ---", flush=True) +temperature = 25.0 +reader.collect() +# Note: The reader will only collect the last value before `collect` is called +print("--- Last value only ---", flush=True) +temperature = 30.0 +temperature = 35.0 +reader.collect() +# Invoking `collect` will read all measurements assigned to the reader +gauge2 = meter.create_observable_gauge( + name="synchronous_gauge_humidity", + description="Gauge value captured synchronously", + callbacks=[read_humidity], +) +print("--- Multiple Measurements ---", flush=True) +temperature = 20.0 +humidity = 50.0 +reader.collect() +# Invoking `force_flush` will read all measurements assigned to the reader +print("--- Invoking force_flush ---", flush=True) +provider.force_flush()