Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

815 righe
27 KiB

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