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