Skip to content

Commit 68539d5

Browse files
committed
Address lint issues.
1 parent a9e3cb5 commit 68539d5

File tree

9 files changed

+228
-37
lines changed

9 files changed

+228
-37
lines changed

opentelemetry-configurator-gcp/src/opentelemetry/configurator/gcp/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
"""Open Telemetry configurator for Google Cloud
2+
3+
This package provides the 'OpenTelemetryGcpConfigurator' which simplifies configuration of the
4+
Open Telemetry library to route logs, traces, and metrics to Google Cloud Observability products
5+
such as Cloud Logging, Cloud Trace, and Cloud Monitoring.
6+
7+
The OpenTelemetryGcpConfigurator can be invoked directly as in:
8+
9+
from opentelemetry.configurator.gcp import OpenTelemetryGcpConfigurator
10+
11+
OpenTelemetryGcpConfigurator().configure()
12+
13+
It can also be invoked automatically from the "opentelemetry-instrument" command,
14+
which is part of the Open Telemetry zero-code instrumentation for Python. To
15+
invoke it automatically, simply supply "--configurator=gcp" as a commandline
16+
flag to the "opentelemetry-instrument" command. As an example:
17+
18+
opentelemetry-instrument \
19+
--configurator=gcp \
20+
python \
21+
the/path/to/your/script.py
22+
23+
This automatic wiring is implemented using the registration mechanism in "pyproject.toml";
24+
in particular, the "[project.entry-points.opentelemetry_configurator]" entry in that file
25+
makes this component known to the auto-instrumentation system. And it being a class
26+
that defines a "configure(self, **kwargs)" method makes it compatible with that API.
27+
."""
28+
129
from .configurator import OpenTelemetryGcpConfigurator
230
from .version import __version__
331

opentelemetry-configurator-gcp/src/opentelemetry/configurator/gcp/configurator.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
"""Defines the 'OpenTelemetryGcpConfigurator' class for simplifying Open Telemetry setup for GCP."""
12
from typing import Optional, Callable
23

