1// Copyright 2016 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// API/gRPC features intentionally missing from this client:
16// - You cannot have the server pick the time of the entry. This client
17//   always sends a time.
18// - There is no way to provide a protocol buffer payload.
19// - No support for the "partial success" feature when writing log entries.
20
21// TODO(jba): test whether forward-slash characters in the log ID must be URL-encoded.
22// These features are missing now, but will likely be added:
23// - There is no way to specify CallOptions.
24
25package logging
26
27import (
28	"bytes"
29	"context"
30	"encoding/json"
31	"errors"
32	"fmt"
33	"log"
34	"net/http"
35	"strconv"
36	"strings"
37	"sync"
38	"time"
39	"unicode/utf8"
40
41	"cloud.google.com/go/compute/metadata"
42	"cloud.google.com/go/internal/version"
43	vkit "cloud.google.com/go/logging/apiv2"
44	"cloud.google.com/go/logging/internal"
45	"github.com/golang/protobuf/proto"
46	"github.com/golang/protobuf/ptypes"
47	structpb "github.com/golang/protobuf/ptypes/struct"
48	tspb "github.com/golang/protobuf/ptypes/timestamp"
49	"google.golang.org/api/option"
50	"google.golang.org/api/support/bundler"
51	mrpb "google.golang.org/genproto/googleapis/api/monitoredres"
52	logtypepb "google.golang.org/genproto/googleapis/logging/type"
53	logpb "google.golang.org/genproto/googleapis/logging/v2"
54)
55
56const (
57	// ReadScope is the scope for reading from the logging service.
58	ReadScope = "https://www.googleapis.com/auth/logging.read"
59
60	// WriteScope is the scope for writing to the logging service.
61	WriteScope = "https://www.googleapis.com/auth/logging.write"
62
63	// AdminScope is the scope for administrative actions on the logging service.
64	AdminScope = "https://www.googleapis.com/auth/logging.admin"
65)
66
67const (
68	// defaultErrorCapacity is the capacity of the channel used to deliver
69	// errors to the OnError function.
70	defaultErrorCapacity = 10
71
72	// DefaultDelayThreshold is the default value for the DelayThreshold LoggerOption.
73	DefaultDelayThreshold = time.Second
74
75	// DefaultEntryCountThreshold is the default value for the EntryCountThreshold LoggerOption.
76	DefaultEntryCountThreshold = 1000
77
78	// DefaultEntryByteThreshold is the default value for the EntryByteThreshold LoggerOption.
79	DefaultEntryByteThreshold = 1 << 20 // 1MiB
80
81	// DefaultBufferedByteLimit is the default value for the BufferedByteLimit LoggerOption.
82	DefaultBufferedByteLimit = 1 << 30 // 1GiB
83
84	// defaultWriteTimeout is the timeout for the underlying write API calls. As
85	// write API calls are not idempotent, they are not retried on timeout. This
86	// timeout is to allow clients to degrade gracefully if underlying logging
87	// service is temporarily impaired for some reason.
88	defaultWriteTimeout = 10 * time.Minute
89)
90
91// For testing:
92var now = time.Now
93
94// ErrOverflow signals that the number of buffered entries for a Logger
95// exceeds its BufferLimit.
96var ErrOverflow = bundler.ErrOverflow
97
98// ErrOversizedEntry signals that an entry's size exceeds the maximum number of
99// bytes that will be sent in a single call to the logging service.
100var ErrOversizedEntry = bundler.ErrOversizedItem
101
102// Client is a Logging client. A Client is associated with a single Cloud project.
103type Client struct {
104	client  *vkit.Client   // client for the logging service
105	parent  string         // e.g. "projects/proj-id"
106	errc    chan error     // should be buffered to minimize dropped errors
107	donec   chan struct{}  // closed on Client.Close to close Logger bundlers
108	loggers sync.WaitGroup // so we can wait for loggers to close
109	closed  bool
110
111	mu      sync.Mutex
112	nErrs   int   // number of errors we saw
113	lastErr error // last error we saw
114
115	// OnError is called when an error occurs in a call to Log or Flush. The
116	// error may be due to an invalid Entry, an overflow because BufferLimit
117	// was reached (in which case the error will be ErrOverflow) or an error
118	// communicating with the logging service. OnError is called with errors
119	// from all Loggers. It is never called concurrently. OnError is expected
120	// to return quickly; if errors occur while OnError is running, some may
121	// not be reported. The default behavior is to call log.Printf.
122	//
123	// This field should be set only once, before any method of Client is called.
124	OnError func(err error)
125}
126
127// NewClient returns a new logging client associated with the provided parent.
128// A parent can take any of the following forms:
129//    projects/PROJECT_ID
130//    folders/FOLDER_ID
131//    billingAccounts/ACCOUNT_ID
132//    organizations/ORG_ID
133// for backwards compatibility, a string with no '/' is also allowed and is interpreted
134// as a project ID.
135//
136// By default NewClient uses WriteScope. To use a different scope, call
137// NewClient using a WithScopes option (see https://godoc.org/google.golang.org/api/option#WithScopes).
138func NewClient(ctx context.Context, parent string, opts ...option.ClientOption) (*Client, error) {
139	if !strings.ContainsRune(parent, '/') {
140		parent = "projects/" + parent
141	}
142	opts = append([]option.ClientOption{
143		option.WithEndpoint(internal.ProdAddr),
144		option.WithScopes(WriteScope),
145	}, opts...)
146	c, err := vkit.NewClient(ctx, opts...)
147	if err != nil {
148		return nil, err
149	}
150	c.SetGoogleClientInfo("gccl", version.Repo)
151	client := &Client{
152		client:  c,
153		parent:  parent,
154		errc:    make(chan error, defaultErrorCapacity), // create a small buffer for errors
155		donec:   make(chan struct{}),
156		OnError: func(e error) { log.Printf("logging client: %v", e) },
157	}
158	// Call the user's function synchronously, to make life easier for them.
159	go func() {
160		for err := range client.errc {
161			// This reference to OnError is memory-safe if the user sets OnError before
162			// calling any client methods. The reference happens before the first read from
163			// client.errc, which happens before the first write to client.errc, which
164			// happens before any call, which happens before the user sets OnError.
165			if fn := client.OnError; fn != nil {
166				fn(err)
167			} else {
168				log.Printf("logging (parent %q): %v", parent, err)
169			}
170		}
171	}()
172	return client, nil
173}
174
175var unixZeroTimestamp *tspb.Timestamp
176
177func init() {
178	var err error
179	unixZeroTimestamp, err = ptypes.TimestampProto(time.Unix(0, 0))
180	if err != nil {
181		panic(err)
182	}
183}
184
185// Ping reports whether the client's connection to the logging service and the
186// authentication configuration are valid. To accomplish this, Ping writes a
187// log entry "ping" to a log named "ping".
188func (c *Client) Ping(ctx context.Context) error {
189	ent := &logpb.LogEntry{
190		Payload:   &logpb.LogEntry_TextPayload{TextPayload: "ping"},
191		Timestamp: unixZeroTimestamp, // Identical timestamps and insert IDs are both
192		InsertId:  "ping",            // necessary for the service to dedup these entries.
193	}
194	_, err := c.client.WriteLogEntries(ctx, &logpb.WriteLogEntriesRequest{
195		LogName:  internal.LogPath(c.parent, "ping"),
196		Resource: monitoredResource(c.parent),
197		Entries:  []*logpb.LogEntry{ent},
198	})
199	return err
200}
201
202// error puts the error on the client's error channel
203// without blocking, and records summary error info.
204func (c *Client) error(err error) {
205	select {
206	case c.errc <- err:
207	default:
208	}
209	c.mu.Lock()
210	c.lastErr = err
211	c.nErrs++
212	c.mu.Unlock()
213}
214
215func (c *Client) extractErrorInfo() error {
216	var err error
217	c.mu.Lock()
218	if c.lastErr != nil {
219		err = fmt.Errorf("saw %d errors; last: %v", c.nErrs, c.lastErr)
220		c.nErrs = 0
221		c.lastErr = nil
222	}
223	c.mu.Unlock()
224	return err
225}
226
227// A Logger is used to write log messages to a single log. It can be configured
228// with a log ID, common monitored resource, and a set of common labels.
229type Logger struct {
230	client     *Client
231	logName    string // "projects/{projectID}/logs/{logID}"
232	stdLoggers map[Severity]*log.Logger
233	bundler    *bundler.Bundler
234
235	// Options
236	commonResource *mrpb.MonitoredResource
237	commonLabels   map[string]string
238	ctxFunc        func() (context.Context, func())
239}
240
241// A LoggerOption is a configuration option for a Logger.
242type LoggerOption interface {
243	set(*Logger)
244}
245
246// CommonResource sets the monitored resource associated with all log entries
247// written from a Logger. If not provided, the resource is automatically
248// detected based on the running environment.  This value can be overridden
249// per-entry by setting an Entry's Resource field.
250func CommonResource(r *mrpb.MonitoredResource) LoggerOption { return commonResource{r} }
251
252type commonResource struct{ *mrpb.MonitoredResource }
253
254func (r commonResource) set(l *Logger) { l.commonResource = r.MonitoredResource }
255
256var detectedResource struct {
257	pb   *mrpb.MonitoredResource
258	once sync.Once
259}
260
261func detectResource() *mrpb.MonitoredResource {
262	detectedResource.once.Do(func() {
263		if !metadata.OnGCE() {
264			return
265		}
266		projectID, err := metadata.ProjectID()
267		if err != nil {
268			return
269		}
270		id, err := metadata.InstanceID()
271		if err != nil {
272			return
273		}
274		zone, err := metadata.Zone()
275		if err != nil {
276			return
277		}
278		name, err := metadata.InstanceName()
279		if err != nil {
280			return
281		}
282		detectedResource.pb = &mrpb.MonitoredResource{
283			Type: "gce_instance",
284			Labels: map[string]string{
285				"project_id":    projectID,
286				"instance_id":   id,
287				"instance_name": name,
288				"zone":          zone,
289			},
290		}
291	})
292	return detectedResource.pb
293}
294
295var resourceInfo = map[string]struct{ rtype, label string }{
296	"organizations":   {"organization", "organization_id"},
297	"folders":         {"folder", "folder_id"},
298	"projects":        {"project", "project_id"},
299	"billingAccounts": {"billing_account", "account_id"},
300}
301
302func monitoredResource(parent string) *mrpb.MonitoredResource {
303	parts := strings.SplitN(parent, "/", 2)
304	if len(parts) != 2 {
305		return globalResource(parent)
306	}
307	info, ok := resourceInfo[parts[0]]
308	if !ok {
309		return globalResource(parts[1])
310	}
311	return &mrpb.MonitoredResource{
312		Type:   info.rtype,
313		Labels: map[string]string{info.label: parts[1]},
314	}
315}
316
317func globalResource(projectID string) *mrpb.MonitoredResource {
318	return &mrpb.MonitoredResource{
319		Type: "global",
320		Labels: map[string]string{
321			"project_id": projectID,
322		},
323	}
324}
325
326// CommonLabels are labels that apply to all log entries written from a Logger,
327// so that you don't have to repeat them in each log entry's Labels field. If
328// any of the log entries contains a (key, value) with the same key that is in
329// CommonLabels, then the entry's (key, value) overrides the one in
330// CommonLabels.
331func CommonLabels(m map[string]string) LoggerOption { return commonLabels(m) }
332
333type commonLabels map[string]string
334
335func (c commonLabels) set(l *Logger) { l.commonLabels = c }
336
337// ConcurrentWriteLimit determines how many goroutines will send log entries to the
338// underlying service. The default is 1. Set ConcurrentWriteLimit to a higher value to
339// increase throughput.
340func ConcurrentWriteLimit(n int) LoggerOption { return concurrentWriteLimit(n) }
341
342type concurrentWriteLimit int
343
344func (c concurrentWriteLimit) set(l *Logger) { l.bundler.HandlerLimit = int(c) }
345
346// DelayThreshold is the maximum amount of time that an entry should remain
347// buffered in memory before a call to the logging service is triggered. Larger
348// values of DelayThreshold will generally result in fewer calls to the logging
349// service, while increasing the risk that log entries will be lost if the
350// process crashes.
351// The default is DefaultDelayThreshold.
352func DelayThreshold(d time.Duration) LoggerOption { return delayThreshold(d) }
353
354type delayThreshold time.Duration
355
356func (d delayThreshold) set(l *Logger) { l.bundler.DelayThreshold = time.Duration(d) }
357
358// EntryCountThreshold is the maximum number of entries that will be buffered
359// in memory before a call to the logging service is triggered. Larger values
360// will generally result in fewer calls to the logging service, while
361// increasing both memory consumption and the risk that log entries will be
362// lost if the process crashes.
363// The default is DefaultEntryCountThreshold.
364func EntryCountThreshold(n int) LoggerOption { return entryCountThreshold(n) }
365
366type entryCountThreshold int
367
368func (e entryCountThreshold) set(l *Logger) { l.bundler.BundleCountThreshold = int(e) }
369
370// EntryByteThreshold is the maximum number of bytes of entries that will be
371// buffered in memory before a call to the logging service is triggered. See
372// EntryCountThreshold for a discussion of the tradeoffs involved in setting
373// this option.
374// The default is DefaultEntryByteThreshold.
375func EntryByteThreshold(n int) LoggerOption { return entryByteThreshold(n) }
376
377type entryByteThreshold int
378
379func (e entryByteThreshold) set(l *Logger) { l.bundler.BundleByteThreshold = int(e) }
380
381// EntryByteLimit is the maximum number of bytes of entries that will be sent
382// in a single call to the logging service. ErrOversizedEntry is returned if an
383// entry exceeds EntryByteLimit. This option limits the size of a single RPC
384// payload, to account for network or service issues with large RPCs. If
385// EntryByteLimit is smaller than EntryByteThreshold, the latter has no effect.
386// The default is zero, meaning there is no limit.
387func EntryByteLimit(n int) LoggerOption { return entryByteLimit(n) }
388
389type entryByteLimit int
390
391func (e entryByteLimit) set(l *Logger) { l.bundler.BundleByteLimit = int(e) }
392
393// BufferedByteLimit is the maximum number of bytes that the Logger will keep
394// in memory before returning ErrOverflow. This option limits the total memory
395// consumption of the Logger (but note that each Logger has its own, separate
396// limit). It is possible to reach BufferedByteLimit even if it is larger than
397// EntryByteThreshold or EntryByteLimit, because calls triggered by the latter
398// two options may be enqueued (and hence occupying memory) while new log
399// entries are being added.
400// The default is DefaultBufferedByteLimit.
401func BufferedByteLimit(n int) LoggerOption { return bufferedByteLimit(n) }
402
403type bufferedByteLimit int
404
405func (b bufferedByteLimit) set(l *Logger) { l.bundler.BufferedByteLimit = int(b) }
406
407// ContextFunc is a function that will be called to obtain a context.Context for the
408// WriteLogEntries RPC executed in the background for calls to Logger.Log. The
409// default is a function that always returns context.Background. The second return
410// value of the function is a function to call after the RPC completes.
411//
412// The function is not used for calls to Logger.LogSync, since the caller can pass
413// in the context directly.
414//
415// This option is EXPERIMENTAL. It may be changed or removed.
416func ContextFunc(f func() (ctx context.Context, afterCall func())) LoggerOption {
417	return contextFunc(f)
418}
419
420type contextFunc func() (ctx context.Context, afterCall func())
421
422func (c contextFunc) set(l *Logger) { l.ctxFunc = c }
423
424// Logger returns a Logger that will write entries with the given log ID, such as
425// "syslog". A log ID must be less than 512 characters long and can only
426// include the following characters: upper and lower case alphanumeric
427// characters: [A-Za-z0-9]; and punctuation characters: forward-slash,
428// underscore, hyphen, and period.
429func (c *Client) Logger(logID string, opts ...LoggerOption) *Logger {
430	r := detectResource()
431	if r == nil {
432		r = monitoredResource(c.parent)
433	}
434	l := &Logger{
435		client:         c,
436		logName:        internal.LogPath(c.parent, logID),
437		commonResource: r,
438		ctxFunc:        func() (context.Context, func()) { return context.Background(), nil },
439	}
440	l.bundler = bundler.NewBundler(&logpb.LogEntry{}, func(entries interface{}) {
441		l.writeLogEntries(entries.([]*logpb.LogEntry))
442	})
443	l.bundler.DelayThreshold = DefaultDelayThreshold
444	l.bundler.BundleCountThreshold = DefaultEntryCountThreshold
445	l.bundler.BundleByteThreshold = DefaultEntryByteThreshold
446	l.bundler.BufferedByteLimit = DefaultBufferedByteLimit
447	for _, opt := range opts {
448		opt.set(l)
449	}
450	l.stdLoggers = map[Severity]*log.Logger{}
451	for s := range severityName {
452		l.stdLoggers[s] = log.New(severityWriter{l, s}, "", 0)
453	}
454
455	c.loggers.Add(1)
456	// Start a goroutine that cleans up the bundler, its channel
457	// and the writer goroutines when the client is closed.
458	go func() {
459		defer c.loggers.Done()
460		<-c.donec
461		l.bundler.Flush()
462	}()
463	return l
464}
465
466type severityWriter struct {
467	l *Logger
468	s Severity
469}
470
471func (w severityWriter) Write(p []byte) (n int, err error) {
472	w.l.Log(Entry{
473		Severity: w.s,
474		Payload:  string(p),
475	})
476	return len(p), nil
477}
478
479// Close waits for all opened loggers to be flushed and closes the client.
480func (c *Client) Close() error {
481	if c.closed {
482		return nil
483	}
484	close(c.donec)   // close Logger bundlers
485	c.loggers.Wait() // wait for all bundlers to flush and close
486	// Now there can be no more errors.
487	close(c.errc) // terminate error goroutine
488	// Prefer errors arising from logging to the error returned from Close.
489	err := c.extractErrorInfo()
490	err2 := c.client.Close()
491	if err == nil {
492		err = err2
493	}
494	c.closed = true
495	return err
496}
497
498// Severity is the severity of the event described in a log entry. These
499// guideline severity levels are ordered, with numerically smaller levels
500// treated as less severe than numerically larger levels.
501type Severity int
502
503const (
504	// Default means the log entry has no assigned severity level.
505	Default = Severity(logtypepb.LogSeverity_DEFAULT)
506	// Debug means debug or trace information.
507	Debug = Severity(logtypepb.LogSeverity_DEBUG)
508	// Info means routine information, such as ongoing status or performance.
509	Info = Severity(logtypepb.LogSeverity_INFO)
510	// Notice means normal but significant events, such as start up, shut down, or configuration.
511	Notice = Severity(logtypepb.LogSeverity_NOTICE)
512	// Warning means events that might cause problems.
513	Warning = Severity(logtypepb.LogSeverity_WARNING)
514	// Error means events that are likely to cause problems.
515	Error = Severity(logtypepb.LogSeverity_ERROR)
516	// Critical means events that cause more severe problems or brief outages.
517	Critical = Severity(logtypepb.LogSeverity_CRITICAL)
518	// Alert means a person must take an action immediately.
519	Alert = Severity(logtypepb.LogSeverity_ALERT)
520	// Emergency means one or more systems are unusable.
521	Emergency = Severity(logtypepb.LogSeverity_EMERGENCY)
522)
523
524var severityName = map[Severity]string{
525	Default:   "Default",
526	Debug:     "Debug",
527	Info:      "Info",
528	Notice:    "Notice",
529	Warning:   "Warning",
530	Error:     "Error",
531	Critical:  "Critical",
532	Alert:     "Alert",
533	Emergency: "Emergency",
534}
535
536// String converts a severity level to a string.
537func (v Severity) String() string {
538	// same as proto.EnumName
539	s, ok := severityName[v]
540	if ok {
541		return s
542	}
543	return strconv.Itoa(int(v))
544}
545
546// ParseSeverity returns the Severity whose name equals s, ignoring case. It
547// returns Default if no Severity matches.
548func ParseSeverity(s string) Severity {
549	sl := strings.ToLower(s)
550	for sev, name := range severityName {
551		if strings.ToLower(name) == sl {
552			return sev
553		}
554	}
555	return Default
556}
557
558// Entry is a log entry.
559// See https://cloud.google.com/logging/docs/view/logs_index for more about entries.
560type Entry struct {
561	// Timestamp is the time of the entry. If zero, the current time is used.
562	Timestamp time.Time
563
564	// Severity is the entry's severity level.
565	// The zero value is Default.
566	Severity Severity
567
568	// Payload must be either a string, or something that marshals via the
569	// encoding/json package to a JSON object (and not any other type of JSON value).
570	Payload interface{}
571
572	// Labels optionally specifies key/value labels for the log entry.
573	// The Logger.Log method takes ownership of this map. See Logger.CommonLabels
574	// for more about labels.
575	Labels map[string]string
576
577	// InsertID is a unique ID for the log entry. If you provide this field,
578	// the logging service considers other log entries in the same log with the
579	// same ID as duplicates which can be removed. If omitted, the logging
580	// service will generate a unique ID for this log entry. Note that because
581	// this client retries RPCs automatically, it is possible (though unlikely)
582	// that an Entry without an InsertID will be written more than once.
583	InsertID string
584
585	// HTTPRequest optionally specifies metadata about the HTTP request
586	// associated with this log entry, if applicable. It is optional.
587	HTTPRequest *HTTPRequest
588
589	// Operation optionally provides information about an operation associated
590	// with the log entry, if applicable.
591	Operation *logpb.LogEntryOperation
592
593	// LogName is the full log name, in the form
594	// "projects/{ProjectID}/logs/{LogID}". It is set by the client when
595	// reading entries. It is an error to set it when writing entries.
596	LogName string
597
598	// Resource is the monitored resource associated with the entry.
599	Resource *mrpb.MonitoredResource
600
601	// Trace is the resource name of the trace associated with the log entry,
602	// if any. If it contains a relative resource name, the name is assumed to
603	// be relative to //tracing.googleapis.com.
604	Trace string
605
606	// ID of the span within the trace associated with the log entry.
607	// The ID is a 16-character hexadecimal encoding of an 8-byte array.
608	SpanID string
609
610	// Optional. Source code location information associated with the log entry,
611	// if any.
612	SourceLocation *logpb.LogEntrySourceLocation
613}
614
615// HTTPRequest contains an http.Request as well as additional
616// information about the request and its response.
617type HTTPRequest struct {
618	// Request is the http.Request passed to the handler.
619	Request *http.Request
620
621	// RequestSize is the size of the HTTP request message in bytes, including
622	// the request headers and the request body.
623	RequestSize int64
624
625	// Status is the response code indicating the status of the response.
626	// Examples: 200, 404.
627	Status int
628
629	// ResponseSize is the size of the HTTP response message sent back to the client, in bytes,
630	// including the response headers and the response body.
631	ResponseSize int64
632
633	// Latency is the request processing latency on the server, from the time the request was
634	// received until the response was sent.
635	Latency time.Duration
636
637	// LocalIP is the IP address (IPv4 or IPv6) of the origin server that the request
638	// was sent to.
639	LocalIP string
640
641	// RemoteIP is the IP address (IPv4 or IPv6) of the client that issued the
642	// HTTP request. Examples: "192.168.1.1", "FE80::0202:B3FF:FE1E:8329".
643	RemoteIP string
644
645	// CacheHit reports whether an entity was served from cache (with or without
646	// validation).
647	CacheHit bool
648
649	// CacheValidatedWithOriginServer reports whether the response was
650	// validated with the origin server before being served from cache. This
651	// field is only meaningful if CacheHit is true.
652	CacheValidatedWithOriginServer bool
653}
654
655func fromHTTPRequest(r *HTTPRequest) *logtypepb.HttpRequest {
656	if r == nil {
657		return nil
658	}
659	if r.Request == nil {
660		panic("HTTPRequest must have a non-nil Request")
661	}
662	u := *r.Request.URL
663	u.Fragment = ""
664	pb := &logtypepb.HttpRequest{
665		RequestMethod:                  r.Request.Method,
666		RequestUrl:                     fixUTF8(u.String()),
667		RequestSize:                    r.RequestSize,
668		Status:                         int32(r.Status),
669		ResponseSize:                   r.ResponseSize,
670		UserAgent:                      r.Request.UserAgent(),
671		ServerIp:                       r.LocalIP,
672		RemoteIp:                       r.RemoteIP, // TODO(jba): attempt to parse http.Request.RemoteAddr?
673		Referer:                        r.Request.Referer(),
674		CacheHit:                       r.CacheHit,
675		CacheValidatedWithOriginServer: r.CacheValidatedWithOriginServer,
676	}
677	if r.Latency != 0 {
678		pb.Latency = ptypes.DurationProto(r.Latency)
679	}
680	return pb
681}
682
683// fixUTF8 is a helper that fixes an invalid UTF-8 string by replacing
684// invalid UTF-8 runes with the Unicode replacement character (U+FFFD).
685// See Issue https://github.com/googleapis/google-cloud-go/issues/1383.
686func fixUTF8(s string) string {
687	if utf8.ValidString(s) {
688		return s
689	}
690
691	// Otherwise time to build the sequence.
692	buf := new(bytes.Buffer)
693	buf.Grow(len(s))
694	for _, r := range s {
695		if utf8.ValidRune(r) {
696			buf.WriteRune(r)
697		} else {
698			buf.WriteRune('\uFFFD')
699		}
700	}
701	return buf.String()
702}
703
704// toProtoStruct converts v, which must marshal into a JSON object,
705// into a Google Struct proto.
706func toProtoStruct(v interface{}) (*structpb.Struct, error) {
707	// Fast path: if v is already a *structpb.Struct, nothing to do.
708	if s, ok := v.(*structpb.Struct); ok {
709		return s, nil
710	}
711	// v is a Go value that supports JSON marshalling. We want a Struct
712	// protobuf. Some day we may have a more direct way to get there, but right
713	// now the only way is to marshal the Go value to JSON, unmarshal into a
714	// map, and then build the Struct proto from the map.
715	var jb []byte
716	var err error
717	if raw, ok := v.(json.RawMessage); ok { // needed for Go 1.7 and below
718		jb = []byte(raw)
719	} else {
720		jb, err = json.Marshal(v)
721		if err != nil {
722			return nil, fmt.Errorf("logging: json.Marshal: %v", err)
723		}
724	}
725	var m map[string]interface{}
726	err = json.Unmarshal(jb, &m)
727	if err != nil {
728		return nil, fmt.Errorf("logging: json.Unmarshal: %v", err)
729	}
730	return jsonMapToProtoStruct(m), nil
731}
732
733func jsonMapToProtoStruct(m map[string]interface{}) *structpb.Struct {
734	fields := map[string]*structpb.Value{}
735	for k, v := range m {
736		fields[k] = jsonValueToStructValue(v)
737	}
738	return &structpb.Struct{Fields: fields}
739}
740
741func jsonValueToStructValue(v interface{}) *structpb.Value {
742	switch x := v.(type) {
743	case bool:
744		return &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: x}}
745	case float64:
746		return &structpb.Value{Kind: &structpb.Value_NumberValue{NumberValue: x}}
747	case string:
748		return &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: x}}
749	case nil:
750		return &structpb.Value{Kind: &structpb.Value_NullValue{}}
751	case map[string]interface{}:
752		return &structpb.Value{Kind: &structpb.Value_StructValue{StructValue: jsonMapToProtoStruct(x)}}
753	case []interface{}:
754		var vals []*structpb.Value
755		for _, e := range x {
756			vals = append(vals, jsonValueToStructValue(e))
757		}
758		return &structpb.Value{Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{Values: vals}}}
759	default:
760		panic(fmt.Sprintf("bad type %T for JSON value", v))
761	}
762}
763
764// LogSync logs the Entry synchronously without any buffering. Because LogSync is slow
765// and will block, it is intended primarily for debugging or critical errors.
766// Prefer Log for most uses.
767// TODO(jba): come up with a better name (LogNow?) or eliminate.
768func (l *Logger) LogSync(ctx context.Context, e Entry) error {
769	ent, err := l.toLogEntry(e)
770	if err != nil {
771		return err
772	}
773	_, err = l.client.client.WriteLogEntries(ctx, &logpb.WriteLogEntriesRequest{
774		LogName:  l.logName,
775		Resource: l.commonResource,
776		Labels:   l.commonLabels,
777		Entries:  []*logpb.LogEntry{ent},
778	})
779	return err
780}
781
782// Log buffers the Entry for output to the logging service. It never blocks.
783func (l *Logger) Log(e Entry) {
784	ent, err := l.toLogEntry(e)
785	if err != nil {
786		l.client.error(err)
787		return
788	}
789	if err := l.bundler.Add(ent, proto.Size(ent)); err != nil {
790		l.client.error(err)
791	}
792}
793
794// Flush blocks until all currently buffered log entries are sent.
795//
796// If any errors occurred since the last call to Flush from any Logger, or the
797// creation of the client if this is the first call, then Flush returns a non-nil
798// error with summary information about the errors. This information is unlikely to
799// be actionable. For more accurate error reporting, set Client.OnError.
800func (l *Logger) Flush() error {
801	l.bundler.Flush()
802	return l.client.extractErrorInfo()
803}
804
805func (l *Logger) writeLogEntries(entries []*logpb.LogEntry) {
806	req := &logpb.WriteLogEntriesRequest{
807		LogName:  l.logName,
808		Resource: l.commonResource,
809		Labels:   l.commonLabels,
810		Entries:  entries,
811	}
812	ctx, afterCall := l.ctxFunc()
813	ctx, cancel := context.WithTimeout(ctx, defaultWriteTimeout)
814	defer cancel()
815	_, err := l.client.client.WriteLogEntries(ctx, req)
816	if err != nil {
817		l.client.error(err)
818	}
819	if afterCall != nil {
820		afterCall()
821	}
822}
823
824// StandardLogger returns a *log.Logger for the provided severity.
825//
826// This method is cheap. A single log.Logger is pre-allocated for each
827// severity level in each Logger. Callers may mutate the returned log.Logger
828// (for example by calling SetFlags or SetPrefix).
829func (l *Logger) StandardLogger(s Severity) *log.Logger { return l.stdLoggers[s] }
830
831func (l *Logger) toLogEntry(e Entry) (*logpb.LogEntry, error) {
832	if e.LogName != "" {
833		return nil, errors.New("logging: Entry.LogName should be not be set when writing")
834	}
835	t := e.Timestamp
836	if t.IsZero() {
837		t = now()
838	}
839	ts, err := ptypes.TimestampProto(t)
840	if err != nil {
841		return nil, err
842	}
843	if e.Trace == "" && e.HTTPRequest != nil && e.HTTPRequest.Request != nil {
844		traceHeader := e.HTTPRequest.Request.Header.Get("X-Cloud-Trace-Context")
845		if traceHeader != "" {
846			// Set to a relative resource name, as described at
847			// https://cloud.google.com/appengine/docs/flexible/go/writing-application-logs.
848			e.Trace = fmt.Sprintf("%s/traces/%s", l.client.parent, traceHeader)
849		}
850	}
851	ent := &logpb.LogEntry{
852		Timestamp:      ts,
853		Severity:       logtypepb.LogSeverity(e.Severity),
854		InsertId:       e.InsertID,
855		HttpRequest:    fromHTTPRequest(e.HTTPRequest),
856		Operation:      e.Operation,
857		Labels:         e.Labels,
858		Trace:          e.Trace,
859		SpanId:         e.SpanID,
860		Resource:       e.Resource,
861		SourceLocation: e.SourceLocation,
862	}
863	switch p := e.Payload.(type) {
864	case string:
865		ent.Payload = &logpb.LogEntry_TextPayload{TextPayload: p}
866	default:
867		s, err := toProtoStruct(p)
868		if err != nil {
869			return nil, err
870		}
871		ent.Payload = &logpb.LogEntry_JsonPayload{JsonPayload: s}
872	}
873	return ent, nil
874}
875