Skip to content

Commit 0f47f66

Browse files
anuraagaxrmx
andauthored
feat(logs): implement log creation metric (#4935)
* feat(logs): implement log creation metric * Changelog * keyword args * Absolute imports --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
1 parent cc0b183 commit 0f47f66

File tree

6 files changed

+104
-2
lines changed

6 files changed

+104
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818
([#4958](https://github.com/open-telemetry/opentelemetry-python/pull/4958))
1919
- `opentelemetry-sdk`: fix type annotations on `MetricReader` and related types
2020
([#4938](https://github.com/open-telemetry/opentelemetry-python/pull/4938/))
21+
- Implement log creation metric
22+
([#4935](https://github.com/open-telemetry/opentelemetry-python/pull/4935))
2123
- `opentelemetry-sdk`: upgrade vendored OTel configuration schema from v1.0.0-rc.3 to v1.0.0
2224
([#4965](https://github.com/open-telemetry/opentelemetry-python/pull/4965))
2325

opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
from opentelemetry.attributes import _VALID_ANY_VALUE_TYPES, BoundedAttributes
4343
from opentelemetry.context import get_current
4444
from opentelemetry.context.context import Context
45+
from opentelemetry.metrics import MeterProvider, get_meter_provider
46+
from opentelemetry.sdk._logs._internal._logger_metrics import LoggerMetrics
4547
from opentelemetry.sdk.environment_variables import (
4648
OTEL_ATTRIBUTE_COUNT_LIMIT,
4749
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT,
@@ -638,6 +640,8 @@ def __init__(
638640
ConcurrentMultiLogRecordProcessor,
639641
],
640642
instrumentation_scope: InstrumentationScope,
643+
*,
644+
logger_metrics: LoggerMetrics,
641645
):
642646
super().__init__(
643647
instrumentation_scope.name,
@@ -648,6 +652,7 @@ def __init__(
648652
self._resource = resource
649653
self._multi_log_record_processor = multi_log_record_processor
650654
self._instrumentation_scope = instrumentation_scope
655+
self._logger_metrics = logger_metrics
651656

652657
@property
653658
def resource(self):
@@ -700,6 +705,7 @@ def emit(
700705
instrumentation_scope=self._instrumentation_scope,
701706
)
702707

708+
self._logger_metrics.emit_log()
703709
self._multi_log_record_processor.on_emit(writable_record)
704710

705711

@@ -711,6 +717,8 @@ def __init__(
711717
multi_log_record_processor: SynchronousMultiLogRecordProcessor
712718
| ConcurrentMultiLogRecordProcessor
713719
| None = None,
720+
*,
721+
meter_provider: MeterProvider | None = None,
714722
):
715723
if resource is None:
716724
self._resource = Resource.create({})
@@ -719,6 +727,9 @@ def __init__(
719727
self._multi_log_record_processor = (
720728
multi_log_record_processor or SynchronousMultiLogRecordProcessor()
721729
)
730+
self._logger_metrics = LoggerMetrics(
731+
meter_provider or get_meter_provider()
732+
)
722733
disabled = environ.get(OTEL_SDK_DISABLED, "")
723734
self._disabled = disabled.lower().strip() == "true"
724735
self._at_exit_handler = None
@@ -747,6 +758,7 @@ def _get_logger_no_cache(
747758
schema_url,
748759
attributes,
749760
),
761+
logger_metrics=self._logger_metrics,
750762
)
751763

752764
def _get_logger_cached(
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from opentelemetry import metrics as metrics_api
16+
from opentelemetry.semconv._incubating.metrics.otel_metrics import (
17+
create_otel_sdk_log_created,
18+
)
19+
20+
21+
class LoggerMetrics:
22+
def __init__(self, meter_provider: metrics_api.MeterProvider) -> None:
23+
meter = meter_provider.get_meter("opentelemetry-sdk")
24+
self._created_logs = create_otel_sdk_log_created(meter)
25+
26+
def emit_log(self) -> None:
27+
self._created_logs.add(1)

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
)
6565
from opentelemetry.sdk.resources import Resource
6666
from opentelemetry.sdk.trace import sampling
67+
from opentelemetry.sdk.trace._tracer_metrics import TracerMetrics
6768
from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator
6869
from opentelemetry.sdk.util import BoundedList
6970
from opentelemetry.sdk.util.instrumentation import (
@@ -81,8 +82,6 @@
8182
from opentelemetry.util import types
8283
from opentelemetry.util._decorator import _agnosticcontextmanager
8384

84-
from ._tracer_metrics import TracerMetrics
85-
8685
logger = logging.getLogger(__name__)
8786

8887
_DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT = 128

opentelemetry-sdk/tests/logs/test_logs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919

2020
from opentelemetry._logs import LogRecord, SeverityNumber
2121
from opentelemetry.context import get_current
22+
from opentelemetry.metrics import NoOpMeterProvider
2223
from opentelemetry.sdk._logs import (
2324
Logger,
2425
LoggerProvider,
2526
ReadableLogRecord,
2627
)
2728
from opentelemetry.sdk._logs._internal import (
29+
LoggerMetrics,
2830
NoOpLogger,
2931
SynchronousMultiLogRecordProcessor,
3032
)
@@ -148,6 +150,7 @@ def _get_logger():
148150
"schema_url",
149151
{"an": "attribute"},
150152
),
153+
logger_metrics=LoggerMetrics(NoOpMeterProvider()),
151154
)
152155
return logger, log_record_processor_mock
153156

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from unittest import TestCase
16+
17+
from opentelemetry.sdk._logs import LoggerProvider
18+
from opentelemetry.sdk.metrics import MeterProvider
19+
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
20+
21+
22+
class TestLoggerProviderMetrics(TestCase):
23+
def setUp(self):
24+
self.metric_reader = InMemoryMetricReader()
25+
self.meter_provider = MeterProvider(
26+
metric_readers=[self.metric_reader]
27+
)
28+
29+
def tearDown(self):
30+
self.meter_provider.shutdown()
31+
32+
def assert_created_logs(self, metric_data, value, attrs):
33+
metrics = metric_data.resource_metrics[0].scope_metrics[0].metrics
34+
created_logs_metric = next(
35+
(m for m in metrics if m.name == "otel.sdk.log.created"), None
36+
)
37+
self.assertIsNotNone(created_logs_metric)
38+
self.assertEqual(created_logs_metric.data.data_points[0].value, value)
39+
self.assertDictEqual(
40+
created_logs_metric.data.data_points[0].attributes, attrs
41+
)
42+
43+
def test_create_logs(self):
44+
logger_provider = LoggerProvider(meter_provider=self.meter_provider)
45+
logger = logger_provider.get_logger("test")
46+
logger.emit(body="log1")
47+
metric_data = self.metric_reader.get_metrics_data()
48+
self.assert_created_logs(
49+
metric_data,
50+
1,
51+
{},
52+
)
53+
logger.emit(body="log2")
54+
metric_data = self.metric_reader.get_metrics_data()
55+
self.assert_created_logs(
56+
metric_data,
57+
2,
58+
{},
59+
)

0 commit comments

Comments
 (0)