315 lines
11 KiB
Go
315 lines
11 KiB
Go
// Copyright (C) MongoDB, Inc. 2023-present.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
package logger
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
const (
|
|
CommandFailed = "Command failed"
|
|
CommandStarted = "Command started"
|
|
CommandSucceeded = "Command succeeded"
|
|
ConnectionPoolCreated = "Connection pool created"
|
|
ConnectionPoolReady = "Connection pool ready"
|
|
ConnectionPoolCleared = "Connection pool cleared"
|
|
ConnectionPoolClosed = "Connection pool closed"
|
|
ConnectionCreated = "Connection created"
|
|
ConnectionReady = "Connection ready"
|
|
ConnectionClosed = "Connection closed"
|
|
ConnectionCheckoutStarted = "Connection checkout started"
|
|
ConnectionCheckoutFailed = "Connection checkout failed"
|
|
ConnectionCheckedOut = "Connection checked out"
|
|
ConnectionCheckedIn = "Connection checked in"
|
|
ServerSelectionFailed = "Server selection failed"
|
|
ServerSelectionStarted = "Server selection started"
|
|
ServerSelectionSucceeded = "Server selection succeeded"
|
|
ServerSelectionWaiting = "Waiting for suitable server to become available"
|
|
TopologyClosed = "Stopped topology monitoring"
|
|
TopologyDescriptionChanged = "Topology description changed"
|
|
TopologyOpening = "Starting topology monitoring"
|
|
TopologyServerClosed = "Stopped server monitoring"
|
|
TopologyServerHeartbeatFailed = "Server heartbeat failed"
|
|
TopologyServerHeartbeatStarted = "Server heartbeat started"
|
|
TopologyServerHeartbeatSucceeded = "Server heartbeat succeeded"
|
|
TopologyServerOpening = "Starting server monitoring"
|
|
)
|
|
|
|
const (
|
|
KeyAwaited = "awaited"
|
|
KeyCommand = "command"
|
|
KeyCommandName = "commandName"
|
|
KeyDatabaseName = "databaseName"
|
|
KeyDriverConnectionID = "driverConnectionId"
|
|
KeyDurationMS = "durationMS"
|
|
KeyError = "error"
|
|
KeyFailure = "failure"
|
|
KeyMaxConnecting = "maxConnecting"
|
|
KeyMaxIdleTimeMS = "maxIdleTimeMS"
|
|
KeyMaxPoolSize = "maxPoolSize"
|
|
KeyMessage = "message"
|
|
KeyMinPoolSize = "minPoolSize"
|
|
KeyNewDescription = "newDescription"
|
|
KeyOperation = "operation"
|
|
KeyOperationID = "operationId"
|
|
KeyPreviousDescription = "previousDescription"
|
|
KeyRemainingTimeMS = "remainingTimeMS"
|
|
KeyReason = "reason"
|
|
KeyReply = "reply"
|
|
KeyRequestID = "requestId"
|
|
KeySelector = "selector"
|
|
KeyServerConnectionID = "serverConnectionId"
|
|
KeyServerHost = "serverHost"
|
|
KeyServerPort = "serverPort"
|
|
KeyServiceID = "serviceId"
|
|
KeyTimestamp = "timestamp"
|
|
KeyTopologyDescription = "topologyDescription"
|
|
KeyTopologyID = "topologyId"
|
|
)
|
|
|
|
// KeyValues is a list of key-value pairs.
|
|
type KeyValues []interface{}
|
|
|
|
// Add adds a key-value pair to an instance of a KeyValues list.
|
|
func (kvs *KeyValues) Add(key string, value interface{}) {
|
|
*kvs = append(*kvs, key, value)
|
|
}
|
|
|
|
const (
|
|
ReasonConnClosedStale = "Connection became stale because the pool was cleared"
|
|
ReasonConnClosedIdle = "Connection has been available but unused for longer than the configured max idle time"
|
|
ReasonConnClosedError = "An error occurred while using the connection"
|
|
ReasonConnClosedPoolClosed = "Connection pool was closed"
|
|
ReasonConnCheckoutFailedTimout = "Wait queue timeout elapsed without a connection becoming available"
|
|
ReasonConnCheckoutFailedError = "An error occurred while trying to establish a new connection"
|
|
ReasonConnCheckoutFailedPoolClosed = "Connection pool was closed"
|
|
)
|
|
|
|
// Component is an enumeration representing the "components" which can be
|
|
// logged against. A LogLevel can be configured on a per-component basis.
|
|
type Component int
|
|
|
|
const (
|
|
// ComponentAll enables logging for all components.
|
|
ComponentAll Component = iota
|
|
|
|
// ComponentCommand enables command monitor logging.
|
|
ComponentCommand
|
|
|
|
// ComponentTopology enables topology logging.
|
|
ComponentTopology
|
|
|
|
// ComponentServerSelection enables server selection logging.
|
|
ComponentServerSelection
|
|
|
|
// ComponentConnection enables connection services logging.
|
|
ComponentConnection
|
|
)
|
|
|
|
const (
|
|
mongoDBLogAllEnvVar = "MONGODB_LOG_ALL"
|
|
mongoDBLogCommandEnvVar = "MONGODB_LOG_COMMAND"
|
|
mongoDBLogTopologyEnvVar = "MONGODB_LOG_TOPOLOGY"
|
|
mongoDBLogServerSelectionEnvVar = "MONGODB_LOG_SERVER_SELECTION"
|
|
mongoDBLogConnectionEnvVar = "MONGODB_LOG_CONNECTION"
|
|
)
|
|
|
|
var componentEnvVarMap = map[string]Component{
|
|
mongoDBLogAllEnvVar: ComponentAll,
|
|
mongoDBLogCommandEnvVar: ComponentCommand,
|
|
mongoDBLogTopologyEnvVar: ComponentTopology,
|
|
mongoDBLogServerSelectionEnvVar: ComponentServerSelection,
|
|
mongoDBLogConnectionEnvVar: ComponentConnection,
|
|
}
|
|
|
|
// EnvHasComponentVariables returns true if the environment contains any of the
|
|
// component environment variables.
|
|
func EnvHasComponentVariables() bool {
|
|
for envVar := range componentEnvVarMap {
|
|
if os.Getenv(envVar) != "" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Command is a struct defining common fields that must be included in all
|
|
// commands.
|
|
type Command struct {
|
|
// TODO(GODRIVER-2824): change the DriverConnectionID type to int64.
|
|
DriverConnectionID uint64 // Driver's ID for the connection
|
|
Name string // Command name
|
|
DatabaseName string // Database name
|
|
Message string // Message associated with the command
|
|
OperationID int32 // Driver-generated operation ID
|
|
RequestID int64 // Driver-generated request ID
|
|
ServerConnectionID *int64 // Server's ID for the connection used for the command
|
|
ServerHost string // Hostname or IP address for the server
|
|
ServerPort string // Port for the server
|
|
ServiceID *primitive.ObjectID // ID for the command in load balancer mode
|
|
}
|
|
|
|
// SerializeCommand takes a command and a variable number of key-value pairs and
|
|
// returns a slice of interface{} that can be passed to the logger for
|
|
// structured logging.
|
|
func SerializeCommand(cmd Command, extraKeysAndValues ...interface{}) KeyValues {
|
|
// Initialize the boilerplate keys and values.
|
|
keysAndValues := KeyValues{
|
|
KeyCommandName, cmd.Name,
|
|
KeyDatabaseName, cmd.DatabaseName,
|
|
KeyDriverConnectionID, cmd.DriverConnectionID,
|
|
KeyMessage, cmd.Message,
|
|
KeyOperationID, cmd.OperationID,
|
|
KeyRequestID, cmd.RequestID,
|
|
KeyServerHost, cmd.ServerHost,
|
|
}
|
|
|
|
// Add the extra keys and values.
|
|
for i := 0; i < len(extraKeysAndValues); i += 2 {
|
|
keysAndValues.Add(extraKeysAndValues[i].(string), extraKeysAndValues[i+1])
|
|
}
|
|
|
|
port, err := strconv.ParseInt(cmd.ServerPort, 10, 32)
|
|
if err == nil {
|
|
keysAndValues.Add(KeyServerPort, port)
|
|
}
|
|
|
|
// Add the "serverConnectionId" if it is not nil.
|
|
if cmd.ServerConnectionID != nil {
|
|
keysAndValues.Add(KeyServerConnectionID, *cmd.ServerConnectionID)
|
|
}
|
|
|
|
// Add the "serviceId" if it is not nil.
|
|
if cmd.ServiceID != nil {
|
|
keysAndValues.Add(KeyServiceID, cmd.ServiceID.Hex())
|
|
}
|
|
|
|
return keysAndValues
|
|
}
|
|
|
|
// Connection contains data that all connection log messages MUST contain.
|
|
type Connection struct {
|
|
Message string // Message associated with the connection
|
|
ServerHost string // Hostname or IP address for the server
|
|
ServerPort string // Port for the server
|
|
}
|
|
|
|
// SerializeConnection serializes a Connection message into a slice of keys and
|
|
// values that can be passed to a logger.
|
|
func SerializeConnection(conn Connection, extraKeysAndValues ...interface{}) KeyValues {
|
|
// Initialize the boilerplate keys and values.
|
|
keysAndValues := KeyValues{
|
|
KeyMessage, conn.Message,
|
|
KeyServerHost, conn.ServerHost,
|
|
}
|
|
|
|
// Add the optional keys and values.
|
|
for i := 0; i < len(extraKeysAndValues); i += 2 {
|
|
keysAndValues.Add(extraKeysAndValues[i].(string), extraKeysAndValues[i+1])
|
|
}
|
|
|
|
port, err := strconv.ParseInt(conn.ServerPort, 10, 32)
|
|
if err == nil {
|
|
keysAndValues.Add(KeyServerPort, port)
|
|
}
|
|
|
|
return keysAndValues
|
|
}
|
|
|
|
// Server contains data that all server messages MAY contain.
|
|
type Server struct {
|
|
DriverConnectionID uint64 // Driver's ID for the connection
|
|
TopologyID primitive.ObjectID // Driver's unique ID for this topology
|
|
Message string // Message associated with the topology
|
|
ServerConnectionID *int64 // Server's ID for the connection
|
|
ServerHost string // Hostname or IP address for the server
|
|
ServerPort string // Port for the server
|
|
}
|
|
|
|
// SerializeServer serializes a Server message into a slice of keys and
|
|
// values that can be passed to a logger.
|
|
func SerializeServer(srv Server, extraKV ...interface{}) KeyValues {
|
|
// Initialize the boilerplate keys and values.
|
|
keysAndValues := KeyValues{
|
|
KeyDriverConnectionID, srv.DriverConnectionID,
|
|
KeyMessage, srv.Message,
|
|
KeyServerHost, srv.ServerHost,
|
|
KeyTopologyID, srv.TopologyID.Hex(),
|
|
}
|
|
|
|
if connID := srv.ServerConnectionID; connID != nil {
|
|
keysAndValues.Add(KeyServerConnectionID, *connID)
|
|
}
|
|
|
|
port, err := strconv.ParseInt(srv.ServerPort, 10, 32)
|
|
if err == nil {
|
|
keysAndValues.Add(KeyServerPort, port)
|
|
}
|
|
|
|
// Add the optional keys and values.
|
|
for i := 0; i < len(extraKV); i += 2 {
|
|
keysAndValues.Add(extraKV[i].(string), extraKV[i+1])
|
|
}
|
|
|
|
return keysAndValues
|
|
}
|
|
|
|
// ServerSelection contains data that all server selection messages MUST
|
|
// contain.
|
|
type ServerSelection struct {
|
|
Selector string
|
|
OperationID *int32
|
|
Operation string
|
|
TopologyDescription string
|
|
}
|
|
|
|
// SerializeServerSelection serializes a Topology message into a slice of keys
|
|
// and values that can be passed to a logger.
|
|
func SerializeServerSelection(srvSelection ServerSelection, extraKV ...interface{}) KeyValues {
|
|
keysAndValues := KeyValues{
|
|
KeySelector, srvSelection.Selector,
|
|
KeyOperation, srvSelection.Operation,
|
|
KeyTopologyDescription, srvSelection.TopologyDescription,
|
|
}
|
|
|
|
if srvSelection.OperationID != nil {
|
|
keysAndValues.Add(KeyOperationID, *srvSelection.OperationID)
|
|
}
|
|
|
|
// Add the optional keys and values.
|
|
for i := 0; i < len(extraKV); i += 2 {
|
|
keysAndValues.Add(extraKV[i].(string), extraKV[i+1])
|
|
}
|
|
|
|
return keysAndValues
|
|
}
|
|
|
|
// Topology contains data that all topology messages MAY contain.
|
|
type Topology struct {
|
|
ID primitive.ObjectID // Driver's unique ID for this topology
|
|
Message string // Message associated with the topology
|
|
}
|
|
|
|
// SerializeTopology serializes a Topology message into a slice of keys and
|
|
// values that can be passed to a logger.
|
|
func SerializeTopology(topo Topology, extraKV ...interface{}) KeyValues {
|
|
keysAndValues := KeyValues{
|
|
KeyTopologyID, topo.ID.Hex(),
|
|
}
|
|
|
|
// Add the optional keys and values.
|
|
for i := 0; i < len(extraKV); i += 2 {
|
|
keysAndValues.Add(extraKV[i].(string), extraKV[i+1])
|
|
}
|
|
|
|
return keysAndValues
|
|
}
|