tessl install tessl/golang-cloud-google-com--go--logging@1.13.0Cloud Logging client library for Go that enables writing log entries to Google Cloud Logging service with buffered asynchronous and synchronous logging capabilities.
This document describes how to integrate Cloud Logging with Go's standard library log.Logger for compatibility with existing logging code.
func (l *Logger) StandardLogger(s Severity) *log.LoggerReturns a *log.Logger for the provided severity level. This method is cheap because a single log.Logger is pre-allocated for each severity level in each Logger. Callers may mutate the returned log.Logger (for example by calling SetFlags or SetPrefix).
Parameters:
s - The severity level for all log entries written by the returned loggerReturns:
*log.Logger - A standard library logger that writes to Cloud LoggingExample:
import (
"cloud.google.com/go/logging"
"log"
)
logger := client.Logger("my-log")
// Get standard logger for Info severity
stdLogger := logger.StandardLogger(logging.Info)
// Use like any standard logger
stdLogger.Println("This is an info message")
stdLogger.Printf("User %s logged in", userID)
// Get standard loggers for different severities
debugLogger := logger.StandardLogger(logging.Debug)
errorLogger := logger.StandardLogger(logging.Error)
debugLogger.Println("Debug information")
errorLogger.Println("An error occurred")func (l *Logger) StandardLoggerFromTemplate(template *Entry) *log.LoggerReturns a Go Standard Logging API *log.Logger that emits logs using the provided template Entry struct. The returned logger emits logs using logging.(*Logger).Log() with an entry constructed from the provided template Entry struct.
The caller is responsible for ensuring that the template Entry struct does not change during the lifetime of the returned *log.Logger.
Prefer (*Logger).StandardLogger() which is more efficient if the template only sets Severity.
Parameters:
template - An Entry struct to use as a template for all log entriesReturns:
*log.Logger - A standard library logger that writes to Cloud Logging with the templateExample:
import (
"cloud.google.com/go/logging"
"log"
)
logger := client.Logger("my-log")
// Create template with labels and metadata
template := &logging.Entry{
Severity: logging.Info,
Labels: map[string]string{
"service": "api-gateway",
"environment": "production",
},
}
// Create standard logger from template
stdLogger := logger.StandardLoggerFromTemplate(template)
// All logs will include the template's severity and labels
stdLogger.Println("Request processed")
stdLogger.Printf("User %s authenticated", userID)Replace existing standard loggers with Cloud Logging:
package main
import (
"context"
"log"
"cloud.google.com/go/logging"
)
func main() {
ctx := context.Background()
client, err := logging.NewClient(ctx, "my-project")
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer client.Close()
logger := client.Logger("app-log")
// Replace default logger
log.SetOutput(logger.StandardLogger(logging.Info).Writer())
log.SetFlags(0) // Cloud Logging adds its own metadata
// Now all log.Print calls go to Cloud Logging
log.Println("Application started")
log.Printf("Listening on port %d", 8080)
}Use different standard loggers for different severity levels:
var (
debugLog *log.Logger
infoLog *log.Logger
warningLog *log.Logger
errorLog *log.Logger
)
func initLoggers(logger *logging.Logger) {
debugLog = logger.StandardLogger(logging.Debug)
infoLog = logger.StandardLogger(logging.Info)
warningLog = logger.StandardLogger(logging.Warning)
errorLog = logger.StandardLogger(logging.Error)
}
func main() {
ctx := context.Background()
client, _ := logging.NewClient(ctx, "my-project")
defer client.Close()
logger := client.Logger("app-log")
initLoggers(logger)
debugLog.Println("Debug information")
infoLog.Println("Application started")
warningLog.Println("Configuration incomplete, using defaults")
errorLog.Println("Failed to connect to database")
}Customize the standard logger with prefixes and flags:
logger := client.Logger("my-log")
stdLogger := logger.StandardLogger(logging.Info)
// Add prefix
stdLogger.SetPrefix("[APP] ")
// Set flags (note: Cloud Logging adds its own timestamps)
stdLogger.SetFlags(log.Lshortfile) // Include file:line
stdLogger.Println("This message has prefix and file location")
// Output in Cloud Logging: [APP] main.go:123: This message has prefix and file locationMigrate existing code that uses standard library logging:
// Before: Using standard library
import "log"
func processRequest(data string) {
log.Printf("Processing: %s", data)
}
// After: Using Cloud Logging with minimal changes
import (
"log"
"cloud.google.com/go/logging"
)
var stdLogger *log.Logger
func init() {
ctx := context.Background()
client, _ := logging.NewClient(ctx, "my-project")
logger := client.Logger("app-log")
stdLogger = logger.StandardLogger(logging.Info)
}
func processRequest(data string) {
stdLogger.Printf("Processing: %s", data)
}Use templates to include HTTP request metadata:
import (
"net/http"
"cloud.google.com/go/logging"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Create template with HTTP request
template := &logging.Entry{
Severity: logging.Info,
HTTPRequest: &logging.HTTPRequest{
Request: r,
},
Labels: map[string]string{
"handler": "user-api",
},
}
stdLogger := logger.StandardLoggerFromTemplate(template)
// All logs include HTTP request context
stdLogger.Println("Processing user request")
stdLogger.Printf("User ID: %s", getUserID(r))
// These logs will be grouped with the HTTP request in Cloud Logging
}Include operation tracking in standard logger:
import (
"cloud.google.com/go/logging"
logpb "cloud.google.com/go/logging/apiv2/loggingpb"
)
func longRunningOperation(operationID string) {
template := &logging.Entry{
Severity: logging.Info,
Operation: &logpb.LogEntryOperation{
Id: operationID,
Producer: "batch-processor",
},
Labels: map[string]string{
"operation_type": "batch",
},
}
opLogger := logger.StandardLoggerFromTemplate(template)
opLogger.Println("Operation started")
opLogger.Println("Processing batch 1/10")
opLogger.Println("Processing batch 2/10")
// ...
opLogger.Println("Operation completed")
// All logs are associated with the same operation
}Replace the global default logger:
package main
import (
"context"
"log"
"cloud.google.com/go/logging"
)
func main() {
ctx := context.Background()
client, err := logging.NewClient(ctx, "my-project")
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer client.Close()
logger := client.Logger("app-log")
// Replace global logger
stdLogger := logger.StandardLogger(logging.Info)
log.SetOutput(stdLogger.Writer())
log.SetFlags(0)
// Now all existing code using log.Print* works with Cloud Logging
log.Println("Global logger now uses Cloud Logging")
// Even third-party libraries using log.Print* will log to Cloud Logging
runThirdPartyCode()
}Create separate standard loggers for different packages:
package database
import (
"log"
"cloud.google.com/go/logging"
)
var dbLogger *log.Logger
func InitLogger(logger *logging.Logger) {
template := &logging.Entry{
Severity: logging.Debug,
Labels: map[string]string{
"component": "database",
},
}
dbLogger = logger.StandardLoggerFromTemplate(template)
}
func Query(sql string) {
dbLogger.Printf("Executing query: %s", sql)
}package api
import (
"log"
"cloud.google.com/go/logging"
)
var apiLogger *log.Logger
func InitLogger(logger *logging.Logger) {
template := &logging.Entry{
Severity: logging.Info,
Labels: map[string]string{
"component": "api",
},
}
apiLogger = logger.StandardLoggerFromTemplate(template)
}
func HandleRequest() {
apiLogger.Println("Handling API request")
}Standard loggers don't return errors, but errors are reported via Client.OnError:
ctx := context.Background()
client, _ := logging.NewClient(ctx, "my-project")
defer client.Close()
// Set error handler
client.OnError = func(err error) {
// Handle logging errors
fmt.Fprintf(os.Stderr, "Logging error: %v\n", err)
}
logger := client.Logger("my-log")
stdLogger := logger.StandardLogger(logging.Info)
// Errors during logging will be reported to OnError
stdLogger.Println("This is logged asynchronously")package main
import (
"context"
"log"
"net/http"
"os"
"cloud.google.com/go/logging"
)
var (
infoLogger *log.Logger
errorLogger *log.Logger
)
func main() {
ctx := context.Background()
// Create Cloud Logging client
client, err := logging.NewClient(ctx, "my-project")
if err != nil {
log.Fatalf("failed to create client: %v", err)
}
defer client.Close()
// Set error handler
client.OnError = func(err error) {
log.Printf("Logging error: %v", err)
}
// Create logger
logger := client.Logger("app-log", logging.CommonLabels(map[string]string{
"service": "web-server",
}))
// Initialize standard loggers
infoLogger = logger.StandardLogger(logging.Info)
errorLogger = logger.StandardLogger(logging.Error)
// Replace global logger
log.SetOutput(infoLogger.Writer())
log.SetFlags(0)
// Log application startup
infoLogger.Println("Application starting")
log.Printf("Listening on port 8080")
// Start server
http.HandleFunc("/", requestHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
errorLogger.Printf("Server failed: %v", err)
os.Exit(1)
}
}
func requestHandler(w http.ResponseWriter, r *http.Request) {
// Create request-specific logger
template := &logging.Entry{
Severity: logging.Info,
HTTPRequest: &logging.HTTPRequest{
Request: r,
},
}
reqLogger := logger.StandardLoggerFromTemplate(template)
reqLogger.Printf("Received %s request for %s", r.Method, r.URL.Path)
// Process request
if r.URL.Path == "/error" {
errorLogger.Println("Error endpoint accessed")
http.Error(w, "Error", http.StatusInternalServerError)
return
}
reqLogger.Println("Request processed successfully")
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}StandardLogger() is cheap because loggers are pre-allocatedLogger.Log() directly for better controlLogger.Log()