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