Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4eda681

Browse filesBrowse files
feat: detect monitored resources on all GCP environments (#200)
1 parent 22f836a commit 4eda681
Copy full SHA for 4eda681

File tree

Expand file treeCollapse file tree

8 files changed

+493
-42
lines changed
Filter options
Expand file treeCollapse file tree

8 files changed

+493
-42
lines changed

‎google/cloud/logging_v2/client.py

Copy file name to clipboardExpand all lines: google/cloud/logging_v2/client.py
+13-14Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
from google.cloud.client import ClientWithProject
3030
from google.cloud.environment_vars import DISABLE_GRPC
3131
from google.cloud.logging_v2._helpers import _add_defaults_to_filter
32-
from google.cloud.logging_v2._helpers import retrieve_metadata_server
3332
from google.cloud.logging_v2._http import Connection
3433
from google.cloud.logging_v2._http import _LoggingAPI as JSONLoggingAPI
3534
from google.cloud.logging_v2._http import _MetricsAPI as JSONMetricsAPI
@@ -39,6 +38,9 @@
3938
from google.cloud.logging_v2.handlers import ContainerEngineHandler
4039
from google.cloud.logging_v2.handlers import setup_logging
4140
from google.cloud.logging_v2.handlers.handlers import EXCLUDED_LOGGER_DEFAULTS
41+
from google.cloud.logging_v2.resource import Resource
42+
from google.cloud.logging_v2.handlers._monitored_resources import detect_resource
43+
4244

4345
from google.cloud.logging_v2.logger import Logger
4446
from google.cloud.logging_v2.metric import Metric
@@ -48,14 +50,8 @@
4850
_DISABLE_GRPC = os.getenv(DISABLE_GRPC, False)
4951
_USE_GRPC = _HAVE_GRPC and not _DISABLE_GRPC
5052

51-
_APPENGINE_FLEXIBLE_ENV_VM = "GAE_APPENGINE_HOSTNAME"
52-
"""Environment variable set in App Engine when vm:true is set."""
53-
54-
_APPENGINE_INSTANCE_ID = "GAE_INSTANCE"
55-
"""Environment variable set in App Engine standard and flexible environment."""
56-
57-
_GKE_CLUSTER_NAME = "instance/attributes/cluster-name"
58-
"""Attribute in metadata server when in GKE environment."""
53+
_GAE_RESOURCE_TYPE = "gae_app"
54+
_GKE_RESOURCE_TYPE = "k8s_container"
5955

6056

6157
class Client(ClientWithProject):
@@ -348,17 +344,20 @@ def get_default_handler(self, **kw):
348344
Returns:
349345
logging.Handler: The default log handler based on the environment
350346
"""
351-
gke_cluster_name = retrieve_metadata_server(_GKE_CLUSTER_NAME)
347+
monitored_resource = kw.pop("resource", detect_resource(self.project))
352348

353349
if (
354-
_APPENGINE_FLEXIBLE_ENV_VM in os.environ
355-
or _APPENGINE_INSTANCE_ID in os.environ
350+
isinstance(monitored_resource, Resource)
351+
and monitored_resource.type == _GAE_RESOURCE_TYPE
356352
):
357353
return AppEngineHandler(self, **kw)
358-
elif gke_cluster_name is not None:
354+
elif (
355+
isinstance(monitored_resource, Resource)
356+
and monitored_resource.type == _GKE_RESOURCE_TYPE
357+
):
359358
return ContainerEngineHandler(**kw)
360359
else:
361-
return CloudLoggingHandler(self, **kw)
360+
return CloudLoggingHandler(self, resource=monitored_resource, **kw)
362361

363362
def setup_logging(
364363
self, *, log_level=logging.INFO, excluded_loggers=EXCLUDED_LOGGER_DEFAULTS, **kw
+195Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Copyright 2021 Google LLC
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+
import os
16+
17+
from google.cloud.logging_v2.resource import Resource
18+
from google.cloud.logging_v2._helpers import retrieve_metadata_server
19+
20+
_GAE_SERVICE_ENV = "GAE_SERVICE"
21+
_GAE_VERSION_ENV = "GAE_VERSION"
22+
_GAE_INSTANCE_ENV = "GAE_INSTANCE"
23+
_GAE_ENV_VARS = [_GAE_SERVICE_ENV, _GAE_VERSION_ENV, _GAE_INSTANCE_ENV]
24+
"""Environment variables set in App Engine environment."""
25+
26+
_CLOUD_RUN_SERVICE_ID = "K_SERVICE"
27+
_CLOUD_RUN_REVISION_ID = "K_REVISION"
28+
_CLOUD_RUN_CONFIGURATION_ID = "K_CONFIGURATION"
29+
_CLOUD_RUN_ENV_VARS = [
30+
_CLOUD_RUN_SERVICE_ID,
31+
_CLOUD_RUN_REVISION_ID,
32+
_CLOUD_RUN_CONFIGURATION_ID,
33+
]
34+
"""Environment variables set in Cloud Run environment."""
35+
36+
_FUNCTION_TARGET = "FUNCTION_TARGET"
37+
_FUNCTION_SIGNATURE = "FUNCTION_SIGNATURE_TYPE"
38+
_FUNCTION_NAME = "FUNCTION_NAME"
39+
_FUNCTION_REGION = "FUNCTION_REGION"
40+
_FUNCTION_ENTRY = "ENTRY_POINT"
41+
_FUNCTION_ENV_VARS = [_FUNCTION_TARGET, _FUNCTION_SIGNATURE, _CLOUD_RUN_SERVICE_ID]
42+
_LEGACY_FUNCTION_ENV_VARS = [_FUNCTION_NAME, _FUNCTION_REGION, _FUNCTION_ENTRY]
43+
"""Environment variables set in Cloud Functions environments."""
44+
45+
46+
_REGION_ID = "instance/region"
47+
_ZONE_ID = "instance/zone"
48+
_GCE_INSTANCE_ID = "instance/id"
49+
"""Attribute in metadata server for compute region and instance."""
50+
51+
_GKE_CLUSTER_NAME = "instance/attributes/cluster-name"
52+
"""Attribute in metadata server when in GKE environment."""
53+
54+
55+
def _create_functions_resource(project):
56+
"""Create a standardized Cloud Functions resource.
57+
Args:
58+
project (str): The project ID to pass on to the resource
59+
Returns:
60+
google.cloud.logging.Resource
61+
"""
62+
region = retrieve_metadata_server(_REGION_ID)
63+
if _FUNCTION_NAME in os.environ:
64+
function_name = os.environ.get(_FUNCTION_NAME)
65+
elif _CLOUD_RUN_SERVICE_ID in os.environ:
66+
function_name = os.environ.get(_CLOUD_RUN_SERVICE_ID)
67+
else:
68+
function_name = ""
69+
resource = Resource(
70+
type="cloud_function",
71+
labels={
72+
"project_id": project,
73+
"function_name": function_name,
74+
"region": region if region else "",
75+
},
76+
)
77+
return resource
78+
79+
80+
def _create_kubernetes_resource(project):
81+
"""Create a standardized Kubernetes resource.
82+
Args:
83+
project (str): The project ID to pass on to the resource
84+
Returns:
85+
google.cloud.logging.Resource
86+
"""
87+
zone = retrieve_metadata_server(_ZONE_ID)
88+
cluster_name = retrieve_metadata_server(_GKE_CLUSTER_NAME)
89+
90+
resource = Resource(
91+
type="k8s_container",
92+
labels={
93+
"project_id": project,
94+
"location": zone if zone else "",
95+
"cluster_name": cluster_name if cluster_name else "",
96+
},
97+
)
98+
return resource
99+
100+
101+
def _create_compute_resource(project):
102+
"""Create a standardized Compute Engine resource.
103+
Args:
104+
project (str): The project ID to pass on to the resource
105+
Returns:
106+
google.cloud.logging.Resource
107+
"""
108+
instance = retrieve_metadata_server(_GCE_INSTANCE_ID)
109+
zone = retrieve_metadata_server(_ZONE_ID)
110+
resource = Resource(
111+
type="gce_instance",
112+
labels={
113+
"project_id": project,
114+
"instance_id": instance if instance else "",
115+
"zone": zone if zone else "",
116+
},
117+
)
118+
return resource
119+
120+
121+
def _create_cloud_run_resource(project):
122+
"""Create a standardized Cloud Run resource.
123+
Args:
124+
project (str): The project ID to pass on to the resource
125+
Returns:
126+
google.cloud.logging.Resource
127+
"""
128+
region = retrieve_metadata_server(_REGION_ID)
129+
resource = Resource(
130+
type="cloud_run_revision",
131+
labels={
132+
"project_id": project,
133+
"service_name": os.environ.get(_CLOUD_RUN_SERVICE_ID, ""),
134+
"revision_name": os.environ.get(_CLOUD_RUN_REVISION_ID, ""),
135+
"location": region if region else "",
136+
"configuration_name": os.environ.get(_CLOUD_RUN_CONFIGURATION_ID, ""),
137+
},
138+
)
139+
return resource
140+
141+
142+
def _create_app_engine_resource(project):
143+
"""Create a standardized App Engine resource.
144+
Args:
145+
project (str): The project ID to pass on to the resource
146+
Returns:
147+
google.cloud.logging.Resource
148+
"""
149+
zone = retrieve_metadata_server(_ZONE_ID)
150+
resource = Resource(
151+
type="gae_app",
152+
labels={
153+
"project_id": project,
154+
"module_id": os.environ.get(_GAE_SERVICE_ENV, ""),
155+
"version_id": os.environ.get(_GAE_VERSION_ENV, ""),
156+
"zone": zone if zone else "",
157+
},
158+
)
159+
return resource
160+
161+
162+
def _create_global_resource(project):
163+
return Resource(type="global", labels={"project_id": project})
164+
165+
166+
def detect_resource(project):
167+
"""Return the default monitored resource based on the local environment.
168+
Args:
169+
project (str): The project ID to pass on to the resource
170+
Returns:
171+
google.cloud.logging.Resource: The default resource based on the environment
172+
"""
173+
gke_cluster_name = retrieve_metadata_server(_GKE_CLUSTER_NAME)
174+
gce_instance_name = retrieve_metadata_server(_GCE_INSTANCE_ID)
175+
176+
if all([env in os.environ for env in _GAE_ENV_VARS]):
177+
# App Engine Flex or Standard
178+
return _create_app_engine_resource(project)
179+
elif gke_cluster_name is not None:
180+
# Kubernetes Engine
181+
return _create_kubernetes_resource(project)
182+
elif all([env in os.environ for env in _LEGACY_FUNCTION_ENV_VARS]) or all(
183+
[env in os.environ for env in _FUNCTION_ENV_VARS]
184+
):
185+
# Cloud Functions
186+
return _create_functions_resource(project)
187+
elif all([env in os.environ for env in _CLOUD_RUN_ENV_VARS]):
188+
# Cloud Run
189+
return _create_cloud_run_resource(project)
190+
elif gce_instance_name is not None:
191+
# Compute Engine
192+
return _create_compute_resource(project)
193+
else:
194+
# use generic global resource
195+
return _create_global_resource(project)

‎google/cloud/logging_v2/handlers/app_engine.py

Copy file name to clipboardExpand all lines: google/cloud/logging_v2/handlers/app_engine.py
+4-10Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import os
2323

2424
from google.cloud.logging_v2.handlers._helpers import get_request_data
25+
from google.cloud.logging_v2.handlers._monitored_resources import (
26+
_create_app_engine_resource,
27+
)
2528
from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport
26-
from google.cloud.logging_v2.resource import Resource
2729

2830
_DEFAULT_GAE_LOGGER_NAME = "app"
2931

@@ -75,15 +77,7 @@ def get_gae_resource(self):
7577
Returns:
7678
google.cloud.logging_v2.resource.Resource: Monitored resource for GAE.
7779
"""
78-
gae_resource = Resource(
79-
type="gae_app",
80-
labels={
81-
"project_id": self.project_id,
82-
"module_id": self.module_id,
83-
"version_id": self.version_id,
84-
},
85-
)
86-
return gae_resource
80+
return _create_app_engine_resource(self.project_id)
8781

8882
def get_gae_labels(self):
8983
"""Return the labels for GAE app.

‎google/cloud/logging_v2/handlers/handlers.py

Copy file name to clipboardExpand all lines: google/cloud/logging_v2/handlers/handlers.py
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import logging
1818

1919
from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport
20-
from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE
20+
from google.cloud.logging_v2.handlers._monitored_resources import detect_resource
2121

2222
DEFAULT_LOGGER_NAME = "python"
2323

@@ -59,7 +59,7 @@ def __init__(
5959
*,
6060
name=DEFAULT_LOGGER_NAME,
6161
transport=BackgroundThreadTransport,
62-
resource=_GLOBAL_RESOURCE,
62+
resource=None,
6363
labels=None,
6464
stream=None,
6565
):
@@ -78,12 +78,15 @@ def __init__(
7878
:class:`.BackgroundThreadTransport`. The other
7979
option is :class:`.SyncTransport`.
8080
resource (~logging_v2.resource.Resource):
81-
Resource for this Handler. Defaults to ``GLOBAL_RESOURCE``.
81+
Resource for this Handler. If not given, will be inferred from the environment.
8282
labels (Optional[dict]): Monitored resource of the entry, defaults
8383
to the global resource type.
8484
stream (Optional[IO]): Stream to be used by the handler.
8585
"""
8686
super(CloudLoggingHandler, self).__init__(stream)
87+
if not resource:
88+
# infer the correct monitored resource from the local environment
89+
resource = detect_resource(client.project)
8790
self.name = name
8891
self.client = client
8992
self.transport = transport(client, name)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.