github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/daemon/logger/gcplogs/gcplogging.go (about)

     1  package gcplogs
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"sync/atomic"
     7  	"time"
     8  
     9  	"github.com/docker/docker/daemon/logger"
    10  
    11  	"cloud.google.com/go/compute/metadata"
    12  	"cloud.google.com/go/logging"
    13  	"github.com/sirupsen/logrus"
    14  	"golang.org/x/net/context"
    15  	mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
    16  )
    17  
    18  const (
    19  	name = "gcplogs"
    20  
    21  	projectOptKey  = "gcp-project"
    22  	logLabelsKey   = "labels"
    23  	logEnvKey      = "env"
    24  	logEnvRegexKey = "env-regex"
    25  	logCmdKey      = "gcp-log-cmd"
    26  	logZoneKey     = "gcp-meta-zone"
    27  	logNameKey     = "gcp-meta-name"
    28  	logIDKey       = "gcp-meta-id"
    29  )
    30  
    31  var (
    32  	// The number of logs the gcplogs driver has dropped.
    33  	droppedLogs uint64
    34  
    35  	onGCE bool
    36  
    37  	// instance metadata populated from the metadata server if available
    38  	projectID    string
    39  	zone         string
    40  	instanceName string
    41  	instanceID   string
    42  )
    43  
    44  func init() {
    45  
    46  	if err := logger.RegisterLogDriver(name, New); err != nil {
    47  		logrus.Fatal(err)
    48  	}
    49  
    50  	if err := logger.RegisterLogOptValidator(name, ValidateLogOpts); err != nil {
    51  		logrus.Fatal(err)
    52  	}
    53  }
    54  
    55  type gcplogs struct {
    56  	logger    *logging.Logger
    57  	instance  *instanceInfo
    58  	container *containerInfo
    59  }
    60  
    61  type dockerLogEntry struct {
    62  	Instance  *instanceInfo  `json:"instance,omitempty"`
    63  	Container *containerInfo `json:"container,omitempty"`
    64  	Message   string         `json:"message,omitempty"`
    65  }
    66  
    67  type instanceInfo struct {
    68  	Zone string `json:"zone,omitempty"`
    69  	Name string `json:"name,omitempty"`
    70  	ID   string `json:"id,omitempty"`
    71  }
    72  
    73  type containerInfo struct {
    74  	Name      string            `json:"name,omitempty"`
    75  	ID        string            `json:"id,omitempty"`
    76  	ImageName string            `json:"imageName,omitempty"`
    77  	ImageID   string            `json:"imageId,omitempty"`
    78  	Created   time.Time         `json:"created,omitempty"`
    79  	Command   string            `json:"command,omitempty"`
    80  	Metadata  map[string]string `json:"metadata,omitempty"`
    81  }
    82  
    83  var initGCPOnce sync.Once
    84  
    85  func initGCP() {
    86  	initGCPOnce.Do(func() {
    87  		onGCE = metadata.OnGCE()
    88  		if onGCE {
    89  			// These will fail on instances if the metadata service is
    90  			// down or the client is compiled with an API version that
    91  			// has been removed. Since these are not vital, let's ignore
    92  			// them and make their fields in the dockerLogEntry ,omitempty
    93  			projectID, _ = metadata.ProjectID()
    94  			zone, _ = metadata.Zone()
    95  			instanceName, _ = metadata.InstanceName()
    96  			instanceID, _ = metadata.InstanceID()
    97  		}
    98  	})
    99  }
   100  
   101  // New creates a new logger that logs to Google Cloud Logging using the application
   102  // default credentials.
   103  //
   104  // See https://developers.google.com/identity/protocols/application-default-credentials
   105  func New(info logger.Info) (logger.Logger, error) {
   106  	initGCP()
   107  
   108  	var project string
   109  	if projectID != "" {
   110  		project = projectID
   111  	}
   112  	if projectID, found := info.Config[projectOptKey]; found {
   113  		project = projectID
   114  	}
   115  	if project == "" {
   116  		return nil, fmt.Errorf("No project was specified and couldn't read project from the metadata server. Please specify a project")
   117  	}
   118  
   119  	// Issue #29344: gcplogs segfaults (static binary)
   120  	// If HOME is not set, logging.NewClient() will call os/user.Current() via oauth2/google.
   121  	// However, in static binary, os/user.Current() leads to segfault due to a glibc issue that won't be fixed
   122  	// in a short term. (golang/go#13470, https://sourceware.org/bugzilla/show_bug.cgi?id=19341)
   123  	// So we forcibly set HOME so as to avoid call to os/user/Current()
   124  	if err := ensureHomeIfIAmStatic(); err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	c, err := logging.NewClient(context.Background(), project)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	var instanceResource *instanceInfo
   133  	if onGCE {
   134  		instanceResource = &instanceInfo{
   135  			Zone: zone,
   136  			Name: instanceName,
   137  			ID:   instanceID,
   138  		}
   139  	} else if info.Config[logZoneKey] != "" || info.Config[logNameKey] != "" || info.Config[logIDKey] != "" {
   140  		instanceResource = &instanceInfo{
   141  			Zone: info.Config[logZoneKey],
   142  			Name: info.Config[logNameKey],
   143  			ID:   info.Config[logIDKey],
   144  		}
   145  	}
   146  
   147  	options := []logging.LoggerOption{}
   148  	if instanceResource != nil {
   149  		vmMrpb := logging.CommonResource(
   150  			&mrpb.MonitoredResource{
   151  				Type: "gce_instance",
   152  				Labels: map[string]string{
   153  					"instance_id": instanceResource.ID,
   154  					"zone":        instanceResource.Zone,
   155  				},
   156  			},
   157  		)
   158  		options = []logging.LoggerOption{vmMrpb}
   159  	}
   160  	lg := c.Logger("gcplogs-docker-driver", options...)
   161  
   162  	if err := c.Ping(context.Background()); err != nil {
   163  		return nil, fmt.Errorf("unable to connect or authenticate with Google Cloud Logging: %v", err)
   164  	}
   165  
   166  	extraAttributes, err := info.ExtraAttributes(nil)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	l := &gcplogs{
   172  		logger: lg,
   173  		container: &containerInfo{
   174  			Name:      info.ContainerName,
   175  			ID:        info.ContainerID,
   176  			ImageName: info.ContainerImageName,
   177  			ImageID:   info.ContainerImageID,
   178  			Created:   info.ContainerCreated,
   179  			Metadata:  extraAttributes,
   180  		},
   181  	}
   182  
   183  	if info.Config[logCmdKey] == "true" {
   184  		l.container.Command = info.Command()
   185  	}
   186  
   187  	if instanceResource != nil {
   188  		l.instance = instanceResource
   189  	}
   190  
   191  	// The logger "overflows" at a rate of 10,000 logs per second and this
   192  	// overflow func is called. We want to surface the error to the user
   193  	// without overly spamming /var/log/docker.log so we log the first time
   194  	// we overflow and every 1000th time after.
   195  	c.OnError = func(err error) {
   196  		if err == logging.ErrOverflow {
   197  			if i := atomic.AddUint64(&droppedLogs, 1); i%1000 == 1 {
   198  				logrus.Errorf("gcplogs driver has dropped %v logs", i)
   199  			}
   200  		} else {
   201  			logrus.Error(err)
   202  		}
   203  	}
   204  
   205  	return l, nil
   206  }
   207  
   208  // ValidateLogOpts validates the opts passed to the gcplogs driver. Currently, the gcplogs
   209  // driver doesn't take any arguments.
   210  func ValidateLogOpts(cfg map[string]string) error {
   211  	for k := range cfg {
   212  		switch k {
   213  		case projectOptKey, logLabelsKey, logEnvKey, logEnvRegexKey, logCmdKey, logZoneKey, logNameKey, logIDKey:
   214  		default:
   215  			return fmt.Errorf("%q is not a valid option for the gcplogs driver", k)
   216  		}
   217  	}
   218  	return nil
   219  }
   220  
   221  func (l *gcplogs) Log(m *logger.Message) error {
   222  	message := string(m.Line)
   223  	ts := m.Timestamp
   224  	logger.PutMessage(m)
   225  
   226  	l.logger.Log(logging.Entry{
   227  		Timestamp: ts,
   228  		Payload: &dockerLogEntry{
   229  			Instance:  l.instance,
   230  			Container: l.container,
   231  			Message:   message,
   232  		},
   233  	})
   234  	return nil
   235  }
   236  
   237  func (l *gcplogs) Close() error {
   238  	l.logger.Flush()
   239  	return nil
   240  }
   241  
   242  func (l *gcplogs) Name() string {
   243  	return name
   244  }