34
from .flags import (
@@ -18,14 +19,52 @@ def _bool_with_flag_default(value: Optional[bool], flag_lookup: Callable[None, b
1819
return flag_lookup()
1920

2021

21-
class OpenTelemetryGcpConfigurator:
22+
class OpenTelemetryGcpConfigurator: # pylint: disable=too-few-public-methods
23+
"""A class that can be used as a configurator in Open Telemetry zero-code instrumentation."""
2224

2325
def __init__(
2426
self,
2527
metrics_exporter_enabled:Optional[bool]=None,
2628
logs_exporter_enabled:Optional[bool]=None,
2729
traces_exporter_enabled:Optional[bool]=None,
2830
resource_detector_enabled:Optional[bool]=None):
31+
"""Initialize the configurator with optional parameters to direct the behavior.
32+
33+
No arguments are supplied when invoked from the zero-configuration system.
34+
35+
Args:
36+
metrics_exporter_enabled: whether to enable metrics export. If unset,
37+
falls back to an environment variable, allowing this argument to
38+
be supplied even in zero-configuration scenarios. If that, too,
39+
is unset, then the metrics export will be enabled.
40+
41+
logs_exporter_enabled: whether to enable logs export. If unset,
42+
falls back to an environment variable, allowing this argument to
43+
be supplied even in zero-configuration scenarios. If that, too,
44+
is unset, then the logs export will be enabled.
45+
46+
traces_exporter_enabled: whether to enable trace export. If unset,
47+
falls back to an environment variable, allowing this argument to
48+
be supplied even in zero-configuration scenarios. If that, too,
49+
is unset, then the trace export will be enabled.
50+
51+
resource_detector_enabled: whether to enable the GCP resource
52+
detector (which is useful only when the code is running in
53+
a GCP environment). If unset, falls back to an environment variable,
54+
allowing this argument to be supplied even in zero-configuration
55+
scenarios. If that, too, is unset, then the code will attempt
56+
to determine if the code is likely deployed in GCP or not
57+
based on the environment to enable the detector or not.
58+
59+
Environment Variables:
60+
61+
The following environment variables affect the defaults:
62+
63+
- OTEL_GCP_METRICS_EXPORTER_ENABLED
64+
- OTEL_GCP_LOGS_EXPORTER_ENABLED
65+
- OTEL_GCP_TRACES_EXPORTER_ENABLED
66+
- OTEL_GCP_RESOURCE_DETECTOR_ENABLED
67+
"""
2968
self._metrics_exporter_enabled = _bool_with_flag_default(
3069
metrics_exporter_enabled, is_metrics_exporter_enabled)
3170
self._logs_exporter_enabled = _bool_with_flag_default(
@@ -35,12 +74,23 @@ def __init__(
3574
self._resource_detector_enabled = _bool_with_flag_default(
3675
resource_detector_enabled, is_resource_detector_enabled)
3776

38-
def configure(self, **kwargs):
77+
def configure(self, **unused_kwargs):
78+
"""Configure the Open Telemetry library to talk to GCP backends.
79+
80+
This function configures the Open Telemetry library to talk to GCP
81+
backends, subject to the class initialization parameters.
82+
83+
Although this class does not inherit any explicit interface, this
84+
function should be treated like an inherited method in that its
85+
signature is dictated by the Open Telemetry auto-instrumentation.
86+
87+
Uses **unused_kwargs to allow future iterations of the Open Telemetry
88+
library to introduce additional keyword arguments without breaking.
89+
"""
3990
resource = get_resource(include_gcp_detector=self._resource_detector_enabled)
4091
if self._metrics_exporter_enabled:
4192
configure_metrics_exporter(resource=resource)
4293
if self._logs_exporter_enabled:
4394
configure_logs_exporter(resource=resource)
4495
if self._traces_exporter_enabled:
4596
configure_traces_exporter(resource=resource)
46-

opentelemetry-configurator-gcp/src/opentelemetry/configurator/gcp/flags.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
1+
"""Provides functions for querying the configuration of this library.
2+
3+
Other modules in this package use the 'flags' library to obtain default
4+
behaviors where the behavior has not been explicitly specified.
5+
"""
6+
from typing import Optional
7+
18
import os
29

310
from . import gcloud_env
411

512

6-
def _str_to_optional_bool(s):
13+
def _str_to_optional_bool(s: str) -> Optional[bool]:
14+
"""Converts a string to an optional boolean.
15+
16+
Args:
17+
s: the string to convert to a boolean
18+
19+
Returns:
20+
A boolean if the value is clearly false or clearly true.
21+
None if the string does not match a known true/false pattern.
22+
"""
723
lower_s = s.lower()
824
if lower_s in ['1', 'true', 't', 'y', 'yes', 'on']:
925
return True
@@ -12,7 +28,17 @@ def _str_to_optional_bool(s):
1228
return None
1329

1430

15-
def _get_bool_flag_from_env(env_var_name, default_value=None):
31+
def _get_bool_flag_from_env(env_var_name: str, default_value:Optional[bool]=None) -> Optional[bool]:
32+
"""Retrieves a boolean value from an environment variable.
33+
34+
Args:
35+
env_var_name: The name of the environment variable to retrieve.
36+
default_value: The value to return if unset or has a non-bool value.
37+
38+
Returns:
39+
The boolean value of the environment variable if set and valid.
40+
Otherwise, falls back to the supplied default value.
41+
"""
1642
s = os.getenv(env_var_name)
1743
if s is None:
1844
return default_value
@@ -23,27 +49,31 @@ def _get_bool_flag_from_env(env_var_name, default_value=None):
2349

2450

2551
def is_metrics_exporter_enabled():
52+
"""Returns whether to enable metrics exporting by default."""
2653
return _get_bool_flag_from_env(
2754
'OTEL_GCP_METRICS_EXPORTER_ENABLED',
2855
default_value=True
2956
)
3057

3158

3259
def is_logs_exporter_enabled():
60+
"""Returns whether to enable logs exporting by default."""
3361
return _get_bool_flag_from_env(
3462
'OTEL_GCP_LOGS_EXPORTER_ENABLED',
3563
default_value=True
3664
)
3765

3866

3967
def is_traces_exporter_enabled():
68+
"""Returns whether to enable trace exporting by default."""
4069
return _get_bool_flag_from_env(
4170
'OTEL_GCP_TRACES_EXPORTER_ENABLED',
4271
default_value=True
4372
)
4473

4574

4675
def is_resource_detector_enabled():
76+
"""Returns whether to enable the GCP resource detector by default."""
4777
result = _get_bool_flag_from_env(
4878
'OTEL_GCP_RESOURCE_DETECTOR_ENABLED')
4979
if result is not None:

opentelemetry-configurator-gcp/src/opentelemetry/configurator/gcp/gcloud_env.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,90 @@
1+
"""Provides 'is_running_on_gcp' to determine whether to enable the GCP resource detector."""
12
import os
23
import os.path
34
import socket
45

56

67
def _can_resolve_metadata_server():
8+
"""Returns whether the GCP metadata server address can be resolved.
9+
10+
On GCP, there is a special 'metadata.google.internal' DNS name that is
11+
used to supply metadata about the environment. Although it is possible
12+
to edit "/etc/hosts" to introduce a similarly-named service outside
13+
of GCP, the existence of this name is a strong hint of running in GCP.
14+
"""
715
try:
816
socket.getaddrinfo('metadata.google.internal', 80)
917
return True
10-
except:
18+
except OSError:
1119
return False
1220

1321

1422
def _is_likely_gae():
23+
"""Returns whether env vars indicate a GAE environment.
24+
25+
The Google App Engine documentation calls out several of these
26+
environment variables as being automatically setup by the runtime.
27+
28+
Although it is possible to set these manually outside of GAE, the
29+
presence of these in conjunction with the presence of the metadata
30+
server provides a strong hint of running within GAE.
31+
"""
1532
return (('GAE_APPLICATION' in os.environ) and
1633
('GAE_DEPLOYMENT_ID' in os.environ) and
1734
('GAE_SERVICE' in os.environ))
1835

1936

2037
def _is_likely_cloud_run():
38+
"""Returns whether env vars indicate a Cloud Run environment.
39+
40+
The Cloud Run documentation calls out several of these
41+
environment variables as being automatically setup by the runtime.
42+
43+
Some of these may aslo be present when running K-Native in Kubernetes;
44+
however, the presence of these environment variables in conjunction
45+
with the presence of the metadata server provide a strong hint
46+
that the code is running inside of Cloud Run.
47+
"""
2148
return (
2249
('K_SERVICE' in os.environ) and
2350
('K_REVISION' in os.environ) and
2451
('K_CONFIGURATION' in os.environ))
2552

2653

2754
def _is_likely_gce():
55+
"""Returns whether there is evidence of running in GCE.
56+
57+
The given pre-supplied paths are called out in GCE documentation
58+
and are not likely to exist in other environments. In conjunction
59+
with the existing of the metadata server, the checks here provide
60+
supportive evidence of running within a GCE environment.
61+
"""
2862
return os.path.exists('/run/google-mds-mtls')
2963

3064

3165
def _is_likely_gke():
32-
return ('KUBERNETES_SERVICE_HOST' in os.environ)
66+
"""Returns whether there is evidence of runing in GKE.
67+
68+
Although also applicable to Kubernetes outside of GCP,
69+
the evidence of Kubernetes in conjunction with the presence
70+
of the GCP metadata server strongly hints at GKE.
71+
"""
72+
return 'KUBERNETES_SERVICE_HOST' in os.environ
3373

3474

3575
def is_running_on_gcp():
76+
"""Returns whether the code is probably running on GCP.
77+
78+
This is not intended to be 100% bullet proof nor
79+
comprehensive of the entire GCP ecosystem; rather, it
80+
is intended to be "good enough" to determine whether to
81+
pay the additional costs of GCP resource detection.
82+
83+
That is, it should ideally be light-weight (if it's too
84+
expensive, you might as well always do GCP resource
85+
detection), and it should ideally cover the subset
86+
of GCP environments for which resource detction exists.
87+
"""
3688
return (
3789
_can_resolve_metadata_server() and
3890
(
@@ -42,4 +94,3 @@ def is_running_on_gcp():
4294
_is_likely_gae()
4395
)
4496
)
45-
Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import sys
1+
"""Provides a mechanism to configure the Logs Exporter for GCP."""
22
import os
33
import os.path
44
import logging
@@ -19,21 +19,6 @@
1919
}
2020

2121

22-
def _get_entrypoint_script_name():
23-
main_script_path = sys.argv[0]
24-
if not main_script_path:
25-
main_script_path = sys.executable
26-
simple_script_name = os.path.basename(main_script_path).rstrip('.py')
27-
return simple_script_name
28-
29-
30-
def _get_log_name():
31-
log_name = os.getenv('OTEL_GCP_LOG_NAME')
32-
if log_name:
33-
return log_name
34-
return _get_entrypoint_script_name()
35-
36-
3722
def _get_log_level():
3823
level = os.getenv(otel_env_vars.OTEL_LOG_LEVEL)
3924
if level is None:
@@ -45,9 +30,19 @@ def _get_log_level():
4530

4631

4732
def configure_logs_exporter(resource=None):
33+
"""Configures the Cloud Logging exporter.
34+
35+
Args:
36+
resource: the resource to include in the emitted logs
37+
38+
Effects:
39+
- Invokes the 'set_logger_provider' function with an
40+
exporter that will cause OTel logs to get routed to GCP.
41+
- Modifies the built-in 'logging' component in Python to
42+
route built-in Python logs to Open Telemetry.
43+
"""
4844
provider = LoggerProvider(resource=resource)
49-
provider.add_log_record_processor(BatchLogRecordProcessor(
50-
CloudLoggingExporter(default_log_name=_get_log_name())))
45+
provider.add_log_record_processor(BatchLogRecordProcessor(CloudLoggingExporter()))
5146
set_logger_provider(provider)
5247
handler = LoggingHandler(level=_get_log_level(), logger_provider=provider)
5348
logging.getLogger().addHandler(handler)

opentelemetry-configurator-gcp/src/opentelemetry/configurator/gcp/metrics.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
"""Provides a mechanism to configure the Metrics Exporter for GCP."""
12
from opentelemetry import metrics as otel_metrics
23
from opentelemetry.sdk import metrics as otel_metrics_sdk
34
from opentelemetry.sdk.metrics import export as otel_metrics_sdk_export
45
from opentelemetry.exporter import cloud_monitoring as otel_cloud_monitoring
56

67

78
def configure_metrics_exporter(resource=None):
9+
"""Configures the Open Telemetry metrics library to write to Cloud Monitoring.
10+
11+
Args:
12+
resource: The resource to use when writing metrics.
13+
14+
Effects:
15+
Calls 'set_meter_provider' with a MeterProvider that will cause
16+
Open Telemetry metrics to get routed to Cloud Monitoring.
17+
"""
818
provider = otel_metrics_sdk.MeterProvider(
919
metric_readers=[
1020
otel_metrics_sdk_export.PeriodicExportingMetricReader(
Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
1+
"""Provides a mechanism to configure the Resource Detector for GCP."""
12
from opentelemetry.sdk import resources as otel_resources_sdk
23
from opentelemetry.resourcedetector import gcp_resource_detector
34

45

56
def get_resource(include_gcp_detector=False):
6-
detectors = [
7-
otel_resources_sdk.OTELResourceDetector(),
8-
otel_resources_sdk.ProcessResourceDetector(),
9-
otel_resources_sdk.OsResourceDetector(),
10-
]
11-
if include_gcp_detector:
12-
detectors.append(gcp_resource_detector.GoogleCloudResourceDetector())
13-
return otel_resources_sdk.get_aggregated_resources(detectors)
7+
"""Calculate the resource to use in Open Telemetry signals.
8+
9+
Args:
10+
include_gcp_detector: Whether to merge in information about
11+
the GCP environment in which the code is running.
12+
13+
Effects:
14+
Gathers information about the current environment to produce
15+
a resource that summarizes the running environment.
16+
17+
Returns:
18+
A resource that summarizes the environment.
19+
"""
20+
detectors = [
21+
otel_resources_sdk.OTELResourceDetector(),
22+
otel_resources_sdk.ProcessResourceDetector(),
23+
otel_resources_sdk.OsResourceDetector(),
24+
]
25+
if include_gcp_detector:
26+
detectors.append(gcp_resource_detector.GoogleCloudResourceDetector())
27+
return otel_resources_sdk.get_aggregated_resources(detectors)

0 commit comments

Comments
 (0)