123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- // Copyright 2021 Google LLC
- //
- // 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.
- package logging
- import (
- "runtime"
- "strings"
- "sync"
- "cloud.google.com/go/logging/internal"
- mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
- )
- // CommonResource sets the monitored resource associated with all log entries
- // written from a Logger. If not provided, the resource is automatically
- // detected based on the running environment (on GCE, GCR, GCF and GAE Standard only).
- // This value can be overridden per-entry by setting an Entry's Resource field.
- func CommonResource(r *mrpb.MonitoredResource) LoggerOption { return commonResource{r} }
- type commonResource struct{ *mrpb.MonitoredResource }
- func (r commonResource) set(l *Logger) { l.commonResource = r.MonitoredResource }
- type resource struct {
- pb *mrpb.MonitoredResource
- attrs internal.ResourceAttributesGetter
- once *sync.Once
- }
- var detectedResource = &resource{
- attrs: internal.ResourceAttributes(),
- once: new(sync.Once),
- }
- func (r *resource) metadataProjectID() string {
- return r.attrs.Metadata("project/project-id")
- }
- func (r *resource) metadataZone() string {
- zone := r.attrs.Metadata("instance/zone")
- if zone != "" {
- return zone[strings.LastIndex(zone, "/")+1:]
- }
- return ""
- }
- func (r *resource) metadataRegion() string {
- region := r.attrs.Metadata("instance/region")
- if region != "" {
- return region[strings.LastIndex(region, "/")+1:]
- }
- return ""
- }
- // isMetadataActive queries valid response on "/computeMetadata/v1/" URL
- func (r *resource) isMetadataActive() bool {
- data := r.attrs.Metadata("")
- return data != ""
- }
- // isAppEngine returns true for both standard and flex
- func (r *resource) isAppEngine() bool {
- service := r.attrs.EnvVar("GAE_SERVICE")
- version := r.attrs.EnvVar("GAE_VERSION")
- instance := r.attrs.EnvVar("GAE_INSTANCE")
- return service != "" && version != "" && instance != ""
- }
- func detectAppEngineResource() *mrpb.MonitoredResource {
- projectID := detectedResource.metadataProjectID()
- if projectID == "" {
- projectID = detectedResource.attrs.EnvVar("GOOGLE_CLOUD_PROJECT")
- }
- if projectID == "" {
- return nil
- }
- zone := detectedResource.metadataZone()
- service := detectedResource.attrs.EnvVar("GAE_SERVICE")
- version := detectedResource.attrs.EnvVar("GAE_VERSION")
- return &mrpb.MonitoredResource{
- Type: "gae_app",
- Labels: map[string]string{
- "project_id": projectID,
- "module_id": service,
- "version_id": version,
- "zone": zone,
- },
- }
- }
- func (r *resource) isCloudFunction() bool {
- target := r.attrs.EnvVar("FUNCTION_TARGET")
- signature := r.attrs.EnvVar("FUNCTION_SIGNATURE_TYPE")
- // note that this envvar is also present in Cloud Run environments
- service := r.attrs.EnvVar("K_SERVICE")
- return target != "" && signature != "" && service != ""
- }
- func detectCloudFunction() *mrpb.MonitoredResource {
- projectID := detectedResource.metadataProjectID()
- if projectID == "" {
- return nil
- }
- region := detectedResource.metadataRegion()
- functionName := detectedResource.attrs.EnvVar("K_SERVICE")
- return &mrpb.MonitoredResource{
- Type: "cloud_function",
- Labels: map[string]string{
- "project_id": projectID,
- "region": region,
- "function_name": functionName,
- },
- }
- }
- func (r *resource) isCloudRun() bool {
- config := r.attrs.EnvVar("K_CONFIGURATION")
- // note that this envvar is also present in Cloud Function environments
- service := r.attrs.EnvVar("K_SERVICE")
- revision := r.attrs.EnvVar("K_REVISION")
- return config != "" && service != "" && revision != ""
- }
- func detectCloudRunResource() *mrpb.MonitoredResource {
- projectID := detectedResource.metadataProjectID()
- if projectID == "" {
- return nil
- }
- region := detectedResource.metadataRegion()
- config := detectedResource.attrs.EnvVar("K_CONFIGURATION")
- service := detectedResource.attrs.EnvVar("K_SERVICE")
- revision := detectedResource.attrs.EnvVar("K_REVISION")
- return &mrpb.MonitoredResource{
- Type: "cloud_run_revision",
- Labels: map[string]string{
- "project_id": projectID,
- "location": region,
- "service_name": service,
- "revision_name": revision,
- "configuration_name": config,
- },
- }
- }
- func (r *resource) isKubernetesEngine() bool {
- clusterName := r.attrs.Metadata("instance/attributes/cluster-name")
- if clusterName == "" {
- return false
- }
- return true
- }
- func detectKubernetesResource() *mrpb.MonitoredResource {
- projectID := detectedResource.metadataProjectID()
- if projectID == "" {
- return nil
- }
- zone := detectedResource.metadataZone()
- clusterName := detectedResource.attrs.Metadata("instance/attributes/cluster-name")
- namespaceName := detectedResource.attrs.ReadAll("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
- if namespaceName == "" {
- // if automountServiceAccountToken is disabled allow to customize
- // the namespace via environment
- namespaceName = detectedResource.attrs.EnvVar("NAMESPACE_NAME")
- }
- // note: if deployment customizes hostname, HOSTNAME envvar will have invalid content
- podName := detectedResource.attrs.EnvVar("HOSTNAME")
- // there is no way to derive container name from within container; use custom envvar if available
- containerName := detectedResource.attrs.EnvVar("CONTAINER_NAME")
- return &mrpb.MonitoredResource{
- Type: "k8s_container",
- Labels: map[string]string{
- "cluster_name": clusterName,
- "location": zone,
- "project_id": projectID,
- "pod_name": podName,
- "namespace_name": namespaceName,
- "container_name": containerName,
- },
- }
- }
- func (r *resource) isComputeEngine() bool {
- preempted := r.attrs.Metadata("instance/preempted")
- platform := r.attrs.Metadata("instance/cpu-platform")
- appBucket := r.attrs.Metadata("instance/attributes/gae_app_bucket")
- return preempted != "" && platform != "" && appBucket == ""
- }
- func detectComputeEngineResource() *mrpb.MonitoredResource {
- projectID := detectedResource.metadataProjectID()
- if projectID == "" {
- return nil
- }
- id := detectedResource.attrs.Metadata("instance/id")
- zone := detectedResource.metadataZone()
- return &mrpb.MonitoredResource{
- Type: "gce_instance",
- Labels: map[string]string{
- "project_id": projectID,
- "instance_id": id,
- "zone": zone,
- },
- }
- }
- func detectResource() *mrpb.MonitoredResource {
- detectedResource.once.Do(func() {
- if detectedResource.isMetadataActive() {
- name := systemProductName()
- switch {
- case name == "Google App Engine", detectedResource.isAppEngine():
- detectedResource.pb = detectAppEngineResource()
- case name == "Google Cloud Functions", detectedResource.isCloudFunction():
- detectedResource.pb = detectCloudFunction()
- case name == "Google Cloud Run", detectedResource.isCloudRun():
- detectedResource.pb = detectCloudRunResource()
- // cannot use name validation for GKE and GCE because
- // both of them set product name to "Google Compute Engine"
- case detectedResource.isKubernetesEngine():
- detectedResource.pb = detectKubernetesResource()
- case detectedResource.isComputeEngine():
- detectedResource.pb = detectComputeEngineResource()
- }
- }
- })
- return detectedResource.pb
- }
- // systemProductName reads resource type on the Linux-based environments such as
- // Cloud Functions, Cloud Run, GKE, GCE, GAE, etc.
- func systemProductName() string {
- if runtime.GOOS != "linux" {
- // We don't have any non-Linux clues available, at least yet.
- return ""
- }
- slurp := detectedResource.attrs.ReadAll("/sys/class/dmi/id/product_name")
- return strings.TrimSpace(slurp)
- }
- var resourceInfo = map[string]struct{ rtype, label string }{
- "organizations": {"organization", "organization_id"},
- "folders": {"folder", "folder_id"},
- "projects": {"project", "project_id"},
- "billingAccounts": {"billing_account", "account_id"},
- }
- func monitoredResource(parent string) *mrpb.MonitoredResource {
- parts := strings.SplitN(parent, "/", 2)
- if len(parts) != 2 {
- return globalResource(parent)
- }
- info, ok := resourceInfo[parts[0]]
- if !ok {
- return globalResource(parts[1])
- }
- return &mrpb.MonitoredResource{
- Type: info.rtype,
- Labels: map[string]string{info.label: parts[1]},
- }
- }
- func globalResource(projectID string) *mrpb.MonitoredResource {
- return &mrpb.MonitoredResource{
- Type: "global",
- Labels: map[string]string{
- "project_id": projectID,
- },
- }
- }
|